Skip to main content

caixa_core/
manifest.rs

1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use tatara_lisp::DeriveTataraDomain;
5
6use thiserror::Error;
7
8use crate::{
9    CaixaKind, Dep,
10    behavior::BehaviorSpec,
11    dep::DepError,
12    limits::LimitsSpec,
13    render::{
14        PathShapeViolation, is_computeunit_yaml_extension, is_git_repo_url, is_lisp_extension,
15        is_sandboxed_relative_path,
16    },
17    supervisor::SupervisorSpec,
18    upgrade::UpgradeFromEntry,
19};
20
21/// Top-level manifest for a caixa (a tatara-lisp package).
22///
23/// Authored as `caixa.lisp`:
24///
25/// ```lisp
26/// (defcaixa
27///   :nome        "pangea-tatara-aws"
28///   :versao      "0.1.0"
29///   :kind        Biblioteca
30///   :edicao      "2026"
31///   :descricao   "AWS provider caixa for tatara-lisp"
32///   :repositorio "github:pleme-io/pangea-tatara-aws"
33///   :licenca     "MIT"
34///   :autores     ("pleme-io")
35///   :etiquetas   ("iac" "aws" "pangea")
36///   :deps        ((:nome "caixa-teia"    :versao "^0.1")
37///                 (:nome "iac-forge-ir"  :versao "^0.5"))
38///   :deps-dev    ((:nome "tatara-check"  :versao "*"))
39///   :bibliotecas ("lib/pangea-tatara-aws.lisp"))
40/// ```
41///
42/// Because `Caixa` derives [`tatara_lisp::domain::TataraDomain`], the manifest
43/// is parsed directly by the tatara-lisp compiler — an ill-formed manifest is
44/// a compile error, not a runtime error.
45#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[tatara(keyword = "defcaixa")]
48pub struct Caixa {
49    /// Package name — the canonical string used in `:deps`, the registry, and
50    /// the default lib/exe entry names.
51    pub nome: String,
52
53    /// Package version — a semver literal like `"0.1.0"`. Parsed lazily via
54    /// [`crate::CaixaVersion::parse`].
55    pub versao: String,
56
57    /// What this caixa produces. See [`CaixaKind`].
58    pub kind: CaixaKind,
59
60    /// Language edition — determines macro surface + compatibility flags.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub edicao: Option<String>,
63
64    /// Free-form description shown in the registry listing.
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub descricao: Option<String>,
67
68    /// Homepage or repo URL.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub repositorio: Option<String>,
71
72    /// SPDX license expression — `"MIT"`, `"Apache-2.0 OR MIT"`, etc.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub licenca: Option<String>,
75
76    /// Authors — free-form strings.
77    #[serde(default)]
78    pub autores: Vec<String>,
79
80    /// Topical tags used for registry search.
81    #[serde(default)]
82    pub etiquetas: Vec<String>,
83
84    /// Runtime dependencies.
85    #[serde(default)]
86    pub deps: Vec<Dep>,
87
88    /// Development-only dependencies (tests, lint, bench).
89    #[serde(default)]
90    pub deps_dev: Vec<Dep>,
91
92    /// Paths to executable entry points (relative to the package root).
93    /// Required when `:kind Binario`.
94    #[serde(default)]
95    pub exe: Vec<String>,
96
97    /// Paths to library entry points (relative to the package root).
98    /// First entry is the canonical `lib/<nome>.lisp`; when omitted under
99    /// `:kind Biblioteca`, the layout check expects `lib/<nome>.lisp`.
100    #[serde(default)]
101    pub bibliotecas: Vec<String>,
102
103    /// Paths to service manifests (relative to the package root).
104    /// Required when `:kind Servico`.
105    #[serde(default)]
106    pub servicos: Vec<String>,
107
108    // ── M2 typed-substrate extensions per theory/ABSORPTION-ROADMAP.md ──
109    //
110    // All four are optional + default to "absent"; existing caixas
111    // round-trip unchanged. Each maps onto a prior-art primitive named
112    // in theory/INSPIRATIONS.md:
113    //
114    //   :limits        — Lunatic per-process limits (§III.1)
115    //   :behavior      — OTP gen_server callbacks  (§II.3)
116    //   :upgrade-from  — OTP appup migration       (§II.4)
117    //   :estrategia    — OTP supervisor strategy   (§II.2 + §III.2)
118    //   :children      — OTP supervisor children    (§II.2 + §III.2)
119    //
120    // The supervisor slots are flat on Caixa (vs nested under a
121    // SupervisorSpec sub-form) to keep tatara-lisp authoring at one
122    // level of nesting; SupervisorSpec exists for validation +
123    // composition convenience (`Caixa::supervisor_view()`).
124    /// Lunatic-style per-process resource limits. None = unbounded.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub limits: Option<LimitsSpec>,
127
128    /// OTP-shaped behavior callbacks for Servico-kind caixas.
129    /// Authored as `(:on-init "..." :on-call "..." …)`.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub behavior: Option<BehaviorSpec>,
132
133    /// OTP appup — declarative upgrade instructions per prior version.
134    /// Empty list = no hot-upgrade path declared (caller falls back to
135    /// `:Restart` strategy).
136    #[serde(default)]
137    pub upgrade_from: Vec<UpgradeFromEntry>,
138
139    /// OTP supervisor strategy. Required when `:kind Supervisor`;
140    /// ignored otherwise.
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub estrategia: Option<crate::supervisor::RestartStrategy>,
143
144    /// Max restarts before the supervisor itself fails. Defaults via
145    /// SupervisorSpec at validation time.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub max_restarts: Option<u32>,
148
149    /// Sliding window for `max_restarts`. Authored as a duration
150    /// string (`"60s"`, `"5m"`).
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub restart_window: Option<String>,
153
154    /// Static children of a supervisor. Required for OneForOne /
155    /// OneForAll / RestForOne; must be empty for SimpleOneForOne.
156    #[serde(default)]
157    pub children: Vec<crate::supervisor::ChildSpec>,
158
159    // ── M3 Aplicacao slots (theory/MESH-COMPOSITION.md) ─────────────────
160    //
161    // Required when :kind Aplicacao; ignored otherwise.
162    // Composed into a typed AplicacaoSpec via Caixa::aplicacao_view().
163    /// Member Servicos that make up this Aplicacao. Each is a
164    /// caixa-name + version-constraint pair. Required for Aplicacao.
165    #[serde(default)]
166    pub membros: Vec<crate::aplicacao::Membro>,
167
168    /// WIT-typed inter-Servico contracts. Each `:de` and `:para`
169    /// must reference a name in `:membros`.
170    #[serde(default)]
171    pub contratos: Vec<crate::aplicacao::WitContract>,
172
173    /// Mesh-level policies (timeout, retries, circuit-breaker, mTLS,
174    /// rate-limit). Apply to every contrato unless overridden per-edge
175    /// in M4.
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub politicas: Option<crate::aplicacao::MeshPolicy>,
178
179    /// Placement strategy across the cluster fleet
180    /// (single-node | replicated | sharded).
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub placement: Option<crate::aplicacao::Placement>,
183
184    /// External entry point — gateway / ingress shape. Optional;
185    /// only for public Aplicacaos.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub entrada: Option<crate::aplicacao::Entrada>,
188
189    // ── Acao slot (CANTEIRO §7.1-C) ──────────────────────────────────────
190    //
191    // Required when :kind Acao; ignored otherwise (mirrors the M2/
192    // supervisor-tree/M3 slot triads above — a declared-but-foreign `:ci`
193    // is a `LayoutError::CiOnNonAcao` build error, not a silent drop).
194    /// Typed CI run — a repo's CI run as a set of typed nodes + their
195    /// dependency edges. Required for `:kind Acao`; validated (not
196    /// rendered) by the `caixa-actions` renderer via
197    /// `canteiro_types::decompose`. See `caixa-actions`' crate docs for
198    /// the M0 validate-only contract.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub ci: Option<canteiro_types::CiRun>,
201}
202
203/// Why reading a manifest into a [`Caixa`] failed.
204///
205/// Split from [`ManifestError`] (which reports a *parsed* manifest that is
206/// semantically wrong) because the two answer different questions, and the
207/// distinction is the whole point of this type: `ManifestError` means "your
208/// caixa is wrong", `LeituraError::DialetoEstrangeiro` means "this file is not
209/// a caixa".
210#[derive(Debug, thiserror::Error)]
211pub enum LeituraError {
212    /// The source is not readable as a `(defcaixa …)` package manifest — bad
213    /// syntax, a wrong head symbol, an unknown or mistyped slot.
214    ///
215    /// `#[source]`, not `#[error(transparent)]`. Transparent delegates
216    /// `source()` past the inner error to ITS source, which drops the
217    /// `LispError` off the cause chain — and `feira`'s
218    /// `load_caixa_parse_error_preserves_underlying_lisp_error_on_chain`
219    /// pins that a caller can `downcast_ref::<tatara_lisp::LispError>()`
220    /// through an anyhow context to read the typed payload. That pin caught
221    /// this exact regression when the variant first landed transparent.
222    #[error("{0}")]
223    Leitura(
224        #[source]
225        #[from]
226        tatara_lisp::LispError,
227    ),
228
229    /// The source IS a well-formed `(defcaixa …)` form, but of a different
230    /// declaration than this crate's.
231    ///
232    /// The variant that did not exist before, and whose absence is the defect.
233    /// A `(defcaixa :name "x" :ecosystem :go …)` used to reach the derive's
234    /// `parse_kwargs_strict` and come back as an unknown-keyword rejection —
235    /// byte-identical in shape to a typo in a real manifest. Measured over the
236    /// org checkout on 2026-07-31, that shape is the MAJORITY of the corpus, so
237    /// the confusing error was also the common one.
238    ///
239    /// Carrying the dialect means a consumer can branch on "not mine" without
240    /// re-parsing, and a census can count it. Every user-facing byte-string
241    /// (canonical keyword, one-line description, consuming crate) is a
242    /// projection of [`crate::dialeto::CaixaDialeto`] — the variant stores the
243    /// typed dialect and the `#[error]` template calls
244    /// [`CaixaDialeto::palavra_canonica`] /
245    /// [`CaixaDialeto::descricao`] / [`CaixaDialeto::consumidor`] on it, so
246    /// the three axes cannot silently diverge from the classification. Prior
247    /// to this closure the variant carried each accessor's return value as a
248    /// stored `&'static str` snapshot alongside `dialeto`, and the sole
249    /// constructor at [`Caixa::from_lisp`] filled all four fields — a caller
250    /// could construct `DialetoEstrangeiro { dialeto: Molde,
251    /// palavra_canonica: "defcaixa", … }` and every downstream consumer
252    /// (Display, ad-hoc audit, future JSON serialization) would silently
253    /// disagree with `dialeto.palavra_canonica() == "defmolde"`. The typed
254    /// enum owns the projections; the variant only carries the axis.
255    #[error(
256        "this is a `{palavra}` declaration ({desc}), read by \
257         {cons} — not a caixa-core package manifest. `defcaixa` is the \
258         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
259         are different declarations that shared one keyword until 2026-07-31",
260        palavra = dialeto.palavra_canonica(),
261        desc = dialeto.descricao(),
262        cons = dialeto.consumidor()
263    )]
264    DialetoEstrangeiro {
265        /// Which declaration this actually is. Sole authoritative axis;
266        /// every user-facing projection routes through
267        /// [`crate::dialeto::CaixaDialeto`]'s typed accessors so the four
268        /// axes cannot silently disagree.
269        dialeto: crate::dialeto::CaixaDialeto,
270    },
271
272    /// Not a manifest declaration at all.
273    #[error(transparent)]
274    Dialeto(#[from] crate::dialeto::DialetoError),
275}
276
277/// Substrate-canonical universal-axis per-[`Caixa`] `:licenca` SPDX-shaped
278/// license-expression fallback for the `Option<String>` `:licenca` slot —
279/// the `"MIT"` SPDX identifier every [`caixa-helm`]-rendered
280/// `lareira-<nome>` Helm chart's `README.md` `## License` section folds an
281/// author-omitted (`None`) `:licenca` slot through, extracted as a typed
282/// `pub const` so every substrate-side consumer that resolves "what license
283/// scalar does an author-omitted `:licenca` degrade onto?" reaches for
284/// exactly one substrate-primitive `&'static str`.
285///
286/// The `:licenca` fallback axis has one production consumer today — the
287/// [`caixa-helm`] `build_readme` fold at `caixa-helm/src/lib.rs`'s
288/// `caixa.licenca().unwrap_or(CAIXA_LICENCA_DEFAULT)` `README.md`
289/// `## License` section body — with three sibling caixa-core sites that
290/// cite the `"MIT"` fallback in prose (this crate's [`Caixa::licenca`]
291/// accessor's docstring, [`Self::validate_licenca`]'s docstring, and the
292/// [`ManifestError::LicencaEmpty`] `#[error]` template's user-facing text)
293/// all quoting the exact byte-string a future substrate-side rebrand of the
294/// fallback (a tightening to `"Apache-2.0"` as the substrate absorbs the
295/// wasm-component-model conventions the `wasi:*` WIT worlds already carry,
296/// a per-cluster license-default overlay the M4 CR materializer resolves
297/// per-CR, a promotion to the plain `Option<String>` byte-string into a
298/// richer `SpdxExpression` enum once the SPDX-expression parser lands per
299/// [`Self::validate_licenca`]'s docstring roadmap) would silently split
300/// against — the caixa-helm renderer would emit the new byte, the
301/// docstrings would still cite the prior byte, and every author who reads
302/// the accessor docstring before authoring would file a fresh
303/// `:licenca "MIT"` verbatim rather than defer to the substrate default,
304/// with the drift surfacing at chart-README-audit time far from the
305/// substrate rebrand commit.
306///
307/// Prior to this lift the sole production emitter (`build_readme`) carried
308/// an inline `"MIT"` byte literal at
309/// `caixa-helm/src/lib.rs:1018`'s `.unwrap_or("MIT")` fallback arm — one
310/// occurrence of the same load-bearing per-`Caixa` universal-axis
311/// SPDX-shaped license-expression convention as the four sibling caixa-core
312/// docstring citations, drift-prone by construction ahead of the second
313/// occurrence the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
314/// materializer's per-Aplicacao registry-annotation synthesis (the
315/// [`Self::validate_licenca`] roadmap already names the `Chart.yaml
316/// annotations["artifacthub.io/license"]` axis every registry-facing chart
317/// carries as the second consumer) will surface.
318///
319/// The `"MIT"` value pins the canonical CAIXA-SDLC §I license scaffold
320/// every `feira init`-emitted [`Self::template`] carries verbatim
321/// (`:licenca "MIT"`) and every substrate-side renderer fixture
322/// ([`caixa-helm`]'s `sample_caixa`, [`caixa-flux`]'s renderer fixtures,
323/// [`caixa-mesh`]'s renderer fixtures) seeds by construction, matching the
324/// pleme-io repo `LICENSE` header this workspace itself ships under. The
325/// alternatives an author declares explicitly (compound SPDX expressions
326/// like `"Apache-2.0 OR MIT"`, permissive-family peers like
327/// `"Apache-2.0"` / `"BSD-3-Clause"`, license-with-exception forms like
328/// `"Apache-2.0 WITH LLVM-exception"`) express deliberate license postures
329/// an author declares explicitly, never a posture an author-omitted slot
330/// should silently assume by default.
331///
332/// Lifted as a typed `pub const` so the substrate's chosen license
333/// fallback has exactly one source of truth on the `:licenca` fallback
334/// axis, on the same substrate-primitive lift discipline the peer
335/// per-`Caixa` load-bearing-scalar constants
336/// ([`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`],
337/// [`crate::version::DEFAULT_GIT_REMOTE`],
338/// [`crate::version::DEFAULT_PLEME_GIT_ORG`]) already carry on the sibling
339/// per-`Caixa` universal-axis publish-side convention surface, and the
340/// same discipline the sibling M2 per-supervisor default set carries
341/// end-to-end ([`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`],
342/// [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`],
343/// [`crate::supervisor::SUPERVISOR_RESTART_WINDOW_DEFAULT`],
344/// [`crate::supervisor::SUPERVISOR_CHILD_RESTART_DEFAULT`]) and the M3
345/// per-`:placement` default set already carries
346/// ([`crate::aplicacao::PLACEMENT_ESTRATEGIA_DEFAULT`]) on the paired
347/// M2 / M3 typed-slot-default axes. First typed default on the outer
348/// top-level [`Caixa`] universal-axis surface to converge onto the
349/// substrate-primitive-lift discipline the M2 / M3 typed-slot families
350/// already carry.
351pub const CAIXA_LICENCA_DEFAULT: &str = "MIT";
352
353impl Caixa {
354    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
355    ///
356    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
357    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
358    /// and who reads it, instead of an unknown-keyword rejection that reads as
359    /// "your manifest is broken".
360    ///
361    /// The ordering is load-bearing. Handing a foreign dialect to the derive
362    /// first and interpreting the failure afterwards would mean guessing from
363    /// an error message, and the guess would be wrong for every file whose
364    /// first unknown slot happens to be one both schemas could plausibly carry.
365    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
366        use tatara_lisp::domain::TataraDomain;
367        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
368        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
369
370        // Route the foreign-dialect rejection gate through the lifted
371        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
372        // typed predicate rather than the pre-lift hand-rolled three-arm
373        // `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
374        // literal — the `defmolde` declaration-family partition (the two-
375        // arity closure of [`crate::dialeto::CaixaDialeto::Molde`] and
376        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two arms
377        // whose sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
378        // projection already collapses onto `"defmolde"` and whose sibling
379        // [`crate::dialeto::CaixaDialeto::consumidor`] projection already
380        // collapses onto `"pleme-doc-gen"`) resolves through one dispatch
381        // on the substrate primitive. `Pacote` (the tatara-lisp package
382        // manifest this derive can parse) and `Desconhecido` (deliberately
383        // falls through to the derive rather than short-circuiting: a
384        // `(defcaixa …)` matching neither schema is most likely a genuine
385        // package manifest with a typo in `:nome`, and the derive's
386        // diagnostic — which names the offending keyword and suggests the
387        // nearest slot — is far better than anything this classifier
388        // could say) both return `false` from `is_molde_family()` and fall
389        // through to the derive. Only the typed dialect flows into the
390        // error — the three user-facing projections (canonical keyword,
391        // description, consumer) are read at Display time through
392        // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
393        // variant cannot carry a snapshot that drifts from
394        // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
395        // `descricao` / `consumidor`. A future fifth dialect the
396        // [`crate::dialeto`] module doc's "third dialect" hazard
397        // actualises that belongs to the `defmolde` family lands one
398        // match arm at [`crate::dialeto::CaixaDialeto::is_molde_family`]
399        // and this gate picks up the new arm by construction — the pre-
400        // lift wildcard `foreign =>` was compile-time-anonymous and would
401        // silently absorb any hypothetical fifth `defcaixa`-family arm as
402        // foreign; routing the partition through the typed predicate
403        // closes both drift surfaces.
404        let dialeto = crate::dialeto::classify_form(first)?;
405        if dialeto.is_molde_family() {
406            return Err(LeituraError::DialetoEstrangeiro { dialeto });
407        }
408
409        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
410    }
411
412    /// Register `Caixa` with the global tatara-lisp domain registry so
413    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
414    /// the registry (e.g. `tatara-check`).
415    ///
416    /// Returns the typed [`tatara_lisp::KeywordCollision`] on the second
417    /// (and every subsequent) call in the same process — one keyword,
418    /// one type, per process is a hard invariant of the upstream
419    /// registry, and a caller that hits it must fix its crate graph
420    /// rather than swallowing the error. Peer of the sibling per-crate
421    /// `register()` entry points at `caixa-flake/src/flake.rs`,
422    /// `caixa-fmt/src/lisp_config.rs`, `caixa-lacre/src/lock.rs`,
423    /// `caixa-lint/src/lisp_config.rs`, `caixa-resolver/src/lisp_config.rs`
424    /// — every substrate crate that owns a tatara-lisp keyword now
425    /// propagates the same typed error verbatim, so a downstream binary
426    /// that seeds the registry (`tatara-check`, the future LSP) reaches
427    /// for one shape at every call site.
428    ///
429    /// # Errors
430    ///
431    /// [`tatara_lisp::KeywordCollision`] when a peer type has already
432    /// claimed the `defcaixa` keyword in this process.
433    pub fn register() -> Result<(), tatara_lisp::KeywordCollision> {
434        tatara_lisp::domain::register::<Self>()
435    }
436
437    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
438    /// accessor every consumer of the top-level manifest's license axis
439    /// keys off — returns the author-declared `:licenca` byte-string
440    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
441    /// `Option<String>` storage. `None` when the slot is absent (the
442    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
443    /// fallback" shape [`Self::validate_licenca`] documents at
444    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
445    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
446    /// predicate too, so an authored-but-unset `:licenca` round-trips to
447    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
448    /// section structurally identical to one that omits the slot).
449    ///
450    /// The `:licenca` slot carries the universal-axis SPDX-expression
451    /// license identifier every kind of caixa emits under (CAIXA-SDLC
452    /// §I — the author-facing surface every `defcaixa` form supplies) —
453    /// the typed slot's `Option<String>` accept-set (empty-string
454    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
455    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
456    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
457    /// section (caixa-helm/src/lib.rs:962) and (through future
458    /// tightening documented at [`Self::validate_licenca`]) the
459    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
460    /// registry-facing chart carries. Every downstream consumer that
461    /// reads the license byte-string keys off this scalar (the
462    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
463    /// routes through `self.licenca.as_deref()`, the caixa-helm
464    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
465    /// the fallback off the `Option::is_none()` arm, every future
466    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
467    /// acknowledges).
468    ///
469    /// Prior to this lift the `.licenca` field was accessed inline at
470    /// two production sites — [`Self::validate_licenca`]'s
471    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
472    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
473    /// "MIT".into())` `README.md` `## License` fold — two open-coded
474    /// field-accesses that expressed no compile-time link back to the
475    /// typed slot. A future extension of the `:licenca` axis to a
476    /// richer author surface — a per-`:licenca` structured SPDX
477    /// expression parser + license-id allowlist (the future tightening
478    /// [`Self::validate_licenca`]'s docstring acknowledges), a
479    /// per-cluster license-default overlay the M4 CR materializer
480    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
481    /// unlisted caixa" arm), a promotion of the plain
482    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
483    /// once the SPDX-expression parser lands — would have had to be
484    /// threaded through both open-coded copies in lockstep or the
485    /// validate gate and the caixa-helm emit path would silently
486    /// disagree on which license a given [`Caixa`] resolves to (an
487    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
488    /// while the emit path silently rendered a stale `MIT` fallback,
489    /// or vice versa). Lifting the resolution to a typed method on the
490    /// substrate primitive means every downstream consumer of the
491    /// caixa's per-`Caixa` license surface reaches for exactly one
492    /// typed dispatch — the resolver's accept-set migrates as a unit
493    /// on any future axis addition.
494    ///
495    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
496    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
497    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
498    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
499    /// substrate primitive, thin projections at each consumer"
500    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
501    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
502    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
503    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
504    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
505    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
506    /// typed-slot atom axes, extended here to the outer top-level
507    /// `Caixa` universal-axis surface. Named `licenca()` to match the
508    /// storage field's name; the accessor's identity maps onto the
509    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
510    /// carries.
511    #[must_use]
512    pub const fn licenca(&self) -> Option<&str> {
513        match &self.licenca {
514            Some(s) => Some(s.as_str()),
515            None => None,
516        }
517    }
518
519    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
520    /// accessor every consumer of the top-level manifest's homepage /
521    /// source-of-truth axis keys off — returns the author-declared
522    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
523    /// from the typed slot's own `Option<String>` storage. `None` when
524    /// the slot is absent (the canonical "omit to defer to the renderer's
525    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
526    /// carries the `Option<String>` through verbatim so an author-omitted
527    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
528    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
529    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
530    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
531    /// fallback derived from `caixa.nome`).
532    ///
533    /// The `:repositorio` slot carries the universal-axis git-repo-URL
534    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
535    /// §I — the author-facing surface every `defcaixa` form supplies) —
536    /// the typed slot's `Option<String>` accept-set (empty-string
537    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
538    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
539    /// past the shared [`crate::render::is_git_repo_url`] predicate the
540    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
541    /// four load-bearing downstream consumers:
542    ///
543    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
544    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
545    ///     universal-axis identity gate wired at caixa-build time.
546    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
547    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
548    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
549    ///     that ingests the chart (ArtifactHub, chartmuseum,
550    ///     `helm search repo`) surfaces as the chart's canonical source-
551    ///     of-truth link.
552    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
553    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
554    ///     chart's `README.md` header link back to the source repo,
555    ///     which every author who inspects the rendered chart bundle
556    ///     lands at.
557    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
558    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
559    ///     the rendered `GitRepository` CR's `spec.url` field, which
560    ///     FluxCD's `source-controller` polls to reconcile the caixa's
561    ///     manifest bundle from git.
562    ///
563    /// Prior to this lift the `.repositorio` field was accessed inline
564    /// at four production sites — [`Self::validate_repositorio`]'s
565    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
566    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
567    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
568    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
569    /// `README.md` `## Source` fold, and the caixa-flux
570    /// `ClusterBundleOpts::for_caixa`
571    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
572    /// `GitRepository.spec.url` fold — four open-coded field-accesses
573    /// that expressed no compile-time link back to the typed slot. A
574    /// future extension of the `:repositorio` axis to a richer author
575    /// surface — a per-`:repositorio` structured
576    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
577    /// (the future tightening [`Self::validate_repositorio`]'s
578    /// docstring anticipates alongside the peer per-`:deps :fonte
579    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
580    /// materializer resolves per-CR (the "cluster policy rewrites
581    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
582    /// arm the private-registry story acknowledges), a promotion of
583    /// the plain `Option<String>` byte-string to a richer
584    /// `RepoUrl` enum discriminated on scheme — would have had to be
585    /// threaded through all four open-coded copies in lockstep or the
586    /// validate gate and the three emit paths would silently disagree
587    /// on which URL a given [`Caixa`] resolves to (an author's
588    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
589    /// while one of the emit paths silently rendered a stale URL, or
590    /// vice versa). Lifting the resolution to a typed method on the
591    /// substrate primitive means every downstream consumer of the
592    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
593    /// typed dispatch — the resolver's accept-set migrates as a unit on
594    /// any future axis addition.
595    ///
596    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
597    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
598    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
599    /// projection pattern this lift folds on. Same "one typed dispatch
600    /// on the substrate primitive, thin projections at each consumer"
601    /// discipline the peer per-`:placement`
602    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
603    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
604    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
605    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
606    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
607    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
608    /// typed-slot atom axes, extended here to the second outer top-level
609    /// `Caixa` universal-axis surface. Named `repositorio()` to match
610    /// the storage field's name; the accessor's identity maps onto the
611    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
612    /// carries.
613    #[must_use]
614    pub const fn repositorio(&self) -> Option<&str> {
615        match &self.repositorio {
616            Some(s) => Some(s.as_str()),
617            None => None,
618        }
619    }
620
621    /// Substrate-canonical per-`Caixa` **resolved-git-repo-URL** composer —
622    /// returns the caixa's canonical git-source-of-truth URL as an owned
623    /// [`String`], author-declared `:repositorio` byte-string verbatim on
624    /// the `Some` arm and the substrate's canonical pleme-org github URL
625    /// fallback ([`crate::DEFAULT_PLEME_GIT_ORG`] and [`Self::nome`]
626    /// interpolated into `https://github.com/<org>/<nome>`) on the
627    /// `None` arm. Every substrate-side consumer that resolves
628    /// "which git URL does this caixa's source live at?" reaches for
629    /// exactly one typed dispatch on the substrate primitive — the raw
630    /// `caixa.repositorio().map(str::to_owned).unwrap_or_else(|| format!(
631    /// "https://github.com/{org}/{nome}", org = DEFAULT_PLEME_GIT_ORG,
632    /// nome = caixa.nome()))` open-coded composition every prior caller
633    /// re-derived collapses onto one canonical arm.
634    ///
635    /// Distinct from [`Self::repositorio`] (`Option<&str>`, exposes the
636    /// author-omitted / author-declared partition to the caller) — this
637    /// accessor is the **resolved** URL surface, folding the fallback in
638    /// at the substrate-primitive boundary. Every consumer that keys off
639    /// the `Option::is_none()` discriminator (a [`Chart.yaml`] `home:`
640    /// field emit that must omit the field entirely on an author-omitted
641    /// `:repositorio`, per the [`Self::repositorio`] docstring's
642    /// documented four-consumer list) reaches through the raw
643    /// [`Self::repositorio`] `Option<&str>` accessor by construction — the
644    /// resolved-URL composer sits alongside it as the second projection
645    /// on the same underlying `:repositorio` slot rather than replacing
646    /// the raw accessor.
647    ///
648    /// The fallback branch is the exact byte-image of the prior inline
649    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url` composer at
650    /// caixa-flux/src/lib.rs:2080 — pinned by the sibling caixa-flux
651    /// byte-parity test
652    /// `cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`
653    /// against a future implementation of this method that reordered the
654    /// `format!` template arguments, migrated the `<org>` segment to a
655    /// different constant (the [`crate::DEFAULT_PLEME_GIT_ORG`] axis a
656    /// future substrate-side git-org migration may split off), or
657    /// silently absorbed the empty-string arm (a hypothetical
658    /// `Some("") → fallback` collapse the raw [`Self::repositorio`]
659    /// accessor's docstring explicitly rejects on the sibling raw
660    /// accessor).
661    ///
662    /// Peer of the sibling per-`&Caixa`-axis composed helpers
663    /// [`caixa-flux::cluster_bundle_for_caixa`] (06d52d7) on the sibling
664    /// substrate-side renderer surface — same "close the composed
665    /// substrate-primitive at one canonical arm on the single-`&Caixa`
666    /// dispatch, converge every prior open-coded caller onto the arm"
667    /// discipline extended onto the resolved-git-URL projection of the
668    /// per-`Caixa` `:repositorio` axis. Owns per-call [`String`]
669    /// allocation on both arms (the `Some` arm's `str::to_owned` and the
670    /// `None` arm's `format!`) — the by-value return matches every
671    /// downstream consumer's field-fill shape (the caixa-flux
672    /// `ClusterBundleOpts::git_url: String` field, every future
673    /// `Chart.yaml` `home:` fold's `Option<String>` field-fill on the
674    /// `Some` arm).
675    #[must_use]
676    pub fn canonical_git_url(&self) -> String {
677        self.repositorio().map_or_else(
678            || {
679                format!(
680                    "https://github.com/{org}/{nome}",
681                    org = crate::DEFAULT_PLEME_GIT_ORG,
682                    nome = self.nome(),
683                )
684            },
685            str::to_owned,
686        )
687    }
688
689    /// Substrate-canonical per-`Caixa` **resolved-publish-tag** composer —
690    /// returns the caixa's canonical Zig-style git-publish-tag as an owned
691    /// [`String`], derived by concatenating
692    /// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] with the typed
693    /// [`Self::versao`] byte-string on a single `format!` template.
694    /// Every substrate-side consumer that resolves "which git tag does this
695    /// caixa publish under?" reaches for exactly one typed dispatch on the
696    /// substrate primitive — the raw `format!("{prefix}{versao}", prefix =
697    /// caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao = caixa.versao())`
698    /// open-coded composition every prior caller re-derived collapses onto
699    /// one canonical arm.
700    ///
701    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
702    /// git-URL composer on the paired per-`Caixa` git-remote axis — same
703    /// "close the composed substrate-primitive at one canonical arm on the
704    /// single-`&Caixa` dispatch, converge every prior open-coded caller
705    /// onto the arm" discipline extended from the resolved-URL projection
706    /// of the per-`Caixa` `:repositorio` axis onto the resolved-tag
707    /// projection of the per-`Caixa` `:versao` axis. The two accessors
708    /// jointly close the pair of scalars every `FluxCD` `GitRepository` CR
709    /// keys off (`spec.url` via [`Self::canonical_git_url`],
710    /// `spec.ref.tag` via [`Self::publish_tag`]) at the substrate primitive
711    /// — a downstream consumer that reaches through both accessors reads
712    /// the complete published-git-identity of a caixa through two typed
713    /// dispatches, not four open-coded field accesses.
714    ///
715    /// The reader-side (`caixa-flux::cluster_bundle` /
716    /// `ClusterBundleOpts::for_caixa`'s `git_ref` field, every future
717    /// per-cluster snapshot bundle emitter, the future M4
718    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's tag-carrier
719    /// slot on the tatara `Process` intent) always resolves the tag under
720    /// the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] prefix — this
721    /// method encodes that reader-side convention. The writer-side
722    /// (`caixa-feira`'s `feira publish` `--prefix` clap flag) allows the
723    /// operator to override the prefix at publish time; the two surfaces
724    /// intentionally sit on the "canonical default + operator override"
725    /// pair the sibling [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] constant's
726    /// own docstring documents — a `feira publish --prefix release/`
727    /// override is the operator's explicit opt-out from the substrate
728    /// default, not a supported drift axis.
729    ///
730    /// The composition body is the exact byte-image of the prior inline
731    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_ref` composer at
732    /// caixa-flux/src/lib.rs:2105 — pinned by the sibling caixa-flux
733    /// byte-parity test
734    /// `cluster_bundle_opts_for_caixa_git_ref_routes_through_publish_tag_accessor`
735    /// against a future implementation of this method that reordered the
736    /// `format!` template arguments, migrated the `<prefix>` segment to a
737    /// different constant (the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] axis
738    /// a future Zig-style-tag rebrand may split off — the constant's own
739    /// docstring anticipates a substrate-side move to `release/<versao>`
740    /// or bare `<versao>` shapes once a sibling forge convention adopts a
741    /// slash-namespaced or bare-scalar form), interposed a canonicalization
742    /// pass on the `:versao` axis (a SemVer-2 build-metadata strip an OCI-
743    /// tag normalizer might apply once the M4 registry-alignment slot
744    /// lands), or silently absorbed an empty `:versao` arm (which cannot
745    /// occur past the [`Self::validate_versao`] gate but which a
746    /// hypothetical bypass on the accessor path must not silently paper
747    /// over).
748    ///
749    /// Owns per-call [`String`] allocation via the single `format!`
750    /// invocation — the by-value return matches every downstream
751    /// consumer's field-fill shape (the caixa-flux `GitRefSpec::Tag(String)`
752    /// variant's owned payload, every future `intent.aplicacao.tag: String`
753    /// field-fill on the M4 CR materializer's tag-carrier slot).
754    #[must_use]
755    pub fn publish_tag(&self) -> String {
756        format!(
757            "{prefix}{versao}",
758            prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
759            versao = self.versao(),
760        )
761    }
762
763    /// Substrate-canonical per-`Caixa` **resolved-Helm-chart-name** composer
764    /// — returns the caixa's canonical `lareira-<nome>` per-Servico Helm
765    /// chart identity as an owned [`String`], derived by dispatching through
766    /// the substrate-canonical [`crate::lareira_chart_name`] helper against
767    /// the typed [`Self::nome`] byte-string. Every substrate-side consumer
768    /// that resolves "which Helm chart identity does this caixa render
769    /// under?" reaches for exactly one typed dispatch on the substrate
770    /// primitive — the raw `caixa_core::lareira_chart_name(caixa.nome())`
771    /// two-step compose every prior caller re-derived collapses onto one
772    /// canonical arm on the single-`&Caixa` dispatch.
773    ///
774    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
775    /// git-URL composer + [`Self::publish_tag`] (07e05b8) resolved-publish-
776    /// tag composer on the paired per-`Caixa` published-artifact-identity
777    /// axis — same "close the composed substrate-primitive at one canonical
778    /// arm on the single-`&Caixa` dispatch, converge every prior open-coded
779    /// caller onto the arm" discipline extended from the resolved-URL /
780    /// resolved-tag projections of the `:repositorio` / `:versao` axes onto
781    /// the resolved-chart-name projection of the `:nome` axis. The three
782    /// accessors jointly close the triple of scalars every per-Servico
783    /// deploy artifact keys off (git source URL via
784    /// [`Self::canonical_git_url`], git source tag via
785    /// [`Self::publish_tag`], per-Servico Helm chart identity via
786    /// [`Self::lareira_chart_name`]) at the substrate primitive — a
787    /// downstream consumer that reaches through all three reads the
788    /// complete deploy-artifact identity of a caixa through three typed
789    /// dispatches, not six open-coded compositions across three renderer
790    /// crates.
791    ///
792    /// The reader-side (three production sites at the time of the lift —
793    /// [`caixa-helm::render_chart_for_servico_with`]'s `ChartDir.name`
794    /// composer at caixa-helm/src/lib.rs:778, the peer
795    /// [`caixa-flux::cluster_bundle`]'s per-CR `chart_name` binding at
796    /// caixa-flux/src/lib.rs:2219, and
797    /// [`caixa-tatara::process_for_aplicacao`]'s `release_name`
798    /// composer at caixa-tatara/src/lib.rs:227, plus every future
799    /// per-Servico OCI publish emitter the CAIXA-SDLC §II
800    /// `caixa-publish.yml` reusable workflow's `skopeo push` step keys
801    /// off, the future per-cluster snapshot bundle emitter, the future
802    /// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
803    /// per-member chart-carrier slot on the tatara `Process` intent) —
804    /// always resolves the chart name under the canonical
805    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] prefix; this method encodes
806    /// that reader-side convention. The joint-length invariant the peer
807    /// [`Self::validate_nome_chart_name_budget`] gate enforces at
808    /// caixa-build time (author-declared `:nome` + fixed prefix ≤
809    /// [`crate::DNS_1123_LABEL_MAX_LEN`]) is verified on the input to
810    /// this composer by construction, so the produced `lareira-<nome>`
811    /// string is a valid Helm chart-name segment on every accept-set
812    /// input.
813    ///
814    /// The composition body is the exact byte-image of the prior inline
815    /// `caixa_core::lareira_chart_name(caixa.nome())` two-step form every
816    /// prior caller re-derived — pinned by the sibling caixa-helm /
817    /// caixa-flux / caixa-tatara byte-parity tests
818    /// `<crate>_lareira_chart_name_routes_through_caixa_accessor` against
819    /// a future implementation of this method that reordered the
820    /// composition arguments, migrated the `<prefix>` segment to a
821    /// different constant (the [`crate::LAREIRA_CHART_NAME_PREFIX`] axis a
822    /// future substrate-side chart-family rebrand may split off — the
823    /// constant's own docstring anticipates a substrate-side move once
824    /// the `lareira-` scoping intent outlives the family it names),
825    /// interposed a canonicalization pass on the `:nome` axis (a per-
826    /// registry namespace-qualification an M4 CR materializer might apply
827    /// per-CR — the "`pleme-io/checkout` vs `partner-org/checkout`
828    /// collision" arm the multi-tenant-registry story acknowledges), or
829    /// silently absorbed an empty `:nome` arm (which cannot occur past
830    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
831    /// on the accessor path must not silently paper over).
832    ///
833    /// Owns per-call [`String`] allocation via the single
834    /// [`crate::lareira_chart_name`] `format!` invocation — the by-value
835    /// return matches every downstream consumer's field-fill shape (the
836    /// caixa-helm `ChartDir.name: String` field, the caixa-flux per-CR
837    /// `chart_name: String` binding, the caixa-tatara
838    /// `AplicacaoIntent.release_name: Option<String>` field-fill on the
839    /// `Some` arm).
840    #[must_use]
841    pub fn lareira_chart_name(&self) -> String {
842        crate::lareira_chart_name(self.nome())
843    }
844
845    /// Substrate-canonical per-`Caixa` **resolved-OCI-chart-ref** composer
846    /// — returns the caixa's canonical `oci://<registry>/lareira-<nome>`
847    /// per-Servico Helm chart OCI artifact reference as an owned
848    /// [`String`], derived by dispatching through the substrate-canonical
849    /// [`crate::oci_chart_ref`] helper (which itself composes
850    /// [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied `registry` +
851    /// [`crate::lareira_chart_name`]-of-[`Self::nome`]) against the
852    /// caller-supplied `registry` and the typed [`Self::nome`] byte-string.
853    /// Every substrate-side consumer that resolves "which OCI chart
854    /// artifact does this caixa publish under, in this registry?" reaches
855    /// for exactly one typed dispatch on the substrate primitive — the raw
856    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step compose
857    /// every prior caller re-derived collapses onto one canonical arm on
858    /// the single-`(&Caixa, &str)` dispatch.
859    ///
860    /// Fourth member of the paired per-`Caixa` published-artifact-identity
861    /// axis alongside [`Self::canonical_git_url`] (124f864) /
862    /// [`Self::publish_tag`] (07e05b8) / [`Self::lareira_chart_name`]
863    /// (a8f0bee) — same "close the composed substrate-primitive at one
864    /// canonical arm on the single-`&Caixa` dispatch, converge every
865    /// prior open-coded caller onto the arm" discipline extended from the
866    /// resolved-URL / resolved-tag / resolved-chart-name projections of
867    /// the `:repositorio` / `:versao` / `:nome` axes onto the resolved-
868    /// OCI-ref projection over the paired `(registry, :nome)` inputs. The
869    /// four accessors jointly close the per-`Caixa` published-artifact-
870    /// identity surface every downstream consumer of a caixa's published
871    /// deploy artifacts keys off (git source URL via
872    /// [`Self::canonical_git_url`], git source tag via
873    /// [`Self::publish_tag`], per-Servico Helm chart identity via
874    /// [`Self::lareira_chart_name`], per-registry OCI chart artifact
875    /// reference via [`Self::oci_chart_ref`]) at the substrate primitive
876    /// — a downstream consumer that reaches through all four reads the
877    /// complete deploy-artifact identity of a caixa through four typed
878    /// dispatches, not eight open-coded compositions across four renderer
879    /// crates. The unique-signature dispatch (`(&Caixa, &str)` on this
880    /// method vs. `&Caixa` on the sibling three) reflects the extra input
881    /// axis this composer folds in: unlike the git-URL / git-tag / chart-
882    /// name axes (each derived purely from a `&Caixa`), the OCI-ref axis
883    /// pairs the caixa's per-`:nome` chart identity with the caller-
884    /// supplied per-registry authority segment, so the accessor threads
885    /// the registry byte-string through as a positional `&str`.
886    ///
887    /// The reader-side (one production site at the time of the lift —
888    /// [`caixa-tatara::process_for_aplicacao`]'s `derive_chart_ref` helper
889    /// at caixa-tatara/src/lib.rs:333 that composes the emitted
890    /// `AplicacaoIntent.chart_ref` scalar the tatara-reconciler feeds into
891    /// `helm install`, plus every future per-Servico OCI publish emitter
892    /// the CAIXA-SDLC §II `caixa-publish.yml` reusable workflow's
893    /// `skopeo push` step keys off, the future per-cluster snapshot bundle
894    /// emitter's per-CR `oci://…` field-fill on the M4 registry-alignment
895    /// slot, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
896    /// materializer's per-member `chart_ref` slot on the tatara `Process`
897    /// intent, the `FluxCD` `HelmRelease` `spec.chart.spec.chart` field-fill
898    /// on the OCI-source path an M4 per-cluster registry-rewrite overlay
899    /// applies per-CR) — always resolves the OCI ref under the canonical
900    /// [`crate::OCI_SCHEME_PREFIX`] scheme prefix + the canonical
901    /// [`Self::lareira_chart_name`] chart-name segment; this method
902    /// encodes that reader-side convention.
903    ///
904    /// The composition body is the exact byte-image of the prior inline
905    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step form
906    /// every prior caller re-derived — pinned by the sibling caixa-tatara
907    /// byte-parity test
908    /// `derive_chart_ref_routes_through_caixa_oci_chart_ref_accessor`
909    /// against a future implementation of this method that reordered the
910    /// composition arguments, migrated the `<scheme>` segment to a
911    /// different constant (the [`crate::OCI_SCHEME_PREFIX`] axis a future
912    /// substrate-side registry-protocol rebrand may split off — the
913    /// constant's own docstring anticipates a substrate-side move once
914    /// Helm 3 / `FluxCD` introduce a successor scheme past `oci://`),
915    /// migrated the `<chart>` segment off the paired
916    /// [`crate::lareira_chart_name`] composer (a per-registry
917    /// namespace-qualification an M4 CR materializer might apply per-CR),
918    /// interposed a canonicalization pass on the `registry` axis (an OCI-
919    /// authority normalization once the M4 registry-alignment slot lands),
920    /// or silently absorbed an empty `:nome` arm (which cannot occur past
921    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
922    /// on the accessor path must not silently paper over).
923    ///
924    /// Owns per-call [`String`] allocation via the single
925    /// [`crate::oci_chart_ref`] `format!` invocation — the by-value return
926    /// matches every downstream consumer's field-fill shape (the caixa-
927    /// tatara `AplicacaoIntent.chart_ref: String` field-fill, every
928    /// future `intent.aplicacao.chart_ref: String` field-fill on the M4
929    /// CR materializer's chart-ref-carrier slot, every future
930    /// `HelmRelease.spec.chart.spec.chart: String` field-fill on the OCI-
931    /// source path).
932    #[must_use]
933    pub fn oci_chart_ref(&self, registry: &str) -> String {
934        crate::oci_chart_ref(registry, self.nome())
935    }
936
937    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
938    /// chart-description scalar accessor every consumer of the top-level
939    /// manifest's Chart.yaml `description:` axis keys off — returns the
940    /// author-declared `:descricao` byte-string verbatim as an
941    /// `Option<&str>`, borrowed from the typed slot's own
942    /// `Option<String>` storage. `None` when the slot is absent (the
943    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
944    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
945    /// omitted slot through a `format!("Generated chart for caixa Servico
946    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
947    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
948    /// and [`caixa-feira`]'s `render_flake` folds it through a
949    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
950    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
951    ///
952    /// The `:descricao` slot carries the universal-axis free-form-prose
953    /// chart-description identifier every kind of caixa emits under
954    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
955    /// supplies) — the typed slot's `Option<String>` accept-set
956    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
957    /// chart-description-shape-invalid rejected through
958    /// [`ManifestError::DescricaoInvalid`] past the shared
959    /// [`crate::render::is_chart_description_shape`] predicate the peer
960    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
961    /// load-bearing downstream consumers:
962    ///
963    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
964    ///     gate binding — the universal-axis identity gate wired at
965    ///     caixa-build time.
966    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
967    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
968    ///     chart's `Chart.yaml` `description:` field, which
969    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
970    ///     `WARNING [chart.metadata.description]: description is required`
971    ///     when absent) and which every registry that ingests the chart
972    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
973    ///     chart's canonical one-line prose descriptor.
974    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
975    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
976    ///     header directly beneath the `# <chart-name>` title, which
977    ///     every author who inspects the rendered chart bundle lands at.
978    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
979    ///     top-level fold — the emitted `flake.nix`'s `description`
980    ///     field, which every Nix consumer (`nix flake show`,
981    ///     `nix flake metadata`, downstream flake-registry ingestors)
982    ///     surfaces as the flake's canonical descriptor.
983    ///
984    /// Prior to this lift the `.descricao` field was accessed inline at
985    /// four production sites — [`Self::validate_descricao`]'s
986    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
987    /// caixa-helm `build_chart_yaml`
988    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
989    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
990    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
991    /// `README.md` header fold, and the caixa-feira `render_flake`
992    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
993    /// `description = ""` fold — four open-coded field-accesses that
994    /// expressed no compile-time link back to the typed slot. A future
995    /// extension of the `:descricao` axis to a richer author surface —
996    /// a per-`:descricao` locale-tagged multi-language descriptor map
997    /// (the "one caixa, N language-tagged prose descriptions" arm
998    /// author-tooling internationalization anticipates), a
999    /// per-registry-target length-and-shape overlay the M4 CR
1000    /// materializer resolves per-CR (the "ArtifactHub caps description
1001    /// at 512 bytes but the internal registry caps at 256" arm), a
1002    /// promotion of the plain `Option<String>` byte-string to a richer
1003    /// `ChartDescription` newtype guaranteeing the
1004    /// `is_chart_description_shape` predicate at the type level — would
1005    /// have had to be threaded through all four open-coded copies in
1006    /// lockstep or the validate gate and the three emit paths would
1007    /// silently disagree on which prose string a given [`Caixa`]
1008    /// resolves to (an author's
1009    /// `:descricao "Checkout flow orchestration."` would satisfy
1010    /// validate while one of the emit paths silently rendered a stale
1011    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
1012    /// resolution to a typed method on the substrate primitive means
1013    /// every downstream consumer of the caixa's per-`Caixa`
1014    /// chart-description surface reaches for exactly one typed dispatch
1015    /// — the resolver's accept-set migrates as a unit on any future
1016    /// axis addition.
1017    ///
1018    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
1019    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
1020    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
1021    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
1022    /// lift folds on. Same "one typed dispatch on the substrate
1023    /// primitive, thin projections at each consumer" discipline the
1024    /// peer per-`:placement`
1025    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1026    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1027    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1028    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1029    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1030    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1031    /// typed-slot atom axes, extended here to the third outer top-level
1032    /// `Caixa` universal-axis surface. Named `descricao()` to match the
1033    /// storage field's name; the accessor's identity maps onto the
1034    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1035    /// carries. The one remaining universal `Option<String>` slot
1036    /// (`:edicao`) folds on this pattern next.
1037    #[must_use]
1038    pub const fn descricao(&self) -> Option<&str> {
1039        match &self.descricao {
1040            Some(s) => Some(s.as_str()),
1041            None => None,
1042        }
1043    }
1044
1045    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
1046    /// accessor every consumer of the top-level manifest's tatara-lisp
1047    /// edition-selector axis keys off — returns the author-declared
1048    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
1049    /// the typed slot's own `Option<String>` storage. `None` when the
1050    /// slot is absent (the canonical "omit the slot to defer to the
1051    /// substrate's default edition" shape every existing
1052    /// [`caixa-resolver`] integration test fixture carries via
1053    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
1054    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
1055    /// arm by construction, so an author-omitted `:edicao` round-trips
1056    /// to a build without triggering the year-shape predicate).
1057    ///
1058    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
1059    /// decimal-year language-edition identifier every kind of caixa
1060    /// emits under (CAIXA-SDLC §I — the author-facing surface every
1061    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
1062    /// accept-set (empty-string rejected through
1063    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
1064    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
1065    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
1066    /// onto one load-bearing downstream consumer today
1067    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
1068    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
1069    /// future edition-aware substrate consumer the CAIXA-SDLC §I
1070    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
1071    /// selector every edition-aware build step keys off, the future
1072    /// per-edition compatibility-flag overlay the M4 CR materializer
1073    /// resolves per-CR, the peer [`Caixa::template`] canonical
1074    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
1075    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
1076    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
1077    /// carry `edicao: Some("2026".into())` by construction).
1078    ///
1079    /// Prior to this lift the `.edicao` field was accessed inline at
1080    /// one production site — [`Self::validate_edicao`]'s
1081    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
1082    /// open-coded field-access that expressed no compile-time link
1083    /// back to the typed slot. A future extension of the `:edicao`
1084    /// axis to a richer author surface — a per-`:edicao` known-
1085    /// edition allowlist (the future tightening
1086    /// [`Self::validate_edicao`]'s docstring acknowledges past the
1087    /// structural year-shape floor, rejecting year-shaped values that
1088    /// don't name a tatara-lisp edition the substrate actually
1089    /// understands — `"1999"` is year-shaped but no `1999` edition
1090    /// exists), a per-edition compatibility-flag overlay the M4 CR
1091    /// materializer resolves per-CR (the "edition `"2026"` enables
1092    /// macro-surface features the sibling `"2018"` gates behind a
1093    /// feature flag" arm the edition-selector story anticipates), a
1094    /// promotion of the plain `Option<String>` byte-string to a
1095    /// richer `CaixaEdition` enum discriminated on year once a sibling
1096    /// edition to `"2026"` lands — would have had to be threaded
1097    /// through the open-coded copy in lockstep with every future
1098    /// edition-aware consumer, or the validate gate and the future
1099    /// edition-aware consumer path would silently disagree on which
1100    /// edition a given [`Caixa`] resolves to (an author's
1101    /// `:edicao "2026"` would satisfy validate while a future
1102    /// edition-aware consumer silently defaulted to a stale edition,
1103    /// or vice versa). Lifting the resolution to a typed method on
1104    /// the substrate primitive means every downstream consumer of the
1105    /// caixa's per-`Caixa` edition surface reaches for exactly one
1106    /// typed dispatch — the resolver's accept-set migrates as a unit
1107    /// on any future axis addition.
1108    ///
1109    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
1110    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
1111    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
1112    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
1113    /// `Option<&str>` scalar" projection pattern this lift folds on.
1114    /// Same "one typed dispatch on the substrate primitive, thin
1115    /// projections at each consumer" discipline the peer per-`:placement`
1116    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1117    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1118    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1119    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1120    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1121    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1122    /// typed-slot atom axes, extended here to close the outer top-level
1123    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
1124    /// slot. Named `edicao()` to match the storage field's name; the
1125    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1126    /// vocabulary the slot's docstring already carries.
1127    #[must_use]
1128    pub const fn edicao(&self) -> Option<&str> {
1129        match &self.edicao {
1130            Some(s) => Some(s.as_str()),
1131            None => None,
1132        }
1133    }
1134
1135    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
1136    /// label caixa-identity scalar accessor every consumer of the top-
1137    /// level manifest's identity axis keys off — returns the author-
1138    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
1139    /// the typed slot's own `String` storage. Non-optional (`:nome` is
1140    /// a required-axis scalar every `defcaixa` form must supply; the
1141    /// [`Self::from_lisp`] derive rejects an omitted / non-string
1142    /// `:nome` at parse time, so a `Caixa` past parse definitionally
1143    /// carries a non-`None` `:nome`).
1144    ///
1145    /// The `:nome` slot carries the universal-axis DNS-1123-label
1146    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
1147    /// the primary identity axis every `defcaixa` form supplies
1148    /// alongside `:versao` / `:kind`; the substrate-wide identity every
1149    /// other typed surface that names a caixa reaches through — `:deps`
1150    /// entries, `:membros` entries, `:children` entries, the
1151    /// `lareira-<nome>` Helm chart name every per-Servico renderer
1152    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
1153    /// renderer emits) — the typed slot's `String` accept-set (empty
1154    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
1155    /// invalid rejected through [`ManifestError::NomeInvalid`] past
1156    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
1157    /// the peer name axes each land on, joint-length-with-`lareira-`-
1158    /// prefix rejected through
1159    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
1160    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
1161    /// load-bearing downstream consumer the substrate carries — the
1162    /// two universal-axis validate gates at caixa-build time
1163    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
1164    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
1165    /// derivation every per-Servico renderer keys off, the caixa-helm
1166    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
1167    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
1168    /// `HTTPRoute` per-Aplicacao name axes at
1169    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
1170    /// [`crate::pleme_program_selector`] /
1171    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
1172    /// derivations, and every future substrate renderer that emits an
1173    /// artifact keyed by the caixa's identity.
1174    ///
1175    /// Prior to this lift the `.nome` field was accessed inline at a
1176    /// dozen production sites across `caixa-core` (the two universal-
1177    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
1178    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
1179    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
1180    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
1181    /// entry `name:` fold, the `flux_kustomization_source_subtree`
1182    /// per-cluster subpath derivation), and `caixa-mesh` (the
1183    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
1184    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
1185    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
1186    /// insert) — a dozen open-coded field-accesses that expressed no
1187    /// compile-time link back to the typed slot. A future extension of
1188    /// the `:nome` axis to a richer author surface — a per-`:nome`
1189    /// structured `CaixaIdentity` newtype that carries the joint-
1190    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
1191    /// enforces at the type level (rather than as a validate-time
1192    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
1193    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
1194    /// `partner-org/checkout` collision" arm the multi-tenant-registry
1195    /// story acknowledges), a promotion of the plain `String` byte-
1196    /// string to a richer `CaixaNome` newtype discriminated on
1197    /// namespace prefix — would have had to be threaded through every
1198    /// open-coded copy in lockstep or the two validate gates and the
1199    /// dozen emit paths would silently disagree on which identity a
1200    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
1201    /// would satisfy validate while one of the emit paths silently
1202    /// rendered a drifted other identity, or vice versa). Lifting the
1203    /// resolution to a typed method on the substrate primitive means
1204    /// every downstream consumer of the caixa's per-`Caixa` identity
1205    /// surface reaches for exactly one typed dispatch — the resolver's
1206    /// accept-set migrates as a unit on any future axis addition.
1207    ///
1208    /// First outer top-level [`Caixa`] `&str`-return required-scalar
1209    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
1210    /// projection pattern the sibling per-`Caixa` `:versao` future lift
1211    /// folds on. Sibling in shape to the peer per-`:membros`
1212    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
1213    /// [`crate::aplicacao::WitContract::source`] /
1214    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
1215    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
1216    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
1217    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
1218    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
1219    /// per-sub-struct required-axis accessors carry on the sibling M3
1220    /// mesh-slot-atom scalar-value axes, extended here to open the
1221    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
1222    /// Named `nome()` to match the storage field's name; the accessor's
1223    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1224    /// slot's docstring already carries.
1225    #[must_use]
1226    pub const fn nome(&self) -> &str {
1227        self.nome.as_str()
1228    }
1229
1230    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
1231    /// pinned-version scalar accessor every consumer of the top-level
1232    /// manifest's version axis keys off — returns the author-declared
1233    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
1234    /// typed slot's own `String` storage. Non-optional (`:versao` is a
1235    /// required-axis scalar every `defcaixa` form must supply alongside
1236    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
1237    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
1238    /// parse definitionally carries a non-`None` `:versao`).
1239    ///
1240    /// The `:versao` slot carries the universal-axis SemVer-2
1241    /// concrete-version body every kind of caixa emits under
1242    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
1243    /// supplies alongside `:nome` / `:kind`; the substrate-wide
1244    /// pinned-version every downstream artifact-emitting consumer
1245    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
1246    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
1247    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
1248    /// prefix composes on top of, the programs.yaml entry's `versao:`
1249    /// value the `lareira-fleet-programs` aggregator carries onto each
1250    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
1251    /// tags every substrate-side `skopeo push` writes, the lacre
1252    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
1253    /// prior-version references peers in the exact same SemVer-2 shape).
1254    /// The typed slot's `String` accept-set (empty rejected through
1255    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
1256    /// through [`ManifestError::VersaoInvalid`] past
1257    /// [`semver::Version::parse`]) maps onto every load-bearing
1258    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
1259    /// universal-axis validate gate at caixa-build time, the
1260    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
1261    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
1262    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
1263    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
1264    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
1265    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
1266    /// tag derivation (`format!("{prefix}{versao}")`), and every future
1267    /// substrate renderer that emits an artifact keyed by the caixa's
1268    /// pinned version.
1269    ///
1270    /// Prior to this lift the `.versao` field was accessed inline at a
1271    /// dozen production sites across `caixa-core` (the universal-axis
1272    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
1273    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
1274    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
1275    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
1276    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
1277    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
1278    /// (the `feira publish` git-tag derivation + the `feira app graph` /
1279    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
1280    /// field-accesses that expressed no compile-time link back to the
1281    /// typed slot. A future extension of the `:versao` axis to a richer
1282    /// author surface — a per-`:versao` structured `CaixaVersion` at the
1283    /// storage layer (the substrate already carries a `CaixaVersion`
1284    /// newtype at [`crate::version::CaixaVersion`], deferred until the
1285    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
1286    /// a per-registry `:versao` immutability overlay the M4 CR
1287    /// materializer enforces per-CR, a promotion of the plain `String`
1288    /// byte-string to a richer `PinnedVersao` newtype discriminated on
1289    /// SemVer-2 pre-release / build-metadata presence — would have had
1290    /// to be threaded through every open-coded copy in lockstep or the
1291    /// validate gate and the dozen emit paths would silently disagree
1292    /// on which version a given [`Caixa`] resolves to (an author's
1293    /// `:versao "0.1.0"` would satisfy validate while one of the emit
1294    /// paths silently rendered a drifted other version, or vice versa).
1295    /// Lifting the resolution to a typed method on the substrate
1296    /// primitive means every downstream consumer of the caixa's
1297    /// per-`Caixa` pinned-version surface reaches for exactly one typed
1298    /// dispatch — the resolver's accept-set migrates as a unit on any
1299    /// future axis addition.
1300    ///
1301    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
1302    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
1303    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
1304    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
1305    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
1306    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
1307    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
1308    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
1309    /// on the sibling per-typed-slot version-carrier axes, extended here
1310    /// to close the second outer top-level [`Caixa`] required-`&str`-
1311    /// carrying axis so the two universal-axis identity-carrying
1312    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
1313    /// share the same "one typed dispatch per axis" discipline. Named
1314    /// `versao()` to match the storage field's name; the accessor's
1315    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1316    /// slot's docstring already carries.
1317    #[must_use]
1318    pub const fn versao(&self) -> &str {
1319        self.versao.as_str()
1320    }
1321
1322    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
1323    /// closed-set-enum discriminant accessor every consumer of the top-
1324    /// level manifest's kind axis keys off — returns the author-declared
1325    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
1326    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
1327    /// (`:kind` is a required-axis discriminant every `defcaixa` form
1328    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
1329    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
1330    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
1331    /// variant).
1332    ///
1333    /// The `:kind` slot carries the universal-axis closed-set typed-
1334    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
1335    /// §I — the primary shape gate every renderer / verifier /
1336    /// operator branches on; the five variants `Biblioteca` /
1337    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1338    /// the caixa surface into disjoint runtime contracts) — the typed
1339    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1340    /// values through the derive-macro's symbol-arm gate, exhaustively
1341    /// matched at every downstream dispatch site) maps onto every
1342    /// load-bearing downstream consumer the substrate carries:
1343    ///
1344    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1345    ///     predicate — the canonical two-line
1346    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1347    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1348    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1349    ///     ComputeUnit` CR materializer) runs at its entry-point,
1350    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1351    ///     `actual:` field the diagnostic surfaces to name the offending
1352    ///     caixa's variant.
1353    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1354    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1355    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1356    ///     slot columns into their typed sub-spec only when the kind
1357    ///     matches (returns `None` otherwise); the future per-Servico
1358    ///     M2-view composer (`servico_view`) will follow the same shape.
1359    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1360    ///     coherence gate — the `!self.kind.requires_exe()` /
1361    ///     `!self.kind.requires_servicos()` predicates that fence
1362    ///     each code-surface slot from the wrong owning kind.
1363    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1364    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1365    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1366    ///     coherence error carriers (`SupervisorOwnsCode` /
1367    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1368    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1369    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1370    ///     variant in their `kind:` field.
1371    ///
1372    /// Prior to this lift the `.kind` field was accessed inline at
1373    /// twenty-plus production sites across `caixa-core` (the
1374    /// [`crate::render::require_kind`] entry-gate predicate + the
1375    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1376    /// composers, the `declared_foreign_code_slots` per-slot kind-
1377    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1378    /// kind ↔ code-surface predicates + four error carriers) — a score
1379    /// of open-coded field-accesses that expressed no compile-time link
1380    /// back to the typed slot. A future extension of the `:kind` axis
1381    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1382    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1383    /// variant across the wasm-component / legacy-container / native-
1384    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1385    /// kind-overlay the M4 CR materializer resolves per-CR (the
1386    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1387    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1388    /// enum to a richer `KindWithRuntime` discriminated on the
1389    /// component-model world axis — would have had to be threaded
1390    /// through every open-coded copy in lockstep or the entry gate,
1391    /// the view composers, and the layout invariants would silently
1392    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1393    /// the resolution to a typed method on the substrate primitive
1394    /// means every downstream consumer of the caixa's per-`Caixa`
1395    /// kind surface reaches for exactly one typed dispatch — the
1396    /// resolver's accept-set migrates as a unit on any future axis
1397    /// addition.
1398    ///
1399    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1400    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1401    /// required-discriminant" projection pattern. Sibling in shape to
1402    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1403    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1404    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1405    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1406    /// on the sibling nested-spec typed-slot discriminator axes,
1407    /// extended here to the outer top-level [`Caixa`] universal-axis
1408    /// surface. Named `kind()` to match the storage field's name;
1409    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1410    /// vocabulary the slot's docstring already carries.
1411    #[must_use]
1412    pub const fn kind(&self) -> CaixaKind {
1413        self.kind
1414    }
1415
1416    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1417    /// maintainer-name-list slice-accessor every consumer of the top-
1418    /// level manifest's maintainer axis keys off — returns the author-
1419    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1420    /// the same backing buffer the raw `self.autores.as_slice()` field
1421    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1422    /// empty axis every `defcaixa` form supplies with an empty `()` when
1423    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1424    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1425    /// parse definitionally carries a `Vec<String>` slot — possibly
1426    /// empty — and the returned `&[String]` degenerates to an empty
1427    /// slice on that arm without any silent `None` collapse).
1428    ///
1429    /// The `:autores` slot carries the universal-axis maintainer-name
1430    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1431    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1432    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1433    /// every downstream registry-facing artifact emits under) — the
1434    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1435    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1436    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1437    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1438    /// onto every load-bearing downstream consumer the substrate carries
1439    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1440    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1441    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1442    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1443    /// name, email: None }` record, every future per-`Caixa` registry-
1444    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1445    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1446    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1447    /// the future per-cluster author-notification overlay the M4 CR
1448    /// materializer resolves per-CR).
1449    ///
1450    /// Prior to this lift the `.autores` field was accessed inline at
1451    /// two production sites — [`Self::validate_autores`]'s `for autor
1452    /// in &self.autores` walk that gates every entry through
1453    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1454    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1455    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1456    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1457    /// two open-coded field-accesses that expressed no compile-time link
1458    /// back to the typed slot. A future extension of the `:autores` axis
1459    /// to a richer author surface — a per-`:autores` structured
1460    /// `Maintainer { name, email, url }` at the storage layer once the
1461    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1462    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1463    /// enforces per-CR (the "cluster policy demands every author declare
1464    /// an on-file `mailto:` contact" arm), a promotion of the plain
1465    /// `Vec<String>` byte-string list to a richer
1466    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1467    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1468    /// predicate already resolves through — would have had to be
1469    /// threaded through both open-coded copies in lockstep or the
1470    /// validate gate and the caixa-helm emit path would silently
1471    /// disagree on which authors a given [`Caixa`] resolves to (an
1472    /// author's `:autores ("alice" "bob")` would satisfy validate while
1473    /// the caixa-helm emit path silently rendered a drifted other
1474    /// maintainer list, or vice versa). Lifting the resolution to a
1475    /// typed method on the substrate primitive means every downstream
1476    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1477    /// for exactly one typed dispatch — the resolver's accept-set
1478    /// migrates as a unit on any future axis addition.
1479    ///
1480    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1481    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1482    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1483    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1484    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1485    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1486    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1487    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1488    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1489    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1490    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1491    /// per-M3 typed-slot list axes, extended here to the outer top-level
1492    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1493    /// `&Vec<String>`) because every downstream consumer of the author
1494    /// list treats it as a read-only sequence — the slice-view is the
1495    /// narrowest borrow that supports every present + roadmapped consumer
1496    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1497    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1498    /// reaches for (the storage-side `Vec` remains reachable through the
1499    /// `pub autores` field for the mutation-carrying serde round-trip and
1500    /// per-test fixture-mutation paths). Named `autores()` to match the
1501    /// storage field's name; the accessor's identity maps onto the
1502    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1503    /// carries.
1504    #[must_use]
1505    pub const fn autores(&self) -> &[String] {
1506        self.autores.as_slice()
1507    }
1508
1509    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1510    /// registry-search-tag-list slice-accessor every consumer of the
1511    /// top-level manifest's topical-tag axis keys off — returns the
1512    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1513    /// slice-view over the same backing buffer the raw
1514    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1515    /// list-carrying (`:etiquetas` is a default-empty axis every
1516    /// `defcaixa` form supplies with an empty `()` when unset; the
1517    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1518    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1519    /// definitionally carries a `Vec<String>` slot — possibly empty —
1520    /// and the returned `&[String]` degenerates to an empty slice on
1521    /// that arm without any silent `None` collapse).
1522    ///
1523    /// The `:etiquetas` slot carries the universal-axis topical-tag
1524    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1525    /// author-facing surface every `defcaixa` form supplies alongside
1526    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1527    /// search-facing axis every downstream registry-facing artifact
1528    /// emits under) — the typed slot's `Vec<String>` accept-set
1529    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1530    /// non-chart-keyword-shape rejected through
1531    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1532    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1533    /// every load-bearing downstream consumer the substrate carries —
1534    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1535    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1536    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1537    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1538    /// `Chart.yaml` `keywords:` array (chained with the
1539    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1540    /// dedup'd through a `BTreeSet` at emit time), every future per-
1541    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1542    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1543    /// annotation, the future per-cluster tag-notification overlay the
1544    /// M4 CR materializer resolves per-CR).
1545    ///
1546    /// Prior to this lift the `.etiquetas` field was accessed inline at
1547    /// two production sites — [`Self::validate_etiquetas`]'s `for
1548    /// etiqueta in &self.etiquetas` walk that gates every entry through
1549    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1550    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1551    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1552    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1553    /// two open-coded field-accesses that expressed no compile-time
1554    /// link back to the typed slot. A future extension of the
1555    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1556    /// structured `ChartKeyword { name, uri, category }` at the storage
1557    /// layer once the substrate absorbs `artifacthub.io/keywords`
1558    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1559    /// CR materializer enforces per-CR (the "cluster policy demands
1560    /// every tag come from a substrate-approved taxonomy" arm), a
1561    /// promotion of the plain `Vec<String>` byte-string list to a
1562    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1563    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1564    /// already resolves through — would have had to be threaded through
1565    /// both open-coded copies in lockstep or the validate gate and the
1566    /// caixa-helm emit path would silently disagree on which tags a
1567    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1568    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1569    /// path silently rendered a drifted other keyword list, or vice
1570    /// versa). Lifting the resolution to a typed method on the
1571    /// substrate primitive means every downstream consumer of the
1572    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1573    /// typed dispatch — the resolver's accept-set migrates as a unit
1574    /// on any future axis addition.
1575    ///
1576    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1577    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1578    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1579    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1580    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1581    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1582    /// fold onto the same pattern in future lifts. Sibling in shape to
1583    /// the peer per-`:supervisor`
1584    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1585    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1586    /// (a6e18d7), per-`:membros`
1587    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1588    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1589    /// (0dcc926), and per-`:upgrade-from :instructions`
1590    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1591    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1592    /// typed-slot list axes, extended here to the outer top-level
1593    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1594    /// `&Vec<String>`) because every downstream consumer of the tag
1595    /// list treats it as a read-only sequence — the slice-view is the
1596    /// narrowest borrow that supports every present + roadmapped
1597    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1598    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1599    /// the typed view reaches for (the storage-side `Vec` remains
1600    /// reachable through the `pub etiquetas` field for the mutation-
1601    /// carrying serde round-trip and per-test fixture-mutation paths).
1602    /// Named `etiquetas()` to match the storage field's name; the
1603    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1604    /// vocabulary the slot's docstring already carries.
1605    #[must_use]
1606    pub const fn etiquetas(&self) -> &[String] {
1607        self.etiquetas.as_slice()
1608    }
1609
1610    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1611    /// library-source-path-list slice-accessor every consumer of the
1612    /// top-level manifest's Biblioteca-source axis keys off — returns
1613    /// the author-declared `:bibliotecas` list verbatim as a
1614    /// `&[String]` slice-view over the same backing buffer the raw
1615    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1616    /// list-carrying (`:bibliotecas` is a default-empty axis every
1617    /// `defcaixa` form supplies with an empty `()` when unset; the
1618    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1619    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1620    /// parse definitionally carries a `Vec<String>` slot — possibly
1621    /// empty — and the returned `&[String]` degenerates to an empty
1622    /// slice on that arm without any silent `None` collapse).
1623    ///
1624    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1625    /// entry-path list every `:kind Biblioteca` caixa emits under
1626    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1627    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1628    /// substrate-wide library-carrier axis every downstream
1629    /// authoring-facing consumer keys off) — the typed slot's
1630    /// `Vec<String>` accept-set (empty-per-entry rejected through
1631    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1632    /// non-sandboxed-relative-shape rejected through
1633    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1634    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1635    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1636    /// maps onto every load-bearing downstream consumer the substrate
1637    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1638    /// empty-check + per-entry file-exists loop at
1639    /// caixa-core/src/layout.rs that gates each entry through
1640    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1641    /// [`Self::validate_code_paths`] per-slot shape gate at
1642    /// caixa-core/src/manifest.rs that walks each entry through the
1643    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1644    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1645    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1646    /// declared library file for lexical / structural errors before
1647    /// downstream `importar` resolution, every future per-`Caixa`
1648    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1649    /// (the future `tatara-lispc` compilation entry the docstring at
1650    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1651    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1652    /// the future `caixa-lsp` per-library semantic-token stream the
1653    /// caixa-lsp docstring roadmaps).
1654    ///
1655    /// Prior to this lift the `.bibliotecas` field was accessed inline
1656    /// at three production sites — [`crate::LayoutInvariants`]'s
1657    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1658    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1659    /// declared library path through the on-disk-existence check,
1660    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1661    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1662    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1663    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1664    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1665    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1666    /// coded field-accesses that expressed no compile-time link back
1667    /// to the typed slot. A future extension of the `:bibliotecas`
1668    /// axis to a richer library surface — a per-`:bibliotecas`
1669    /// structured `BibliotecaEntry { path, edition, exports }` at the
1670    /// storage layer once the substrate absorbs the per-library
1671    /// language-edition + explicit-exports tuple the tatara-lisp
1672    /// module-system roadmap acknowledges, a per-registry
1673    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1674    /// per-CR (the "cluster policy demands every biblioteca declare
1675    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1676    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1677    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1678    /// [`crate::render::is_sandboxed_relative_path`] +
1679    /// [`crate::render::is_lisp_extension`] predicates already resolve
1680    /// through — would have had to be threaded through all three
1681    /// open-coded copies in lockstep or the layout gate, the shape
1682    /// validator, and the `feira build` phase-1 parse walk would
1683    /// silently disagree on which library paths a given [`Caixa`]
1684    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1685    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1686    /// silently parsed a drifted other list, or vice versa). Lifting
1687    /// the resolution to a typed method on the substrate primitive
1688    /// means every downstream consumer of the caixa's per-`Caixa`
1689    /// library-source surface reaches for exactly one typed dispatch
1690    /// — the resolver's accept-set migrates as a unit on any future
1691    /// axis addition.
1692    ///
1693    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1694    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1695    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1696    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1697    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1698    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1699    /// `:children` / `:membros` / `:contratos`) fold onto the same
1700    /// pattern in future lifts. Sibling in shape to the peer
1701    /// per-`:supervisor`
1702    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1703    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1704    /// (a6e18d7), per-`:membros`
1705    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1706    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1707    /// (0dcc926), and per-`:upgrade-from :instructions`
1708    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1709    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1710    /// typed-slot list axes, extended here to the outer top-level
1711    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1712    /// `&Vec<String>`) because every downstream consumer of the
1713    /// library-source list treats it as a read-only sequence — the
1714    /// slice-view is the narrowest borrow that supports every
1715    /// present + roadmapped consumer (`.iter()`, `.len()`,
1716    /// `.is_empty()`) without leaking the backing `Vec`'s
1717    /// grow/push/reserve surface no consumer of the typed view
1718    /// reaches for (the storage-side `Vec` remains reachable through
1719    /// the `pub bibliotecas` field for the mutation-carrying serde
1720    /// round-trip and per-test fixture-mutation paths). Named
1721    /// `bibliotecas()` to match the storage field's name; the
1722    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1723    /// vocabulary the slot's docstring already carries.
1724    #[must_use]
1725    pub const fn bibliotecas(&self) -> &[String] {
1726        self.bibliotecas.as_slice()
1727    }
1728
1729    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1730    /// nix-built-executable-entry-path-list slice-accessor every consumer
1731    /// of the top-level manifest's Binario-executable axis keys off —
1732    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1733    /// slice-view over the same backing buffer the raw
1734    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1735    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1736    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1737    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1738    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1739    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1740    /// degenerates to an empty slice on that arm without any silent
1741    /// `None` collapse).
1742    ///
1743    /// The `:exe` slot carries the universal-axis nix-built executable
1744    /// entry-path list every `:kind Binario` caixa emits under
1745    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1746    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1747    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1748    /// downstream flake-build-facing consumer keys off) — the typed
1749    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1750    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1751    /// non-sandboxed-relative-shape rejected through
1752    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1753    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1754    /// directory paths rejected past the layout's
1755    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1756    /// onto every load-bearing downstream consumer the substrate carries
1757    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1758    /// per-entry file-exists + `exe/`-directory-fence loop at
1759    /// caixa-core/src/layout.rs that gates each entry through
1760    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1761    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1762    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1763    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1764    /// that fences code-surface slots off from the two no-code kinds,
1765    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1766    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1767    /// fences the `:exe` code surface off from every non-Binario code-
1768    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1769    /// that walks each entry through the sandbox-relative / cross-entry
1770    /// duplicate gates, every future per-`Caixa` executable-facing
1771    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1772    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1773    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1774    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1775    /// future `feira nix` per-executable Binario-target emit path).
1776    ///
1777    /// Prior to this lift the `.exe` field was accessed inline at three
1778    /// production sites — the compound-code-path `has_code =
1779    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1780    /// !caixa.servicos.is_empty()` OR-fold on the
1781    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1782    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1783    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1784    /// gate, the per-entry `for p in &caixa.exe`
1785    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1786    /// [`Self::declared_foreign_code_slots`]'s
1787    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1788    /// open-coded field-accesses that expressed no compile-time link
1789    /// back to the typed slot. A future extension of the `:exe` axis
1790    /// to a richer executable surface — a per-`:exe` structured
1791    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1792    /// layer once the substrate absorbs the per-executable
1793    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1794    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1795    /// the M4 CR materializer enforces per-CR (the "cluster policy
1796    /// demands every Binario declare an explicit `:wrapper`" arm), a
1797    /// promotion of the plain `Vec<String>` byte-string list to a
1798    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1799    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1800    /// fence already resolves through — would have had to be threaded
1801    /// through all four open-coded copies in lockstep or the layout
1802    /// gate, the shape validator, and the `feira nix` emit path would
1803    /// silently disagree on which executable paths a given [`Caixa`]
1804    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1805    /// satisfy layout while `feira nix` silently packaged a drifted
1806    /// other list, or vice versa). Lifting the resolution to a typed
1807    /// method on the substrate primitive means every downstream
1808    /// consumer of the caixa's per-`Caixa` executable-source surface
1809    /// reaches for exactly one typed dispatch — the resolver's accept-
1810    /// set migrates as a unit on any future axis addition.
1811    ///
1812    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1813    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1814    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1815    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1816    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1817    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1818    /// future lift closes onto (per the trio of code-surface list slots
1819    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1820    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1821    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1822    /// last unlifted code-surface slot). Sibling in shape to the peer
1823    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1824    /// (bc92bce), per-`:placement`
1825    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1826    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1827    /// (6c77e36), per-`:contratos`
1828    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1829    /// per-`:upgrade-from :instructions`
1830    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1831    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1832    /// typed-slot list axes, extended here to the outer top-level
1833    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1834    /// `&Vec<String>`) because every downstream consumer of the
1835    /// executable-source list treats it as a read-only sequence — the
1836    /// slice-view is the narrowest borrow that supports every
1837    /// present + roadmapped consumer (`.iter()`, `.len()`,
1838    /// `.is_empty()`) without leaking the backing `Vec`'s
1839    /// grow/push/reserve surface no consumer of the typed view
1840    /// reaches for (the storage-side `Vec` remains reachable through
1841    /// the `pub exe` field for the mutation-carrying serde
1842    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1843    /// to match the storage field's name; the accessor's identity
1844    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1845    /// docstring already carries.
1846    #[must_use]
1847    pub const fn exe(&self) -> &[String] {
1848        self.exe.as_slice()
1849    }
1850
1851    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1852    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1853    /// of the top-level manifest's Servico-component axis keys off —
1854    /// returns the author-declared `:servicos` list verbatim as a
1855    /// `&[String]` slice-view over the same backing buffer the raw
1856    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1857    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1858    /// form supplies with an empty `()` when unset; the
1859    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1860    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1861    /// definitionally carries a `Vec<String>` slot — possibly empty —
1862    /// and the returned `&[String]` degenerates to an empty slice on
1863    /// that arm without any silent `None` collapse).
1864    ///
1865    /// The `:servicos` slot carries the universal-axis
1866    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1867    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1868    /// author-facing surface every `defcaixa` form supplies alongside
1869    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1870    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1871    /// Servico-facing renderer keys off) — the typed slot's
1872    /// `Vec<String>` accept-set (empty-per-entry rejected through
1873    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1874    /// non-sandboxed-relative-shape rejected through
1875    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1876    /// extension rejected through
1877    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1878    /// entry duplicate rejected through
1879    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1880    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1881    /// renderer entry-points, out-of-`servicos/`-directory paths
1882    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1883    /// `starts_with` fence) maps onto every load-bearing downstream
1884    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1885    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1886    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1887    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1888    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1889    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1890    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1891    /// that fences code-surface slots off from the two no-code kinds,
1892    /// [`Self::declared_foreign_code_slots`]'s
1893    /// `!self.servicos.is_empty()` arm on the
1894    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1895    /// `:servicos` code surface off from every non-Servico code-running
1896    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1897    /// walks each entry through the sandbox-relative / `.computeunit.
1898    /// yaml`-extension / cross-entry duplicate gates, the
1899    /// [`crate::require_single_servico`] V0 singularity gate every
1900    /// per-Servico renderer entry-point runs through
1901    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1902    /// `feira deploy` per-verb `first_servico_path` walk at
1903    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1904    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1905    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1906    /// per-Servico OCI packager, the future M4
1907    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1908    /// per-Servico OTel collector-config emit).
1909    ///
1910    /// Prior to this lift the `.servicos` field was accessed inline at
1911    /// five production sites — the compound-code-path `has_code =
1912    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1913    /// !caixa.servicos.is_empty()` OR-fold on the
1914    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1915    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1916    /// `caixa.servicos.is_empty()`
1917    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1918    /// per-entry `for p in &caixa.servicos`
1919    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1920    /// [`Self::declared_foreign_code_slots`]'s
1921    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1922    /// and the [`crate::require_single_servico`] V0 count gate's
1923    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1924    /// projection (both the accept-arm predicate and the
1925    /// diagnostic-carrying `ServicoCountMismatch { count }`
1926    /// projection) — five open-coded field-accesses across three
1927    /// crates that expressed no compile-time link back to the typed
1928    /// slot. A future extension of the `:servicos` axis to a richer
1929    /// component surface — a per-`:servicos` structured
1930    /// `ServicoEntry { path, world, capabilities }` at the storage
1931    /// layer once the substrate absorbs the per-component WIT-world +
1932    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1933    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1934    /// materializer enforces per-CR (the "cluster policy demands every
1935    /// Servico declare an explicit `:world`" arm), a promotion of the
1936    /// plain `Vec<String>` byte-string list to a richer
1937    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1938    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1939    /// `starts_with(servicos_dir)` fence and the
1940    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1941    /// already resolve through, a promotion of the V0 singleton
1942    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1943    /// component-model multi-world boundary — would have had to be
1944    /// threaded through all five open-coded copies in lockstep or the
1945    /// layout gate, the shape validator, the V0 count gate, and the
1946    /// `feira chart` / `feira deploy` entry-point walks would silently
1947    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1948    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1949    /// yaml")` would satisfy layout while `feira chart` silently
1950    /// packaged a drifted other list, or vice versa). Lifting the
1951    /// resolution to a typed method on the substrate primitive means
1952    /// every downstream consumer of the caixa's per-`Caixa`
1953    /// ComputeUnit-CR-source surface reaches for exactly one typed
1954    /// dispatch — the resolver's accept-set migrates as a unit on any
1955    /// future axis addition.
1956    ///
1957    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1958    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1959    /// projection pattern [`Self::autores`] (b5d813f) opened,
1960    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1961    /// (8a36c23) closed the universal-axis text-tag family of, and
1962    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1963    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1964    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1965    /// a substrate-canonical slice accessor, the trio of code-surface
1966    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1967    /// tuple carries is complete on the typed dispatch surface (the
1968    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1969    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1970    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1971    /// per-element accessor swap in isolation — a future companion lift
1972    /// promotes the tuple's element type to `&[String]` and threads the
1973    /// triple of typed dispatches through as a unit). Sibling in shape
1974    /// to the peer per-`:supervisor`
1975    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1976    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1977    /// (a6e18d7), per-`:membros`
1978    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1979    /// per-`:contratos`
1980    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1981    /// per-`:upgrade-from :instructions`
1982    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1983    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1984    /// typed-slot list axes, extended here to the outer top-level
1985    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1986    /// `&Vec<String>`) because every downstream consumer of the
1987    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1988    /// the slice-view is the narrowest borrow that supports every
1989    /// present + roadmapped consumer (`.iter()`, `.len()`,
1990    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1991    /// grow/push/reserve surface no consumer of the typed view reaches
1992    /// for (the storage-side `Vec` remains reachable through the
1993    /// `pub servicos` field for the mutation-carrying serde round-trip
1994    /// and per-test fixture-mutation paths, and for the
1995    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1996    /// homogeneous-element-type shape carries the raw field access
1997    /// until the trio-closure lift promotes the tuple as a unit).
1998    /// Named `servicos()` to match the storage field's name; the
1999    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
2000    /// vocabulary the slot's docstring already carries.
2001    #[must_use]
2002    pub const fn servicos(&self) -> &[String] {
2003        self.servicos.as_slice()
2004    }
2005
2006    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
2007    /// runtime-dependency-declaration-list slice-accessor every consumer
2008    /// of the top-level manifest's runtime-dep-graph axis keys off —
2009    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
2010    /// slice-view over the same backing buffer the raw
2011    /// `self.deps.as_slice()` field access borrows from. Empty-list-
2012    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
2013    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
2014    /// derive folds an omitted `:deps` through `#[serde(default)]` to
2015    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
2016    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
2017    /// degenerates to an empty slice on that arm without any silent
2018    /// `None` collapse).
2019    ///
2020    /// The `:deps` slot carries the universal-axis runtime dependency
2021    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
2022    /// facing surface every `defcaixa` form supplies alongside `:nome` /
2023    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
2024    /// every downstream resolver-facing artifact emits under) — the
2025    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
2026    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
2027    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
2028    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
2029    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
2030    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
2031    /// maps onto every load-bearing downstream consumer the substrate
2032    /// carries — the [`Self::validate_deps`] per-entry
2033    /// [`Dep::validate`] + within-list dedup walk at
2034    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
2035    /// cross-list self-reference gate at caixa-core/src/layout.rs that
2036    /// checks each entry against the caixa's own `:nome`, the
2037    /// caixa-resolver `for dep in &root.deps` closure walk at
2038    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
2039    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
2040    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
2041    /// caixa-crd/src/conversion.rs that materializes each entry into the
2042    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
2043    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
2044    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
2045    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
2046    /// closure emit walk the caixa-resolver docstring roadmaps).
2047    ///
2048    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2049    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2050    /// sibling `:deps-dev` future lift closes on. Peer of the closed
2051    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
2052    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
2053    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
2054    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
2055    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
2056    /// pattern onto a novel element-type axis (`Dep` composite vs the
2057    /// prior sibling family's `String` scalar). Sibling in shape to the
2058    /// peer per-`:supervisor`
2059    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
2060    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
2061    /// (a6e18d7), per-`:membros`
2062    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
2063    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
2064    /// (0dcc926), and per-`:upgrade-from :instructions`
2065    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2066    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2067    /// typed-slot list axes, extended here to the outer top-level
2068    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
2069    /// (not `&Vec<Dep>`) because every downstream consumer of the
2070    /// runtime-dep list treats it as a read-only sequence — the slice-
2071    /// view is the narrowest borrow that supports every present +
2072    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2073    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2074    /// of the typed view reaches for (the storage-side `Vec` remains
2075    /// reachable through the `pub deps` field for the mutation-carrying
2076    /// serde round-trip and per-test fixture-mutation paths). Named
2077    /// `deps()` to match the storage field's name; the accessor's
2078    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
2079    /// slot's docstring already carries.
2080    #[must_use]
2081    pub const fn deps(&self) -> &[Dep] {
2082        self.deps.as_slice()
2083    }
2084
2085    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
2086    /// development-only-dependency-declaration-list slice-accessor every
2087    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
2088    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
2089    /// slice-view over the same backing buffer the raw
2090    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
2091    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
2092    /// form supplies with an empty `()` when unset; the
2093    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
2094    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
2095    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
2096    /// the returned `&[Dep]` degenerates to an empty slice on that arm
2097    /// without any silent `None` collapse).
2098    ///
2099    /// The `:deps-dev` slot carries the universal-axis dev-only
2100    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
2101    /// the author-facing sibling of `:deps` that every `defcaixa` form
2102    /// supplies to declare tests / lint / bench closures the runtime
2103    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
2104    /// axis every downstream test-facing artifact emits under, matching
2105    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
2106    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
2107    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
2108    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
2109    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
2110    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
2111    /// within-list duplicate `:nome` rejected through
2112    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
2113    /// load-bearing downstream consumer the substrate carries — the
2114    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
2115    /// dedup walk at caixa-core/src/manifest.rs, the
2116    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
2117    /// gate at caixa-core/src/layout.rs that checks each entry against
2118    /// the caixa's own `:nome`, the caixa-resolver
2119    /// `for dep in &root.deps_dev` closure walk at
2120    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
2121    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
2122    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
2123    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
2124    /// overlay the M4 CR materializer resolves per-CR, the future
2125    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
2126    /// roadmaps).
2127    ///
2128    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2129    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2130    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
2131    /// jointly close the two-list dep-graph surface every downstream
2132    /// resolver-facing consumer keys off (runtime `:deps` +
2133    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
2134    /// pair the [`Self::validate_deps`] gate already walks in canonical
2135    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
2136    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
2137    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
2138    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
2139    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
2140    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
2141    /// dev-dep composite-element axis (`Dep` composite, matching the
2142    /// [`Self::deps`] element type). Sibling in shape to the peer
2143    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
2144    /// (bc92bce), per-`:placement`
2145    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
2146    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
2147    /// (6c77e36), per-`:contratos`
2148    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
2149    /// per-`:upgrade-from :instructions`
2150    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2151    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2152    /// typed-slot list axes, folded here to the outer top-level
2153    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
2154    /// (not `&Vec<Dep>`) because every downstream consumer of the
2155    /// dev-dep list treats it as a read-only sequence — the slice-view
2156    /// is the narrowest borrow that supports every present +
2157    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2158    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2159    /// of the typed view reaches for (the storage-side `Vec` remains
2160    /// reachable through the `pub deps_dev` field for the mutation-
2161    /// carrying serde round-trip and per-test fixture-mutation paths).
2162    /// Named `deps_dev()` to match the storage field's `snake_case` name;
2163    /// the kebab-case author-surface tag `:deps-dev` is the same axis
2164    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
2165    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
2166    /// docstring already carries.
2167    #[must_use]
2168    pub const fn deps_dev(&self) -> &[Dep] {
2169        self.deps_dev.as_slice()
2170    }
2171
2172    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
2173    /// every consumer that walks one of the two dep-list axes keyed on a
2174    /// [`crate::dep::DepList`] discriminant reaches for — routes the
2175    /// `(list: DepList) -> &[Dep]` projection through one typed method on
2176    /// the substrate primitive rather than the prior open-coded
2177    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
2178    /// inline dispatch every per-axis walker would otherwise carry.
2179    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
2180    /// `&[Dep]` slice-view over the same backing buffer the sibling
2181    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
2182    /// accessors borrow from, preserving the empty-list-carrying invariant
2183    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
2184    /// are default-empty axes every `defcaixa` form supplies with an empty
2185    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
2186    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
2187    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
2188    /// returned `&[Dep]` degenerates to an empty slice on either arm
2189    /// without any silent `None` collapse).
2190    ///
2191    /// The [`crate::dep::DepList`] closed-set typed enum is the
2192    /// substrate's canonical discriminator for the "runtime-closure
2193    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
2194    /// consumer dispatches on — the compiler-checked exhaustiveness on
2195    /// the enum's `match` arms is the build-time guarantee that no future
2196    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
2197    /// that a future third dep-list axis (a `:deps-build` build-only
2198    /// closure once the substrate grows cross-artifact heterogeneous
2199    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
2200    /// consumer. Prior to this the read side carried two per-slot
2201    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
2202    /// typed dispatch that a per-axis walker could parametrise on, so
2203    /// every per-list walker (the [`Self::validate_deps`] per-list
2204    /// [`crate::render::insert_first_seen`] dedup walk, a future
2205    /// `feira app graph` per-list dep summary, a future M4 per-cluster
2206    /// dev-closure-audit overlay the CR materializer resolves per-CR)
2207    /// open-coded the same two-block "run over `:deps`, then run over
2208    /// `:deps-dev`" pattern — a silent duplication that a future third
2209    /// dep-list axis would have had to grow a third block at every site.
2210    ///
2211    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
2212    /// (359fba5) — closes the two-side dispatch symmetry on the outer
2213    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
2214    /// side, `deps_of` on the read side, both keyed on the same
2215    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
2216    /// the substrate primitive, thin projections at each consumer"
2217    /// discipline the sibling per-slot read accessors ([`Self::nome`]
2218    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
2219    /// the outer-[`Caixa`] typed-dispatch read surface.
2220    ///
2221    /// Declared `pub const fn` — every operator in the body is already
2222    /// `const`-callable (the [`crate::dep::DepList`] enum is a plain
2223    /// closed-set `#[derive(Copy)]` discriminator so the `match` arms
2224    /// are const-evaluable, and each arm forwards through the sibling
2225    /// `pub const fn` [`Self::deps`] / [`Self::deps_dev`] per-slot
2226    /// slice accessor). Pinned load-bearing by the paired
2227    /// [`caixa_deps_of_is_const_fn`][pin] wrapper test (a
2228    /// `const fn deps_of_via_const_fn(c: &Caixa, l: DepList) -> &[Dep]`
2229    /// that forwards through this accessor) — any future accidental
2230    /// downgrade to non-`const` fails the wrapper at caixa-core build
2231    /// time with E0015 (`cannot call non-const method`), strictly
2232    /// stronger than a runtime `assert!` and side-stepping the
2233    /// destructor-in-const restriction the `Caixa` fixture's owning
2234    /// carriers rule out on the direct-`const _: () = assert!(…)`
2235    /// residence. Peer of the sibling per-`Dep` outer-accessor
2236    /// family's parallel `const`-eval-surface pass and of the outer-
2237    /// `Caixa` slice-return accessor family's earlier pass (231a968)
2238    /// — same "one canonical dispatch per axis, `const`-eval posture
2239    /// pinned at the substrate primitive, thin projections at each
2240    /// consumer" discipline extended onto the outer-`Caixa`
2241    /// typed-dispatch read surface on the [`DepList`]-keyed dep-list
2242    /// axis.
2243    ///
2244    /// [DepList]: crate::dep::DepList
2245    /// [pin]: tests::caixa_deps_of_is_const_fn
2246    #[must_use]
2247    pub const fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2248        match list {
2249            crate::dep::DepList::Prod => self.deps(),
2250            crate::dep::DepList::Dev => self.deps_dev(),
2251        }
2252    }
2253
2254    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2255    /// consumer that appends to one of the two dep-list axes keys off
2256    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2257    /// method on the substrate primitive rather than the prior
2258    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2259    /// else { &mut caixa.deps }` inline dispatch + open-coded
2260    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2261    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2262    /// a within-list name collision — the same `list: &'static str`
2263    /// diagnostic shape [`Self::validate_deps`]'s per-list
2264    /// [`crate::render::insert_first_seen`] walk raises on the peer
2265    /// parse-time within-list dedup axis, so a future author reading a
2266    /// `feira add` refusal and a `feira build` refusal reaches for the
2267    /// same corrective surface without switching diagnostic idioms.
2268    ///
2269    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2270    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2271    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2272    /// dispatches on — the compiler-checked exhaustiveness on the
2273    /// enum's `match` arms is the build-time guarantee that no future
2274    /// per-list mutation-site regresses to a bare-`bool`-flag
2275    /// (`is_dev: bool`) inline dispatch that a future third
2276    /// dep-list axis (a `:deps-build` build-only closure once the
2277    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2278    /// CAIXA-SDLC §I) would silently split at every consumer.
2279    ///
2280    /// Same "one typed dispatch on the substrate primitive, thin
2281    /// projections at each consumer" discipline the sibling per-slot
2282    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2283    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2284    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2285    /// the substrate's first typed-mutation dispatch on the top-level
2286    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2287    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2288    /// diagnostic path routed no through-line back to the typed slot,
2289    /// so a future extension of either dep-list axis to a richer author
2290    /// surface (a per-cluster override the operator pins through a
2291    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2292    /// roadmap acknowledges, an M4
2293    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2294    /// admission-webhook that normalized the list at admission time)
2295    /// would have had to be threaded through the `feira add` mutation
2296    /// site in lockstep with every read consumer or one path would
2297    /// silently disagree with the other on which list a given dep lands
2298    /// in. Lifting the resolution rule to a typed method on the
2299    /// substrate primitive means every downstream dep-list-mutating
2300    /// consumer of the top-level manifest reaches for exactly one typed
2301    /// dispatch — the resolver's accept-set migrates as a unit on any
2302    /// future axis addition.
2303    ///
2304    /// # Errors
2305    ///
2306    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2307    /// when another entry in the same list already carries the same
2308    /// `:nome` — the mutation is refused and the caller can surface the
2309    /// typed diagnostic to the author (the `feira add` verb routes the
2310    /// error through `anyhow::Error::from`, which preserves the
2311    /// canonical `#[error(...)]`-templated diagnostic body).
2312    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2313        let target = match list {
2314            crate::dep::DepList::Prod => &mut self.deps,
2315            crate::dep::DepList::Dev => &mut self.deps_dev,
2316        };
2317        if target.iter().any(|d| d.nome() == dep.nome()) {
2318            return Err(DepError::DuplicateNome {
2319                nome: dep.nome().to_string(),
2320                list: list.as_str(),
2321            });
2322        }
2323        target.push(dep);
2324        Ok(())
2325    }
2326
2327    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2328    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2329    /// composite-reference accessor every consumer of the top-level
2330    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2331    /// off — returns the author-declared `:limits` typed composite
2332    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2333    /// backing storage the raw `self.limits.as_ref()` field access
2334    /// borrows from, with `None` naming the "no `:limits` block
2335    /// authored — every per-axis Lunatic-sandbox cap defers to the
2336    /// wasm-engine-default arm named on the per-axis
2337    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2338    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2339    /// docstrings" partition every downstream Servico-M2-overlay
2340    /// emitter treats as "emit nothing" and the sibling
2341    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2342    /// treats as "skip the per-axis
2343    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2344    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2345    ///
2346    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2347    /// composite — the load-bearing container of every Lunatic-shaped
2348    /// per-process wasm32-sandbox cap axis every long-running wasm
2349    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2350    /// Lunatic per-process linear-memory / fuel / wall-clock /
2351    /// millicore cap primitives translated onto pleme-io's typed
2352    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2353    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2354    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2355    /// chart both fan on). Every per-`:limits` axis threads through a
2356    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2357    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2358    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2359    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2360    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2361    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2362    /// consumer that reaches for a limits axis first passes through
2363    /// this outer accessor onto the composite and then dispatches
2364    /// onto the per-axis accessor — the two-level dispatch means
2365    /// every per-`:limits` reader now routes through a typed dispatch
2366    /// on the substrate primitive at both altitudes.
2367    ///
2368    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2369    /// was accessed inline at three production sites — the
2370    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2371    /// `if let Some(l) = &caixa.limits { … }` traversal head
2372    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2373    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2374    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2375    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2376    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2377    /// [`LimitsSpec::validate`] fans onto), the
2378    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2379    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2380    /// head (caixa-core/src/render.rs:18504, which drives the
2381    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2382    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2383    /// block emitter fans on), and the
2384    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2385    /// set enumerator's `self.limits.is_some()` presence probe
2386    /// (caixa-core/src/manifest.rs:1788, which drives the
2387    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2388    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2389    /// gate reads) — three open-coded outer-field accesses that
2390    /// expressed no compile-time link back to the typed slot at the
2391    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2392    /// axis to a richer author surface (a multi-`:limits` list the M4
2393    /// CR materializer resolves per-CR at admission time so a Servico
2394    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2395    /// cluster `:limits-overrides` slot the operator pins so a
2396    /// cluster-specific policy can tighten a caixa-declared cap
2397    /// without re-authoring the `caixa.lisp`, a promotion of the
2398    /// plain `Option<LimitsSpec>` to a richer
2399    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2400    /// resolved dynamic-cap surface lands) would have had to be
2401    /// threaded through all three open-coded copies in lockstep or
2402    /// one consumer would silently disagree with the peers on which
2403    /// limits composite a given Caixa resolves to — the layout gate's
2404    /// per-axis bracket-dispatch seed reading the raw slot while the
2405    /// peer `servico_m2_overlay` emitter read an operator-resolved
2406    /// slot would silently split the build-time sandbox-shape gate
2407    /// from the runtime `ComputeUnit` CR emission gate, a three-
2408    /// consumer split at the layout gate, the M2 overlay emitter, and
2409    /// the declared-slot enumerator far from the source `caixa.lisp`
2410    /// with no field naming the limits-drift root cause. Lifting the
2411    /// resolution rule to a typed method on the substrate primitive
2412    /// means every downstream consumer of the caixa's per-`Caixa`
2413    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2414    /// one typed dispatch — the resolver's accept-set migrates as a
2415    /// unit on any future axis addition.
2416    ///
2417    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2418    /// composite-reference accessor — opens the outer-`Caixa`
2419    /// `Option<&Composite>` composite-reference projection pattern the
2420    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2421    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2422    /// [`crate::aplicacao::Placement`] / `:entrada`
2423    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2424    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2425    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2426    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2427    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2428    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2429    /// altitude — extends that "one typed dispatch on the substrate
2430    /// primitive, thin projections at each consumer" discipline onto
2431    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2432    /// runtime slot family's outer-composite axis. Returns
2433    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2434    /// clone) because every downstream consumer of the limits
2435    /// composite treats it as a read-only per-axis dispatch source —
2436    /// the reference-view is the narrowest borrow that supports every
2437    /// present + roadmapped consumer (per-axis accessor dispatch,
2438    /// `.is_empty()`-gated overlay projection, presence-probe early
2439    /// return on the "author-omitted `:limits` ⇒ engine-default
2440    /// applies" partition) without cloning the composite through
2441    /// every consumer's fast path. The `Option` half of the return-
2442    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2443    /// engine-default applies" partition (not a default composite the
2444    /// downstream must reject on emptiness) — the accessor projects
2445    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2446    /// reference-return unchanged. Named `limits()` to match the
2447    /// storage field's name verbatim and the tatara-lisp author-
2448    /// surface term (`:limits`) the field's own docstring already
2449    /// carries.
2450    #[must_use]
2451    pub const fn limits(&self) -> Option<&LimitsSpec> {
2452        self.limits.as_ref()
2453    }
2454
2455    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2456    /// composite OTP-`gen_server`-shaped callback-table optional-
2457    /// composite-reference accessor every consumer of the top-level
2458    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2459    /// keys off — returns the author-declared `:behavior` typed
2460    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2461    /// the same backing storage the raw `self.behavior.as_ref()` field
2462    /// access borrows from, with `None` naming the "no `:behavior`
2463    /// block authored — every per-callback OTP-shaped hook defers to
2464    /// the wasm-engine's runtime default arm named on the per-axis
2465    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2466    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2467    /// [`BehaviorSpec::on_state_change`] /
2468    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2469    /// partition every downstream Servico-M2-overlay emitter treats as
2470    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2471    /// per-`:behavior` shape gate treats as "skip the per-arm
2472    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2473    /// per-callback on-disk `MissingEntry` existence check".
2474    ///
2475    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2476    /// composite — the load-bearing container of every OTP-shaped
2477    /// per-Servico lifecycle-callback path axis every long-running wasm
2478    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2479    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2480    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2481    /// translated onto pleme-io's typed `:behavior :on-init` /
2482    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2483    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2484    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2485    /// chart both fan on). Every per-`:behavior` axis threads through a
2486    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2487    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2488    /// Every downstream consumer that reaches for a behavior axis
2489    /// first passes through this outer accessor onto the composite
2490    /// and then dispatches onto the per-callback accessor — the
2491    /// two-level dispatch means every per-`:behavior` reader now
2492    /// routes through a typed dispatch on the substrate primitive at
2493    /// both altitudes.
2494    ///
2495    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2496    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2497    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2498    /// keys the "per-version `:state-change` instruction must have a
2499    /// `:on-state-change` callback" precondition off this accessor's
2500    /// composite (the callback-side counterpart to the
2501    /// `:upgrade-from :instructions :state-change :script` refusal at
2502    /// the appup-side). Threading that gate's traversal input through
2503    /// this accessor closes the cross-slot invariant on the substrate
2504    /// primitive, not on the raw field.
2505    ///
2506    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2507    /// composite was accessed inline at four production sites — the
2508    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2509    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2510    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2511    /// `BehaviorError` refusal cascade + the per-callback on-disk
2512    /// [`crate::LayoutError::MissingEntry`] existence check under
2513    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2514    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2515    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2516    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2517    /// drives the `:state-change` ↔ `:on-state-change` precondition
2518    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2519    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2520    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2521    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2522    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2523    /// Servico values-block emitter fans on), and the
2524    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2525    /// set enumerator's `self.behavior.is_some()` presence probe
2526    /// (caixa-core/src/manifest.rs:1919, which drives the
2527    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2528    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2529    /// gate reads) — four open-coded outer-field accesses that
2530    /// expressed no compile-time link back to the typed slot at the
2531    /// [`Caixa`] altitude. A future extension of the `:behavior`
2532    /// outer axis to a richer author surface (a per-callback overlay
2533    /// resolver the operator materializes at admission time so a
2534    /// cluster-specific policy can inject a per-callback tracing
2535    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2536    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2537    /// dynamic}` partition once a runtime-resolved behavior-swap
2538    /// surface lands, the M4 per-callback middleware chain the
2539    /// caixa-operator's per-Servico admission webhook keys off) would
2540    /// have had to be threaded through all four open-coded copies in
2541    /// lockstep or one consumer would silently disagree with the
2542    /// peers on which behavior composite a given Caixa resolves to —
2543    /// the layout gate's per-callback existence-check seed reading
2544    /// the raw slot while the peer `servico_m2_overlay` emitter read
2545    /// an operator-resolved slot would silently split the build-time
2546    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2547    /// gate from the cross-slot `:state-change` composition gate from
2548    /// the M2 declared-slot enumerator, a four-consumer split far
2549    /// from the source `caixa.lisp` with no field naming the
2550    /// behavior-drift root cause. Lifting the resolution rule to a
2551    /// typed method on the substrate primitive means every downstream
2552    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2553    /// composite surface reaches for exactly one typed dispatch — the
2554    /// resolver's accept-set migrates as a unit on any future axis
2555    /// addition.
2556    ///
2557    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2558    /// composite-reference accessor — sibling to the opening
2559    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2560    /// `Option<&Composite>` composite-reference sub-family, extends
2561    /// the "one typed dispatch on the substrate primitive, thin
2562    /// projections at each consumer" discipline onto the second of
2563    /// the three M2 Servico-runtime slots. The remaining
2564    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2565    /// altitude — the M3 mesh-slot family (`:politicas`,
2566    /// `:placement`, `:entrada` — already closed on the inner
2567    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2568    /// d32111c) — remain the future sibling lifts on the outer
2569    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2570    /// the owning composite by copy or clone) because every
2571    /// downstream consumer of the behavior composite treats it as a
2572    /// read-only per-callback dispatch source — the reference-view is
2573    /// the narrowest borrow that supports every present + roadmapped
2574    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2575    /// overlay projection, presence-probe early return on the
2576    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2577    /// partition, cross-slot `:state-change` composition input)
2578    /// without cloning the composite through every consumer's fast
2579    /// path. The `Option` half of the return-type preserves the
2580    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2581    /// applies" partition (not a default composite the downstream
2582    /// must reject on emptiness) — the accessor projects the raw
2583    /// `Option<BehaviorSpec>` slot's presence bit through the
2584    /// reference-return unchanged. Named `behavior()` to match the
2585    /// storage field's name verbatim and the tatara-lisp author-
2586    /// surface term (`:behavior`) the field's own docstring already
2587    /// carries.
2588    #[must_use]
2589    pub const fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2590        self.behavior.as_ref()
2591    }
2592
2593    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2594    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2595    /// reference accessor every consumer of the top-level manifest's
2596    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2597    /// reader keys off — returns the author-declared `:politicas` typed
2598    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2599    /// same backing storage the raw `self.politicas.as_ref()` field
2600    /// access borrows from, with `None` naming the "no `:politicas`
2601    /// block authored — every per-axis mesh-policy scalar defers to the
2602    /// cluster-default arm named on the per-axis
2603    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2604    /// [`crate::aplicacao::MeshPolicy::retries`] /
2605    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2606    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2607    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2608    /// docstrings" partition every downstream caixa-mesh /
2609    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2610    /// "emit no per-`:politicas` overlay" and the sibling
2611    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2612    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2613    /// arm.
2614    ///
2615    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2616    /// Aplicacao typed composite — the load-bearing container of every
2617    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2618    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2619    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2620    /// composite; §V — the "no infinite blocking" per-call deadline +
2621    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2622    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2623    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2624    /// threads through a lifted per-slot accessor on the
2625    /// [`crate::aplicacao::MeshPolicy`] type: the
2626    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2627    /// mTLS-enforcement toggle, the
2628    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2629    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2630    /// (7073d0f) Gateway-API per-call deadline, the
2631    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2632    /// Envoy-outlier-detection composite. Every downstream consumer
2633    /// that reaches for a mesh-policy axis first passes through this
2634    /// outer accessor onto the composite and then dispatches onto the
2635    /// per-axis accessor — the two-level dispatch means every per-
2636    /// `:politicas` reader now routes through a typed dispatch on the
2637    /// substrate primitive at both altitudes.
2638    ///
2639    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2640    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2641    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2642    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2643    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2644    /// composite whether or not the author declared the outer slot.
2645    /// The outer accessor preserves the "author-omitted vs authored-
2646    /// empty" partition the inner accessor's `is_empty()`-gated
2647    /// renderer overlay collapses — routing the presence bit through
2648    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2649    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2650    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2651    ///
2652    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2653    /// composite was accessed inline at two production sites — the
2654    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2655    /// `self.politicas.clone().unwrap_or_default()` traversal head
2656    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2657    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2658    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2659    /// then observes), and the [`Self::declared_mesh_slots`] M3
2660    /// declared-slot-set enumerator's `self.politicas.is_some()`
2661    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2662    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2663    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2664    /// coherence gate reads) — two open-coded outer-field accesses
2665    /// that expressed no compile-time link back to the typed slot at
2666    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2667    /// outer axis to a richer author surface (a per-cluster
2668    /// `:politicas-overrides` slot the operator materializes at
2669    /// admission time so a cluster-specific policy can tighten the
2670    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2671    /// promotion of the plain `Option<MeshPolicy>` to a richer
2672    /// `{static, dynamic}` partition once the M4 per-edge
2673    /// contrato-scoped policy-override surface lands, the M5 traffic-
2674    /// shaping composition the caixa-operator's per-Aplicacao mesh
2675    /// admission webhook keys off) would have had to be threaded
2676    /// through both open-coded copies in lockstep or the Aplicacao-
2677    /// composition seed's default-fold arm would silently disagree
2678    /// with the M3 declared-slot enumerator on which policy composite
2679    /// a given Caixa resolves to — the seed reading an operator-
2680    /// resolved slot while the enumerator's presence probe read the
2681    /// raw slot would silently split the build-time mesh-artifact
2682    /// emission gate from the M3 declared-slot enumerator's kind-
2683    /// coherence gate, a two-consumer split far from the source
2684    /// `caixa.lisp` with no field naming the policy-drift root cause.
2685    /// Lifting the resolution rule to a typed method on the substrate
2686    /// primitive means every downstream consumer of the caixa's per-
2687    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2688    /// reaches for exactly one typed dispatch — the resolver's
2689    /// accept-set migrates as a unit on any future axis addition.
2690    ///
2691    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2692    /// composite-reference accessor — sibling to the opening
2693    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2694    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2695    /// reference sub-family, extends the "one typed dispatch on the
2696    /// substrate primitive, thin projections at each consumer"
2697    /// discipline onto the first of the three M3 mesh-slot axes.
2698    /// Peer of the closed inner mesh-slot outer-composite family the
2699    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2700    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2701    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2702    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2703    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2704    /// mesh-slot arm of the composite-reference family the remaining
2705    /// two axes (`:placement`, `:entrada`) fold onto in future
2706    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2707    /// composite by copy or clone) because every downstream consumer
2708    /// of the mesh-policy composite treats it as a read-only per-axis
2709    /// dispatch source — the reference-view is the narrowest borrow
2710    /// that supports every present + roadmapped consumer (per-axis
2711    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2712    /// presence-probe early return on the "author-omitted `:politicas`
2713    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2714    /// seed's default-fold arm) without cloning the composite through
2715    /// every consumer's fast path. The `Option` half of the return-
2716    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2717    /// cluster-default applies" partition (not a default composite
2718    /// the downstream must reject on emptiness) — the accessor
2719    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2720    /// through the reference-return unchanged. Named `politicas()` to
2721    /// match the storage field's name verbatim and the tatara-lisp
2722    /// author-surface term (`:politicas`) the field's own docstring
2723    /// already carries.
2724    #[must_use]
2725    pub const fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2726        self.politicas.as_ref()
2727    }
2728
2729    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2730    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2731    /// reference accessor every consumer of the top-level manifest's
2732    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2733    /// reader keys off — returns the author-declared `:placement` typed
2734    /// composite verbatim as an `Option<&Placement>` reference over the
2735    /// same backing storage the raw `self.placement.as_ref()` field
2736    /// access borrows from, with `None` naming the "no `:placement`
2737    /// block authored — every per-axis placement scalar defers to the
2738    /// cluster-default arm named on the per-axis
2739    /// [`crate::aplicacao::Placement::estrategia`] /
2740    /// [`crate::aplicacao::Placement::clusters`] /
2741    /// [`crate::aplicacao::Placement::affinity`] /
2742    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2743    /// docstrings" partition every downstream caixa-mesh /
2744    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2745    /// "emit no per-`:placement` overlay" and the sibling
2746    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2747    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2748    ///
2749    /// The outer `:placement` slot carries the M3 mesh-slot per-
2750    /// Aplicacao typed distribution composite — the load-bearing
2751    /// container of every where-does-this-Aplicacao-run axis every
2752    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2753    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2754    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2755    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2756    /// Aplicacao's typed distribution composite; §V CSE invariants —
2757    /// "distribution is a first-class typed composite, not a runtime
2758    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2759    /// typed inter-Servico contrato-edge overlay the per-cluster
2760    /// mesh renderer keys off). Every per-`:placement` axis threads
2761    /// through a lifted per-slot accessor on the
2762    /// [`crate::aplicacao::Placement`] type: the
2763    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2764    /// MESH-COMPOSITION distribution-strategy scalar, the
2765    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2766    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2767    /// M3-Adaptive-compression-hint optional-scalar, and the
2768    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2769    /// sharding extractor-expression optional-scalar. Every downstream
2770    /// consumer that reaches for a placement axis first passes through
2771    /// this outer accessor onto the composite and then dispatches onto
2772    /// the per-axis accessor — the two-level dispatch means every per-
2773    /// `:placement` reader now routes through a typed dispatch on the
2774    /// substrate primitive at both altitudes.
2775    ///
2776    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2777    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2778    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2779    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2780    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2781    /// whether or not the author declared the outer slot. The outer
2782    /// accessor preserves the "author-omitted vs authored-empty" partition
2783    /// the inner accessor collapses at the cluster-default fold —
2784    /// routing the presence bit through this accessor keeps the
2785    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2786    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2787    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2788    /// dispatch.
2789    ///
2790    /// Prior to this lift the `.placement` `Option<Placement>`
2791    /// composite was accessed inline at two production sites — the
2792    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2793    /// `self.placement.clone().unwrap_or_default()` traversal head
2794    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2795    /// the [`crate::aplicacao::Placement::default`] cluster-default
2796    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2797    /// then observes), and the [`Self::declared_mesh_slots`] M3
2798    /// declared-slot-set enumerator's `self.placement.is_some()`
2799    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2800    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2801    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2802    /// coherence gate reads) — two open-coded outer-field accesses
2803    /// that expressed no compile-time link back to the typed slot at
2804    /// the [`Caixa`] altitude. A future extension of the `:placement`
2805    /// outer axis to a richer author surface (a per-cluster
2806    /// `:placement-overrides` slot the operator materializes at
2807    /// admission time so a cluster-specific placement can tighten the
2808    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2809    /// per-tenant placement-alias table the M4
2810    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2811    /// per-CR at admission time, a promotion of the plain
2812    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2813    /// once Orleans-style virtual-actor dynamic placement comes into
2814    /// typed scope) would have had to be threaded through both open-
2815    /// coded copies in lockstep or the Aplicacao-composition seed's
2816    /// default-fold arm would silently disagree with the M3 declared-
2817    /// slot enumerator on which distribution composite a given Caixa
2818    /// resolves to — the seed reading an operator-resolved slot while
2819    /// the enumerator's presence probe read the raw slot would
2820    /// silently split the build-time distribution-artifact emission
2821    /// gate from the M3 declared-slot enumerator's kind-coherence
2822    /// gate, a two-consumer split far from the source `caixa.lisp`
2823    /// with no field naming the distribution-drift root cause.
2824    /// Lifting the resolution rule to a typed method on the substrate
2825    /// primitive means every downstream consumer of the caixa's per-
2826    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2827    /// reaches for exactly one typed dispatch — the resolver's
2828    /// accept-set migrates as a unit on any future axis addition.
2829    ///
2830    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2831    /// composite-reference accessor — sibling to the opening
2832    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2833    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2834    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2835    /// composite-reference sub-family, folds on the "one typed
2836    /// dispatch on the substrate primitive, thin projections at each
2837    /// consumer" discipline extended onto the second of the three M3
2838    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2839    /// composite family the sibling
2840    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2841    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2842    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2843    /// accessor pins already close on the inner
2844    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2845    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2846    /// [`Self::politicas`] opened, extending the discipline onto the
2847    /// second of the three M3 mesh-slot axes. The remaining M3
2848    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2849    /// discipline in the final sibling lift, closing the outer top-
2850    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2851    /// Returns `Option<&Placement>` (not the owning composite by copy
2852    /// or clone) because every downstream consumer of the placement
2853    /// composite treats it as a read-only per-axis dispatch source —
2854    /// the reference-view is the narrowest borrow that supports every
2855    /// present + roadmapped consumer (per-axis accessor dispatch,
2856    /// serde composite-serialization on the programs.yaml overlay,
2857    /// presence-probe early return on the "author-omitted `:placement`
2858    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2859    /// seed's default-fold arm) without cloning the composite through
2860    /// every consumer's fast path. The `Option` half of the return-
2861    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2862    /// cluster-default applies" partition (not a default composite
2863    /// the downstream must reject on emptiness) — the accessor
2864    /// projects the raw `Option<Placement>` slot's presence bit
2865    /// through the reference-return unchanged. Named `placement()` to
2866    /// match the storage field's name verbatim and the tatara-lisp
2867    /// author-surface term (`:placement`) the field's own docstring
2868    /// already carries.
2869    #[must_use]
2870    pub const fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2871        self.placement.as_ref()
2872    }
2873
2874    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2875    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2876    /// composite-reference accessor every consumer of the top-level
2877    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2878    /// composite reader keys off — returns the author-declared
2879    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2880    /// reference over the same backing storage the raw
2881    /// `self.entrada.as_ref()` field access borrows from, with `None`
2882    /// naming the "no `:entrada` block authored — this Aplicacao is
2883    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2884    /// partition every downstream caixa-mesh Gateway-API artifact
2885    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2886    /// backend for this Aplicacao" and the sibling
2887    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2888    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2889    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2890    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2891    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2892    /// the same `Option<&Entrada>` presence bit unchanged).
2893    ///
2894    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2895    /// Aplicacao typed external-gateway composite — the load-bearing
2896    /// container of every how-does-the-outside-world-reach-this-
2897    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2898    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2899    /// external-entry composite; §V CSE invariants — "the external
2900    /// gateway is a first-class typed composite, not a per-Servico
2901    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2902    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2903    /// API renderer keys off). Every per-`:entrada` axis threads
2904    /// through a lifted per-slot accessor on the
2905    /// [`crate::aplicacao::Entrada`] type: the
2906    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2907    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2908    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2909    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2910    /// backend `trigger.service.port` scalar, and the
2911    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2912    /// resolver every HTTPRoute-aware renderer consumes. Every
2913    /// downstream consumer that reaches for an entry axis first passes
2914    /// through this outer accessor onto the composite and then
2915    /// dispatches onto the per-axis accessor — the two-level dispatch
2916    /// means every per-`:entrada` reader now routes through a typed
2917    /// dispatch on the substrate primitive at both altitudes.
2918    ///
2919    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2920    /// seed: the Aplicacao-view builder forwards the outer `Option`
2921    /// arm verbatim (no default fold — `:entrada` is inherently
2922    /// optional; a cluster-internal Aplicacao has no external gateway
2923    /// at all, not "an external gateway that defaults to nothing"), so
2924    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2925    /// `Option<&Entrada>`-return accessor observes the same presence
2926    /// bit whether or not the author declared the outer slot. Routing
2927    /// the presence bit through this accessor keeps the
2928    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2929    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2930    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2931    /// hostname/backend/path emission dispatch.
2932    ///
2933    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2934    /// was accessed inline at two production sites — the
2935    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2936    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2937    /// which drives the forward onto the peer inner
2938    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2939    /// Gateway-API fan-out then observes), and the
2940    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2941    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2942    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2943    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2944    /// kind-coherence gate reads) — two open-coded outer-field
2945    /// accesses that expressed no compile-time link back to the typed
2946    /// slot at the [`Caixa`] altitude. A future extension of the
2947    /// `:entrada` outer axis to a richer author surface (a per-cluster
2948    /// `:entrada-overrides` slot the operator materializes at admission
2949    /// time so a cluster-specific hostname can pin the caixa-declared
2950    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2951    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2952    /// CR materializer resolves per-CR at admission time, a promotion
2953    /// of the plain `Option<Entrada>` to a richer
2954    /// `{public, private, internal}` partition once Cilium-identity-
2955    /// scoped internal gateways come into typed scope) would have had
2956    /// to be threaded through both open-coded copies in lockstep or the
2957    /// Aplicacao-composition seed's forward arm would silently
2958    /// disagree with the M3 declared-slot enumerator on which external-
2959    /// gateway composite a given Caixa resolves to — the seed reading
2960    /// an operator-resolved slot while the enumerator's presence probe
2961    /// read the raw slot would silently split the build-time gateway-
2962    /// artifact emission gate from the M3 declared-slot enumerator's
2963    /// kind-coherence gate, a two-consumer split far from the source
2964    /// `caixa.lisp` with no field naming the entry-drift root cause.
2965    /// Lifting the resolution rule to a typed method on the substrate
2966    /// primitive means every downstream consumer of the caixa's per-
2967    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2968    /// surface reaches for exactly one typed dispatch — the resolver's
2969    /// accept-set migrates as a unit on any future axis addition.
2970    ///
2971    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2972    /// return composite-reference accessor — closes the outer-`Caixa`
2973    /// `Option<&Composite>` composite-reference sub-family opened by
2974    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2975    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2976    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2977    /// folds on the "one typed dispatch on the substrate primitive,
2978    /// thin projections at each consumer" discipline extended onto the
2979    /// third and final M3 mesh-slot axis. Peer of the closed inner
2980    /// mesh-slot outer-composite family the sibling
2981    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2982    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2983    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2984    /// accessor pins already close on the inner
2985    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2986    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2987    /// altitudes of the outer-composite reference-return discipline
2988    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2989    /// slot presence) now carry the full five-arm accept-set behind a
2990    /// typed dispatch on the substrate primitive. Returns
2991    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2992    /// because every downstream consumer of the entrada composite
2993    /// treats it as a read-only per-axis dispatch source — the
2994    /// reference-view is the narrowest borrow that supports every
2995    /// present + roadmapped consumer (per-axis accessor dispatch,
2996    /// serde composite-serialization on the programs.yaml overlay,
2997    /// presence-probe early return on the "author-omitted `:entrada`
2998    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2999    /// seed's forward arm) without cloning the composite through every
3000    /// consumer's fast path. The `Option` half of the return-type
3001    /// preserves the load-bearing "author-omitted `:entrada` ⇒
3002    /// cluster-internal Aplicacao" partition (not a default composite
3003    /// the downstream must reject on emptiness — a cluster-internal
3004    /// Aplicacao has no external gateway at all, not "a default gateway
3005    /// that emits nothing"); the accessor projects the raw
3006    /// `Option<Entrada>` slot's presence bit through the reference-
3007    /// return unchanged. Named `entrada()` to match the storage field's
3008    /// name verbatim and the tatara-lisp author-surface term
3009    /// (`:entrada`) the field's own docstring already carries.
3010    #[must_use]
3011    pub const fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
3012        self.entrada.as_ref()
3013    }
3014
3015    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
3016    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
3017    /// an `Option<&CiRun>`, borrowed from the typed slot's own
3018    /// `Option<CiRun>` storage. `None` when the slot is absent (every
3019    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
3020    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
3021    /// not silently accepted).
3022    ///
3023    /// Named `ci()` to match the storage field's name and the
3024    /// tatara-lisp author surface (`:ci`); mirrors the sibling
3025    /// `Option<&Composite>` accessors on this same `Caixa` altitude
3026    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
3027    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
3028    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
3029    /// at every consumer.
3030    #[must_use]
3031    pub const fn ci(&self) -> Option<&canteiro_types::CiRun> {
3032        self.ci.as_ref()
3033    }
3034
3035    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
3036    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
3037    /// accessor every consumer of the top-level manifest's per-Supervisor
3038    /// restart-strategy axis keys off — returns the author-declared
3039    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
3040    /// `Copy`-projected from the typed slot's own
3041    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
3042    /// (`:estrategia` is a flat-spread supervisor-only slot every
3043    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
3044    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3045    /// still omit to defer to [`RestartStrategy::default`] —
3046    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
3047    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
3048    /// [`SupervisorSpec::default`]-inherited strategy without any silent
3049    /// promotion to a fresh explicit variant at the accessor boundary).
3050    ///
3051    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
3052    /// restart-strategy discriminant every substrate-side per-Supervisor
3053    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
3054    /// closed-set `one_for_one | one_for_all | rest_for_one |
3055    /// simple_one_for_one` algebra translated onto pleme-io's typed
3056    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
3057    /// slot algebra the operator's hierarchical reconciliation scheduler
3058    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
3059    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
3060    /// supervisor slots are flat on Caixa (vs nested under a
3061    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
3062    /// level of nesting"), so the accessor's altitude is the outer
3063    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
3064    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
3065    /// (eafb619) accessor keys off. The two typed axes — the outer
3066    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
3067    /// (author-omitted arm carried as `None`) and the inner post-
3068    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
3069    /// (`Option` collapsed through the [`Self::supervisor_view`]
3070    /// `unwrap_or_default()` fold) — now share one accessor discipline for
3071    /// the shared substrate concept "the author-declared OTP-shaped
3072    /// sibling-restart-strategy variant that partitions the downstream
3073    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
3074    /// `None` arm is the pre-composition presence bit every declared-slot
3075    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
3076    /// inner-altitude non-`Option` `RestartStrategy` is the post-
3077    /// composition partition-dispatch input every strategy-arm consumer
3078    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
3079    /// Supervisor sibling-restart branch, the future M4
3080    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
3081    /// webhook) fans on.
3082    ///
3083    /// Prior to this lift the `.estrategia` field was accessed inline at
3084    /// two production sites in `caixa-core/src/manifest.rs` — the
3085    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
3086    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
3087    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3088    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
3089    /// `SupervisorSpec` construction site at `estrategia:
3090    /// self.estrategia.unwrap_or_default()` (which composes the flat-
3091    /// spread outer author-surface `Option<RestartStrategy>` onto the
3092    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
3093    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
3094    /// coded field-accesses that expressed no compile-time link back to
3095    /// the typed slot. A future extension of the outer `:estrategia` axis
3096    /// to a richer author surface (a per-cluster strategy override the
3097    /// operator pins through a future `:estrategia-overrides` overlay the
3098    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
3099    /// a per-tenant strategy-alias table the M4 CR materializer resolves
3100    /// per-CR, a per-Supervisor dynamic strategy derivation the future
3101    /// adaptive-supervision engine computes from child-failure-history
3102    /// topology, a per-child-cohort strategy split the future
3103    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
3104    /// absorption roadmap acknowledges, a promotion of the plain
3105    /// `Option<RestartStrategy>` to a richer
3106    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
3107    /// operator-resolved overlay lands) would have had to be threaded
3108    /// through both open-coded copies in lockstep or the enumerator's
3109    /// presence probe and the composition site's `unwrap_or_default()`
3110    /// fold would silently disagree on which strategy a given [`Caixa`]
3111    /// resolves to (an author's `:estrategia OneForAll` would satisfy
3112    /// the enumerator's presence probe while the composition site
3113    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3114    /// the resolution rule to a typed method on the substrate primitive
3115    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3116    /// altitude sibling-restart-strategy surface reaches for exactly one
3117    /// typed dispatch — the resolver's accept-set migrates as a unit on
3118    /// any future axis addition.
3119    ///
3120    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3121    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3122    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3123    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3124    /// `Option<u32>` and (through the future duration-newtype landing)
3125    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3126    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3127    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3128    /// the post-composition [`SupervisorSpec`] altitude — same "one
3129    /// typed dispatch on the substrate primitive, thin projections at
3130    /// each consumer" discipline extended onto the pre-composition outer
3131    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3132    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3133    /// `Option<&Composite>` composite-reference family the sibling
3134    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3135    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3136    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3137    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3138    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3139    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3140    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3141    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3142    /// pins on the inner-altitude per-`:placement` composite. Named
3143    /// `estrategia()` to match the storage field's name and the
3144    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3145    /// / per-[`crate::aplicacao::Placement`] peer
3146    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3147    /// verbatim; the accessor's identity name maps onto the canonical
3148    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3149    /// docstring already carries.
3150    #[must_use]
3151    pub const fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3152        self.estrategia
3153    }
3154
3155    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3156    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3157    /// scalar accessor every consumer of the top-level manifest's per-
3158    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3159    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3160    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3161    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3162    /// accessor returns by value; no borrow of `&self` past the call).
3163    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3164    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3165    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3166    /// still omit to defer to the [`Self::supervisor_view`]
3167    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3168    ///
3169    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3170    /// `MaxIntensity` restart-budget count that pairs with the sibling
3171    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3172    /// restart-intensity ratio the supervisor trips its own escalation on
3173    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3174    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3175    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3176    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3177    /// the outer top-level `Caixa` (per the field-shape docstring at
3178    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3179    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3180    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3181    /// composed [`SupervisorSpec`] altitude the sibling
3182    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3183    /// off. The two typed axes — the outer author-surface `Option<u32>`
3184    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3185    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3186    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3187    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3188    /// shared substrate concept "the author-declared OTP-shaped
3189    /// restart-budget count every downstream per-Supervisor consumer's
3190    /// restart-intensity budget-vs-count comparator fans on".
3191    ///
3192    /// Prior to this lift the `.max_restarts` field was accessed inline
3193    /// at two production sites in `caixa-core/src/manifest.rs` — the
3194    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3195    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3196    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3197    /// kind-coherence gate's per-slot label push) and the
3198    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3199    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3200    /// flat-spread outer author-surface `Option<u32>` onto the inner
3201    /// post-composition [`SupervisorSpec`] `u32` field the
3202    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3203    /// coded field-accesses that expressed no compile-time link back to
3204    /// the typed slot. A future extension of the outer `:max-restarts`
3205    /// axis to a richer author surface (a per-cluster restart-budget
3206    /// override the operator pins through a future `:max-restarts-overrides`
3207    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3208    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3209    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3210    /// budget derivation the future adaptive-supervision engine computes
3211    /// from child-failure-history topology, a promotion of the plain
3212    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3213    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3214    /// per-child-cohort roadmap lands) would have had to be threaded
3215    /// through both open-coded copies in lockstep or the enumerator's
3216    /// presence probe and the composition site's `unwrap_or(5)` fold
3217    /// would silently disagree on which restart-budget a given [`Caixa`]
3218    /// resolves to (an author's `:max-restarts 10` would satisfy the
3219    /// enumerator's presence probe while the composition site silently
3220    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3221    /// resolution rule to a typed method on the substrate primitive means
3222    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3223    /// restart-budget-count surface reaches for exactly one typed dispatch
3224    /// — the resolver's accept-set migrates as a unit on any future axis
3225    /// addition.
3226    ///
3227    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3228    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3229    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3230    /// projection pattern the sibling per-`Caixa`
3231    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3232    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3233    /// Peer of the inner-altitude
3234    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3235    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3236    /// typed dispatch on the substrate primitive, thin projections at
3237    /// each consumer" discipline extended onto the pre-composition outer
3238    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3239    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3240    /// the storage field's name and the per-[`SupervisorSpec`] peer
3241    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3242    /// discipline verbatim; the accessor's identity maps onto the
3243    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3244    /// field's docstring already carries.
3245    #[must_use]
3246    pub const fn max_restarts(&self) -> Option<u32> {
3247        self.max_restarts
3248    }
3249
3250    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3251    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3252    /// denominator raw-duration-string scalar accessor every consumer of
3253    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3254    /// window axis keys off — returns the author-declared `:restart-window`
3255    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3256    /// from the typed slot's own `Option<String>` storage. `None` when
3257    /// the slot is absent (the canonical "never reset — every restart
3258    /// across the supervisor's lifetime counts against the sibling
3259    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3260    /// `defcaixa` carries by `#[serde(default)]` and every
3261    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3262    /// [`Self::supervisor_view`] `restart_window: None` composition
3263    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3264    /// swallow `.and_then(|s| … .ok())` fold).
3265    ///
3266    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3267    /// shaped `Period` sliding-observation-interval duration string that
3268    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3269    /// budget count to form the `MaxIntensity / Period` restart-intensity
3270    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3271    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3272    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3273    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3274    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3275    /// holds an `Option<Duration>` routed through the shared
3276    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3277    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3278    /// authoring surface) while the inner altitude's
3279    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3280    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3281    /// is closed by the sibling [`Self::validate_restart_window`] gate
3282    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3283    /// the offending value; the view-construction path
3284    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3285    /// `None` to keep the view best-effort.
3286    ///
3287    /// Prior to this lift the `.restart_window` field was accessed inline
3288    /// at three production sites in `caixa-core/src/manifest.rs` — the
3289    /// [`Self::declared_supervisor_slots`]
3290    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3291    /// `if self.restart_window.is_some()` (which drives the
3292    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3293    /// coherence gate's per-slot label push), the
3294    /// [`Self::validate_restart_window`] `let Some(s) =
3295    /// self.restart_window.as_deref()` empty-and-shape gate binding
3296    /// (which folds the raw string through the shared
3297    /// [`crate::supervisor::duration_codec::parse`] to surface
3298    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3299    /// value), and the [`Self::supervisor_view`] `self.restart_window
3300    /// .as_deref().and_then(…)` view-construction fold (which composes
3301    /// the flat-spread outer author-surface `Option<String>` onto the
3302    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3303    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3304    /// three open-coded field-accesses that expressed no compile-time
3305    /// link back to the typed slot. A future extension of the outer
3306    /// `:restart-window` axis to a richer author surface (a per-cluster
3307    /// window override, a per-tenant window-alias table, a per-Supervisor
3308    /// dynamic window derivation the future adaptive-supervision engine
3309    /// computes from child-failure-history topology, a promotion of the
3310    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3311    /// once the future author-surface parser lands at the [`Caixa`]
3312    /// altitude and the raw-string form is retired) would have had to be
3313    /// threaded through every open-coded copy in lockstep or the three
3314    /// consumers would silently disagree on which raw string a given
3315    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3316    /// method on the substrate primitive means every downstream consumer
3317    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3318    /// string surface reaches for exactly one typed dispatch — the
3319    /// resolver's accept-set migrates as a unit on any future axis
3320    /// addition.
3321    ///
3322    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3323    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3324    /// spread projection pattern the sibling per-`Caixa`
3325    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3326    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3327    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3328    /// string arm (the outer altitude's raw-string form; the inner
3329    /// altitude's parsed [`Duration`] form is the peer
3330    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3331    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3332    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3333    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3334    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3335    /// sub-family already carries — same "one typed dispatch on the
3336    /// substrate primitive, thin projections at each consumer"
3337    /// discipline extended onto the M2 supervisor-tree flat-spread
3338    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3339    /// to match the storage field's name and the per-[`SupervisorSpec`]
3340    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3341    /// method-name discipline verbatim; the accessor's identity maps
3342    /// onto the canonical OTP-shape supervision vocabulary the
3343    /// `:restart-window` field's docstring already carries.
3344    #[must_use]
3345    pub const fn restart_window(&self) -> Option<&str> {
3346        match &self.restart_window {
3347            Some(s) => Some(s.as_str()),
3348            None => None,
3349        }
3350    }
3351
3352    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3353    /// outer-composite OTP-appup-shaped per-prior-version migration-
3354    /// entry-list slice accessor every consumer of the top-level
3355    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3356    /// slice-view keys off — returns the author-declared `:upgrade-from`
3357    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3358    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3359    /// the raw `self.upgrade_from.as_slice()` field access borrows
3360    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3361    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3362    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3363    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3364    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3365    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3366    /// degenerates to an empty slice on that arm without any silent
3367    /// `None` collapse).
3368    ///
3369    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3370    /// migration block — the load-bearing container of every per-
3371    /// prior-`:versao` migration-instruction list the wasm-operator
3372    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3373    /// `.appup` per-prior-version `LoadModule | StateChange |
3374    /// SoftPurge | Purge | Restart` instruction algebra translated
3375    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3376    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3377    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3378    /// threads through a lifted per-entry accessor on the
3379    /// [`UpgradeFromEntry`] type: the
3380    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3381    /// version scalar accessor and the
3382    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3383    /// return per-entry instruction-list accessor (0137e5a). Every
3384    /// downstream consumer of the hot-upgrade path first passes
3385    /// through this outer accessor onto the slice and then dispatches
3386    /// per-entry through the inner accessors — the two-level dispatch
3387    /// means every per-`:upgrade-from` reader now routes through a
3388    /// typed dispatch on the substrate primitive at both altitudes.
3389    ///
3390    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3391    /// slot was accessed inline at production sites across three
3392    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3393    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3394    /// (caixa-core/src/manifest.rs, which drives the
3395    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3396    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3397    /// gate reads), the [`crate::StandardLayout::verify`] per-
3398    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3399    /// layout.rs, which fans onto the
3400    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3401    /// cross-entry duplicate gate, the
3402    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3403    /// SemVer-precedence cross-slot gate, the
3404    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3405    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3406    /// gate, and the per-instruction script-path existence-probe walk
3407    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3408    /// resolve every declared migration script against the layout
3409    /// root), and the [`crate::render::servico_m2_overlay`] per-
3410    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3411    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3412    /// projection (caixa-core/src/render.rs, which drives the
3413    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3414    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3415    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3416    /// A future extension of the outer `:upgrade-from` axis (a per-
3417    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3418    /// resolves at admission time so a cluster-specific migration
3419    /// policy can tighten a caixa-declared step without re-authoring
3420    /// the `caixa.lisp`, promotion of the plain
3421    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3422    /// partition once runtime-resolved hot-upgrade instructions land,
3423    /// per-entry priority annotation once multi-strategy fan-out
3424    /// lands) would have had to be threaded through all six open-
3425    /// coded copies in lockstep or one consumer would silently
3426    /// disagree with the peers on which upgrade slice a given Caixa
3427    /// resolves to — a six-consumer split at the enumerator, the
3428    /// three-stage validate pass, the script-path probe walk, and the
3429    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3430    /// field naming the upgrade-drift root cause. Lifting the
3431    /// resolution rule to a typed method on the substrate primitive
3432    /// means every downstream consumer of the caixa's per-`Caixa`
3433    /// OTP-appup outer-slice surface reaches for exactly one typed
3434    /// dispatch — the resolver's accept-set migrates as a unit on any
3435    /// future axis addition.
3436    ///
3437    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3438    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3439    /// outer-`Caixa` `&[Composite]` composite-slice projection
3440    /// pattern the sibling `:children`
3441    /// [`crate::supervisor::ChildSpec`] / `:membros`
3442    /// [`crate::aplicacao::Membro`] / `:contratos`
3443    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3444    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3445    /// `Option<&Composite>` composite-reference family the sibling
3446    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3447    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3448    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3449    /// `Option<&Composite>` altitude, extended here to the outer-
3450    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3451    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3452    /// (0137e5a) — same "one typed dispatch on the substrate
3453    /// primitive, thin projections at each consumer" discipline
3454    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3455    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3456    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3457    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3458    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3459    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3460    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3461    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3462    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3463    /// slice" projection pattern onto the sibling M2 typed-composite-
3464    /// element axis (`UpgradeFromEntry` composite, matching the
3465    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3466    /// different altitude).
3467    ///
3468    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3469    /// because every downstream consumer of the hot-upgrade list
3470    /// treats it as a read-only sequence — the slice-view is the
3471    /// narrowest borrow that supports every present + roadmapped
3472    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3473    /// serialization through
3474    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3475    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3476    /// the typed view reaches for (the storage-side `Vec` remains
3477    /// reachable through the `pub upgrade_from` field for the
3478    /// mutation-carrying serde round-trip and per-test fixture-
3479    /// mutation paths). Named `upgrade_from()` to match the storage
3480    /// field's `snake_case` name; the kebab-case author-surface tag
3481    /// `:upgrade-from` is the same axis after tatara-lisp's
3482    /// kebab↔snake fold and the accessor's identity maps onto the
3483    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3484    /// already carries.
3485    #[must_use]
3486    pub const fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3487        self.upgrade_from.as_slice()
3488    }
3489
3490    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3491    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3492    /// slice accessor every consumer of the top-level manifest's per-
3493    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3494    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3495    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3496    /// the same backing buffer the raw `self.children.as_slice()` field
3497    /// access borrows from. Empty-slice-carrying (the "no static children
3498    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3499    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3500    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3501    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3502    /// on those arms without any silent `None` collapse).
3503    ///
3504    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3505    /// static-child list — the load-bearing container of every per-
3506    /// child `{caixa, versao, restart}` triple the wasm-operator's
3507    /// hierarchical reconciler dispatches on at supervisor-tree
3508    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3509    /// static-child list translated onto pleme-io's typed
3510    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3511    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3512    /// dispatch fans on). Every per-child axis threads through a lifted
3513    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3514    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3515    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3516    /// version-requirement scalar accessor, and the
3517    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3518    /// per-child post-exit restart-decision-policy discriminant
3519    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3520    /// tree path first passes through this outer accessor onto the
3521    /// slice and then dispatches per-child through the inner accessors
3522    /// — the two-level dispatch means every per-`:children` reader now
3523    /// routes through a typed dispatch on the substrate primitive at
3524    /// both altitudes.
3525    ///
3526    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3527    /// accessed inline at three production sites across two files —
3528    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3529    /// declared-slot enumerator's `!self.children.is_empty()` presence
3530    /// probe (caixa-core/src/manifest.rs, which drives the
3531    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3532    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3533    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3534    /// per-supervisor typed-view composer's `self.children.clone()`
3535    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3536    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3537    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3538    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3539    /// `:children :caixa` self-parent refusal probe's
3540    /// `&caixa.children`-borrowed
3541    /// [`crate::supervisor::validate_no_self_supervision`] input
3542    /// (caixa-core/src/layout.rs, which pins the "no child names the
3543    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3544    /// extension of the outer `:children` axis (a per-cluster
3545    /// `:children-overrides` overlay the wasm-engine operator resolves
3546    /// at admission time so a cluster-specific child-set can tighten
3547    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3548    /// promotion of the plain `Vec<ChildSpec>` to a richer
3549    /// `{static, dynamic}` partition once Erlang/OTP's
3550    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3551    /// axis, per-child priority annotation once multi-strategy fan-out
3552    /// lands) would have had to be threaded through all three open-
3553    /// coded copies in lockstep or one consumer would silently
3554    /// disagree with the peers on which child slice a given Caixa
3555    /// resolves to — the enumerator's presence probe reading the raw
3556    /// slot while the peer view-composer's fold-in path read an
3557    /// operator-resolved slot would silently split the paired
3558    /// declared-slot enumerator and typed-view composition, and the
3559    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3560    /// refusal probe reading a third borrow would silently drift the
3561    /// cross-slot coherence gate's traversal input from the two peers,
3562    /// a three-consumer split at the enumerator, the view composer,
3563    /// and the self-parent gate far from the source `caixa.lisp` with
3564    /// no field naming the child-set-drift root cause. Lifting the
3565    /// resolution rule to a typed method on the substrate primitive
3566    /// means every downstream consumer of the caixa's per-`Caixa`
3567    /// OTP-supervisor outer-slice surface reaches for exactly one
3568    /// typed dispatch — the resolver's accept-set migrates as a unit
3569    /// on any future axis addition.
3570    ///
3571    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3572    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3573    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3574    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3575    /// at the outer altitude of the closed inner-`SupervisorSpec`
3576    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3577    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3578    /// borrow-shared" outer-accessor discipline extended onto the
3579    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3580    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3581    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3582    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3583    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3584    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3585    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3586    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3587    /// M2 typed-composite-element axis
3588    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3589    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3590    /// different altitude).
3591    ///
3592    /// Returns `&[crate::supervisor::ChildSpec]` (not
3593    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3594    /// child list treats it as a read-only sequence — the slice-view
3595    /// is the narrowest borrow that supports every present +
3596    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3597    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3598    /// input, `serde` slice-serialization) without leaking the backing
3599    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3600    /// reaches for (the storage-side `Vec` remains reachable through
3601    /// the `pub children` field for the mutation-carrying serde round-
3602    /// trip and per-test fixture-mutation paths, including the
3603    /// [`Self::supervisor_view`] fold-in path that clones the slot
3604    /// into the typed view). Named `children()` to match the storage
3605    /// field's name verbatim and the tatara-lisp author-surface term
3606    /// (`:children`) the field's own docstring already carries; the
3607    /// accessor's identity maps onto the canonical OTP supervision
3608    /// vocabulary the [`Caixa::children`] field's docstring already
3609    /// reaches for ("Static children of a supervisor").
3610    #[must_use]
3611    pub const fn children(&self) -> &[crate::supervisor::ChildSpec] {
3612        self.children.as_slice()
3613    }
3614
3615    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3616    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3617    /// accessor every consumer of the top-level manifest's per-Aplicacao
3618    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3619    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3620    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3621    /// same backing buffer the raw `self.membros.as_slice()` field access
3622    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3623    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3624    /// and every partially-authored Aplicacao carries before the
3625    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3626    /// `&[Membro]` degenerates to an empty slice on those arms without any
3627    /// silent `None` collapse).
3628    ///
3629    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3630    /// per-Aplicacao member list — the load-bearing container of every
3631    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3632    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3633    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3634    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3635    /// the `:entrada :para` external-gateway destination validates
3636    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3637    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3638    /// threads through a lifted per-entry accessor on the
3639    /// [`crate::aplicacao::Membro`] type: the
3640    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3641    /// identity scalar accessor (4a32abf) and the peer
3642    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3643    /// version-requirement scalar accessor (a40b0e3). Every downstream
3644    /// consumer of the mesh-graph path first passes through this outer
3645    /// accessor onto the slice and then dispatches per-member through
3646    /// the inner accessors — the two-level dispatch means every per-
3647    /// `:membros` reader now routes through a typed dispatch on the
3648    /// substrate primitive at both altitudes.
3649    ///
3650    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3651    /// inline at three production sites across two files — the
3652    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3653    /// enumerator's `!self.membros.is_empty()` presence probe
3654    /// (caixa-core/src/manifest.rs, which drives the
3655    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3656    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3657    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3658    /// composer's `self.membros.clone()` per-member fold-in path
3659    /// (caixa-core/src/manifest.rs, which materializes the typed
3660    /// [`crate::aplicacao::AplicacaoSpec`] view every
3661    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3662    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3663    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3664    /// [`crate::aplicacao::validate_no_self_membership`] input
3665    /// (caixa-core/src/layout.rs, which pins the "no member names the
3666    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3667    /// extension of the outer `:membros` axis (a per-cluster
3668    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3669    /// admission time so a cluster-specific member-set can tighten a
3670    /// caixa-declared list without re-authoring the `caixa.lisp`,
3671    /// promotion of the plain `Vec<Membro>` to a richer
3672    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3673    /// members land as a typed axis, per-member priority annotation once
3674    /// multi-strategy fan-out lands) would have had to be threaded
3675    /// through all three open-coded copies in lockstep or one consumer
3676    /// would silently disagree with the peers on which member slice a
3677    /// given Caixa resolves to — the enumerator's presence probe reading
3678    /// the raw slot while the peer view-composer's fold-in path read an
3679    /// operator-resolved slot would silently split the paired
3680    /// declared-slot enumerator and typed-view composition, and the
3681    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3682    /// refusal probe reading a third borrow would silently drift the
3683    /// cross-slot coherence gate's traversal input from the two peers, a
3684    /// three-consumer split at the enumerator, the view composer, and
3685    /// the self-membership gate far from the source `caixa.lisp` with no
3686    /// field naming the member-set-drift root cause. Lifting the
3687    /// resolution rule to a typed method on the substrate primitive
3688    /// means every downstream consumer of the caixa's per-`Caixa`
3689    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3690    /// typed dispatch — the resolver's accept-set migrates as a unit on
3691    /// any future axis addition.
3692    ///
3693    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3694    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3695    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3696    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3697    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3698    /// altitude. Peer at the outer altitude of the closed inner-
3699    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3700    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3701    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3702    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3703    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3704    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3705    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3706    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3707    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3708    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3709    /// pattern onto the sibling M3 typed-composite-element axis
3710    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3711    /// [`crate::AplicacaoSpec::membros`] element type at a different
3712    /// altitude).
3713    ///
3714    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3715    /// because every downstream consumer of the member list treats it
3716    /// as a read-only sequence — the slice-view is the narrowest borrow
3717    /// that supports every present + roadmapped consumer (`.iter()`,
3718    /// `.len()`, `.is_empty()`, the
3719    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3720    /// input, `serde` slice-serialization) without leaking the backing
3721    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3722    /// reaches for (the storage-side `Vec` remains reachable through the
3723    /// `pub membros` field for the mutation-carrying serde round-trip
3724    /// and per-test fixture-mutation paths, including the
3725    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3726    /// the typed view). Named `membros()` to match the storage field's
3727    /// name verbatim and the tatara-lisp author-surface term
3728    /// (`:membros`) the field's own docstring already carries; the
3729    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3730    /// vocabulary the [`Caixa::membros`] field's docstring already
3731    /// reaches for ("Member Servicos that make up this Aplicacao").
3732    #[must_use]
3733    pub const fn membros(&self) -> &[crate::aplicacao::Membro] {
3734        self.membros.as_slice()
3735    }
3736
3737    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3738    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3739    /// inter-Servico contract-list slice accessor every consumer of the
3740    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3741    /// slice-view keys off — returns the author-declared `:contratos`
3742    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3743    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3744    /// backing buffer the raw `self.contratos.as_slice()` field access
3745    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3746    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3747    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3748    /// single member with no inter-Servico edge carries; the returned
3749    /// `&[WitContract]` degenerates to an empty slice on those arms
3750    /// without any silent `None` collapse).
3751    ///
3752    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3753    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3754    /// container of every per-edge `{de, para, wit, endpoint | subject |
3755    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3756    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3757    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3758    /// adjacency-list seed dispatch on at mesh-artifact materialization
3759    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3760    /// `:membros` vertex set resolves against, closed by the
3761    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3762    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3763    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3764    /// per-edge axis threads through a lifted per-entry accessor on the
3765    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3766    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3767    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3768    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3769    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3770    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3771    /// and the WIT-world discriminant. Every downstream consumer of the
3772    /// mesh-graph edge path first passes through this outer accessor
3773    /// onto the slice and then dispatches per-contract through the
3774    /// inner accessors — the two-level dispatch means every
3775    /// per-`:contratos` reader now routes through a typed dispatch on
3776    /// the substrate primitive at both altitudes.
3777    ///
3778    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3779    /// accessed inline at two production sites in
3780    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3781    /// mesh-slot declared-slot enumerator's
3782    /// `!self.contratos.is_empty()` presence probe (which drives the
3783    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3784    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3785    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3786    /// typed-view composer's `self.contratos.clone()` per-contract
3787    /// fold-in path (which materializes the typed
3788    /// [`crate::aplicacao::AplicacaoSpec`] view every
3789    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3790    /// downstream `caixa-mesh` renderer dispatches on). A future
3791    /// extension of the outer `:contratos` axis (a per-cluster
3792    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3793    /// at admission time so a cluster-specific edge-set can tighten a
3794    /// caixa-declared list without re-authoring the `caixa.lisp`,
3795    /// promotion of the plain `Vec<WitContract>` to a richer
3796    /// `{static, dynamic}` partition once runtime-resolved contract
3797    /// edges land, per-edge policy annotation once the M4 per-edge
3798    /// policy overlay axis lands) would have had to be threaded through
3799    /// both open-coded copies in lockstep or one consumer would
3800    /// silently disagree with the peer on which edge slice a given
3801    /// Caixa resolves to — the enumerator's presence probe reading the
3802    /// raw slot while the peer view-composer's fold-in path read an
3803    /// operator-resolved slot would silently split the paired
3804    /// declared-slot enumerator and typed-view composition, a
3805    /// two-consumer split at the enumerator and the view composer far
3806    /// from the source `caixa.lisp` with no field naming the edge-set-
3807    /// drift root cause. Lifting the resolution rule to a typed method
3808    /// on the substrate primitive means every downstream consumer of
3809    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3810    /// reaches for exactly one typed dispatch — the resolver's
3811    /// accept-set migrates as a unit on any future axis addition.
3812    ///
3813    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3814    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3815    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3816    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3817    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3818    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3819    /// mesh-slot arm of the composite-slice sub-family the sibling
3820    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3821    /// Peer at the outer altitude of the closed inner-
3822    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3823    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3824    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3825    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3826    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3827    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3828    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3829    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3830    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3831    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3832    /// pattern onto the sibling M3 typed-composite-element axis
3833    /// ([`crate::aplicacao::WitContract`] composite, matching the
3834    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3835    /// different altitude).
3836    ///
3837    /// Returns `&[crate::aplicacao::WitContract]` (not
3838    /// `&Vec<WitContract>`) because every downstream consumer of the
3839    /// contract list treats it as a read-only sequence — the slice-view
3840    /// is the narrowest borrow that supports every present + roadmapped
3841    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3842    /// discriminant dispatch, `serde` slice-serialization) without
3843    /// leaking the backing `Vec`'s grow/push/reserve surface no
3844    /// consumer of the typed view reaches for (the storage-side `Vec`
3845    /// remains reachable through the `pub contratos` field for the
3846    /// mutation-carrying serde round-trip and per-test fixture-mutation
3847    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3848    /// clones the slot into the typed view). Named `contratos()` to
3849    /// match the storage field's name verbatim and the tatara-lisp
3850    /// author-surface term (`:contratos`) the field's own docstring
3851    /// already carries; the accessor's identity maps onto the canonical
3852    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3853    /// docstring already reaches for ("WIT-typed inter-Servico
3854    /// contracts").
3855    #[must_use]
3856    pub const fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3857        self.contratos.as_slice()
3858    }
3859
3860    /// Compose the Aplicacao-related flat slots into a single typed
3861    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3862    /// downstream renderer consumption. Returns `None` when the
3863    /// caixa isn't a `:kind Aplicacao`.
3864    #[must_use]
3865    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3866        if !self.kind().is_aplicacao() {
3867            return None;
3868        }
3869        Some(crate::aplicacao::AplicacaoSpec {
3870            membros: self.membros().to_vec(),
3871            contratos: self.contratos().to_vec(),
3872            politicas: self.politicas().cloned().unwrap_or_default(),
3873            placement: self.placement().cloned().unwrap_or_default(),
3874            entrada: self.entrada().cloned(),
3875        })
3876    }
3877
3878    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3879    /// *declares* a value on, in canonical declaration order
3880    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3881    /// `:entrada`). A slot counts as declared when its backing field
3882    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3883    ///
3884    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3885    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3886    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3887    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3888    /// caixa-flux / caixa-helm renderers only emit them for an
3889    /// Aplicacao. On any *other* kind a declared mesh slot is the
3890    /// manifest field's documented "ignored otherwise" (see the
3891    /// `:membros` … `:entrada` field docs): it silently passes
3892    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3893    /// rendered — far from the source caixa.lisp.
3894    /// [`crate::StandardLayout::verify`] consults this to reject that
3895    /// silent-drop at caixa-build time
3896    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3897    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3898    /// a slot foreign to the kind is a build error, not a silent drop.
3899    ///
3900    /// Lifted as a typed method (rather than an inline disjunction at
3901    /// the verify call site) so the mesh-slot set lives in one place —
3902    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3903    /// overlay, distributed-app takeover config) is one push here, and
3904    /// every consumer reaching for "which mesh slots are set" (the
3905    /// verify gate, a future `feira lint` kind-coherence advisory)
3906    /// inherits the canonical order without rolling its own.
3907    ///
3908    /// Each per-arm kebab-case label is routed through the peer
3909    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3910    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3911    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3912    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3913    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3914    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3915    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3916    /// kebab-case label + renderer-side artifact key) route through one
3917    /// canonical declaration per arm — same discipline the peer
3918    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3919    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3920    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3921    /// axis, extended here to close the M3 mesh-slot author-facing-label
3922    /// axis so both altitudes of the typed-slot algebra
3923    /// (per-Servico M2 + per-Aplicacao M3) share the same
3924    /// "one canonical byte-string per arm, next to the axis" discipline.
3925    #[must_use]
3926    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3927        let mut slots = Vec::new();
3928        if !self.membros().is_empty() {
3929            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3930        }
3931        if !self.contratos().is_empty() {
3932            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3933        }
3934        if self.politicas().is_some() {
3935            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3936        }
3937        if self.placement().is_some() {
3938            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3939        }
3940        if self.entrada().is_some() {
3941            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3942        }
3943        slots
3944    }
3945
3946    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3947    /// caixa *declares* a value on, in canonical declaration order
3948    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3949    /// `:children`). A slot counts as declared when its backing field
3950    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3951    ///
3952    /// The supervisor-tree slots compose the typed OTP supervisor of a
3953    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3954    /// `:children` field docs above). [`Self::supervisor_view`] only
3955    /// folds them into a validatable [`SupervisorSpec`] when the kind
3956    /// matches (returns `None` otherwise), and the wasm-operator's
3957    /// hierarchical reconciler only consumes them for a Supervisor. On
3958    /// any *other* kind a declared supervisor slot is the manifest
3959    /// field's documented "ignored otherwise" (see the `:estrategia` …
3960    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3961    /// and then vanishes — never validated, never reconciled — far from
3962    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3963    /// this to reject that silent-drop at caixa-build time
3964    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3965    /// exact mirror of the [`Self::declared_mesh_slots`] /
3966    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3967    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3968    /// error, not a silent drop.
3969    #[must_use]
3970    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3971        let mut slots = Vec::new();
3972        if self.estrategia().is_some() {
3973            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3974        }
3975        if self.max_restarts().is_some() {
3976            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3977        }
3978        if self.restart_window().is_some() {
3979            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3980        }
3981        if !self.children().is_empty() {
3982            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3983        }
3984        slots
3985    }
3986
3987    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3988    /// caixa *declares* a value on, in canonical declaration order
3989    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3990    /// declared when its backing field carries a value — a `Some(...)`,
3991    /// or a non-empty `Vec`.
3992    ///
3993    /// The M2 slots configure the runtime of a long-running wasm
3994    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3995    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3996    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3997    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3998    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3999    /// emit these slots for a Servico; on any *other* kind a declared M2
4000    /// slot is the manifest field's documented "ignored otherwise": its
4001    /// well-formedness is checked by [`crate::StandardLayout::verify`]
4002    /// but the value is never rendered into a chart / programs.yaml entry
4003    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
4004    /// vanishes, far from the source caixa.lisp.
4005    /// [`crate::StandardLayout::verify`] consults this to reject that
4006    /// silent-drop at caixa-build time
4007    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
4008    /// mirror of the [`Self::declared_mesh_slots`] /
4009    /// [`Self::declared_supervisor_slots`] gates on the peer
4010    /// kind-exclusive slot sets: a slot foreign to the kind is a build
4011    /// error, not a silent drop.
4012    ///
4013    /// Each per-arm kebab-case label is routed through the peer
4014    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
4015    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
4016    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
4017    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
4018    /// both halves of the M2 top-level slot's dual axis (author-facing
4019    /// kebab-case label + renderer-side camelCase overlay-container wire
4020    /// key) route through one canonical declaration per arm — same
4021    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
4022    /// author-label consts (889dc18) establish on the sibling
4023    /// per-callback axis inside the `:behavior` overlay block.
4024    #[must_use]
4025    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
4026        let mut slots = Vec::new();
4027        if self.limits().is_some() {
4028            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
4029        }
4030        if self.behavior().is_some() {
4031            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
4032        }
4033        if !self.upgrade_from().is_empty() {
4034            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
4035        }
4036        slots
4037    }
4038
4039    /// The kebab-case `:slot` tags of every code-surface slot this caixa
4040    /// declares a value on that its [`CaixaKind`] doesn't natively own,
4041    /// in canonical declaration order (`:exe` → `:servicos`). A
4042    /// code-surface slot is owned by exactly one kind: `:exe` by
4043    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
4044    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
4045    /// `ComputeUnit` daemon surface).
4046    ///
4047    /// Each is silently ignored when declared on the wrong kind: the
4048    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
4049    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
4050    /// code-running kind a declared `:exe` / `:servicos` is the manifest
4051    /// field's documented "ignored otherwise" — its path is checked for
4052    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
4053    /// (which run after [`Caixa::from_lisp`]), but the value is never
4054    /// rendered into a build target or programs.yaml entry. It silently
4055    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
4056    /// caixa.lisp, with no field naming which slot is foreign.
4057    ///
4058    /// [`crate::StandardLayout::verify`] consults this to reject that
4059    /// silent-drop at caixa-build time
4060    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
4061    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
4062    /// gates ([`Self::declared_servico_slots`] /
4063    /// [`Self::declared_supervisor_slots`] /
4064    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
4065    /// axis to be closed on the typed surface. The Supervisor /
4066    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
4067    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
4068    /// diagnostics — they fire ahead of this gate on the same `verify`
4069    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
4070    /// and this method is moot. For Biblioteca / Binario / Servico, this
4071    /// gate fires when a code-running kind declares another code-running
4072    /// kind's exclusive code surface.
4073    ///
4074    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
4075    /// may legitimately ship a `lib/` helper that the underlying
4076    /// substrate (the nix flake for Binario, the wasm component build
4077    /// for Servico) bundles into its build, so the slot's
4078    /// declared-on-wrong-kind cardinality isn't a structural error on
4079    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
4080    /// is the native case (the slot's owning kind). Supervisor /
4081    /// Aplicacao declaring `:bibliotecas` is gated upstream by
4082    /// [`crate::LayoutError::SupervisorOwnsCode`] /
4083    /// [`crate::LayoutError::AplicacaoOwnsCode`].
4084    ///
4085    /// Lifted as a typed method (rather than an inline disjunction at
4086    /// the verify call site) so the foreign-code-slot set lives in one
4087    /// place — a future kind that gains its own code-surface slot is
4088    /// one push here, and every consumer reaching for "which code
4089    /// surfaces are foreign to this kind" (the verify gate, a future
4090    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
4091    /// per-caixa build-target classifier) inherits the canonical order
4092    /// without rolling its own.
4093    #[must_use]
4094    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
4095        let mut slots = Vec::new();
4096        if !self.exe().is_empty() && !self.kind().requires_exe() {
4097            slots.push(":exe");
4098        }
4099        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
4100            slots.push(":servicos");
4101        }
4102        slots
4103    }
4104
4105    /// Validate every entry of `:deps` and `:deps-dev` through
4106    /// [`Dep::validate`] — closing the parity loop with the per-axis
4107    /// `:versao` gates already wired into the typed-graph
4108    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
4109    /// 9888b13) and typed supervisor tree
4110    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
4111    ///
4112    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
4113    /// were the only `:versao` axes still untyped past
4114    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
4115    /// as a String without parsing it, so a malformed-but-non-empty
4116    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4117    /// silently passed parse and the `semver::Error` surfaced at
4118    /// lacre-resolve time, far from the source caixa.lisp, with no
4119    /// field naming which `:deps` entry carried the typo. Lifting the
4120    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4121    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4122    /// every requirement string past `validate_deps` is round-trippable
4123    /// through [`crate::parse_requirement`] without re-checking at the
4124    /// resolver layer.
4125    ///
4126    /// Both lists run through the same per-entry validator so a typo
4127    /// in `:deps-dev` surfaces with the same diagnostic as one in
4128    /// `:deps` — neither axis is a second-class citizen of the typed
4129    /// surface.
4130    ///
4131    /// Within each list, [`DepError::DuplicateNome`] closes the
4132    /// set-not-multiset discipline on the `:nome` axis: two entries
4133    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4134    /// triples that the caixa-resolver's lacre pipeline collapses to one
4135    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4136    /// silently overwrites the first at `concrete_versao`-resolve time
4137    /// (the same "second wins / one silently overwrites the other"
4138    /// shape the peer typed-graph duplicate gates already close on every
4139    /// other Vec-shaped authoring surface that keys by name). The
4140    /// duplicate check fires per-list and runs *after* each per-entry
4141    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4142    /// surfaces its narrower per-entry diagnostic
4143    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4144    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4145    /// diagnostic — the canonical "per-entry shape before cross-entry
4146    /// uniqueness" precedence the peer `:children :caixa`
4147    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4148    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4149    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4150    /// ([`crate::AplicacaoSpec::validate_placement`]),
4151    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4152    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4153    /// and the within-`:upgrade-from`-entry per-instruction-class
4154    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4155    /// [`crate::UpgradeError::DuplicateStateChange`],
4156    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4157    ///
4158    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4159    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4160    /// same name in both tables (the dev table's pin overrides the
4161    /// runtime table's pin in test/dev contexts), and caixa's surface
4162    /// mirrors that convention until a deliberate choice retires the
4163    /// override pattern. Only within-list duplicates are structurally
4164    /// incoherent — those are what this gate closes.
4165    pub fn validate_deps(&self) -> Result<(), DepError> {
4166        for &list in crate::dep::DepList::ALL {
4167            let mut seen = std::collections::HashSet::new();
4168            for dep in self.deps_of(list) {
4169                dep.validate()?;
4170                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4171                    DepError::DuplicateNome {
4172                        nome: dep.nome().to_string(),
4173                        list: list.as_str(),
4174                    }
4175                })?;
4176            }
4177        }
4178        Ok(())
4179    }
4180
4181    /// Reject `:nome` values the K8s apiserver would refuse at admission
4182    /// time. The top-level Caixa identity flows directly into every
4183    /// substrate-side artifact's `metadata.name` axis: the
4184    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4185    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4186    /// aggregator keys ComputeUnit derivation off
4187    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4188    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4189    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4190    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4191    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4192    /// ([`caixa-mesh::lib::cilium_network_policies`],
4193    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4194    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4195    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4196    /// schema enforces the DNS-1123 label rule on admission; a
4197    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4198    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4199    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4200    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4201    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4202    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4203    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4204    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4205    /// Invalid value` rejection on whichever derived artifact admitted
4206    /// first, far from the source `caixa.lisp` and without any field
4207    /// naming the offending `:nome`.
4208    ///
4209    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4210    /// substrate-side predicate the per-axis name gates already share:
4211    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4212    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4213    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4214    /// diagnostic is self-locating (the offending `:nome` is named
4215    /// verbatim) and the author can grep their `caixa.lisp` for
4216    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4217    /// every per-axis sibling gate already exposes
4218    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4219    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4220    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4221    ///
4222    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4223    /// derive macro stores the raw String) is gated by the narrower
4224    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4225    /// consulted, mirroring the empty-first cascade every per-axis
4226    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4227    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4228    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4229        // Routes through the shared
4230        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4231        // name axes each land on so drift between the eight axes'
4232        // accepted DNS-1123-label sets is structurally impossible.
4233        let nome = self.nome();
4234        crate::render::require_valid_dns_1123_label(
4235            nome,
4236            || ManifestError::NomeEmpty,
4237            |reason| ManifestError::NomeInvalid {
4238                nome: nome.to_string(),
4239                reason,
4240            },
4241        )
4242    }
4243
4244    /// Reject `:nome` values whose joint length with the canonical
4245    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4246    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4247    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4248    /// substrate carries materializes the caixa's `:nome` through the
4249    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4250    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4251    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4252    /// `ChartDir.name` + `Chart.yaml::name`
4253    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4254    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4255    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4256    /// `oci://<registry>/lareira-<nome>` chart ref
4257    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4258    /// admission rule strict-parses against DNS-1123-label, the Helm
4259    /// operator's tracking-secret name is derived from `release_name`
4260    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4261    /// K8s object `metadata.name` axes embed the chart name as a
4262    /// prefix — every one fails admission on a > 63-byte chart name.
4263    ///
4264    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4265    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4266    /// `:nome` of 56–63 bytes silently passed validate (the inner
4267    /// DNS-1123 check accepts the bare `:nome`) but produced a
4268    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4269    /// rejected at admission — far from the source `caixa.lisp`, with
4270    /// no field naming the overflow root cause. The
4271    /// [`lareira_chart_name`] helper's own doc comment
4272    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4273    /// "the M4 admission webhook will pin the joint-length invariant
4274    /// when it lands". This gate lands the invariant at the
4275    /// manifest-validate layer rather than waiting for the apiserver
4276    /// — the same fail-at-the-source posture every peer per-axis
4277    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4278    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4279    /// `:edicao`, etc.) takes.
4280    ///
4281    /// Thin wrapper around
4282    /// [`crate::render::is_lareira_chart_name_shape`] (the
4283    /// substrate-side predicate that composes [`lareira_chart_name`] +
4284    /// [`is_dns_1123_label`] via the lifted
4285    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4286    /// shared parser-shaped reason into the
4287    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4288    /// diagnostic is self-locating (the offending `:nome` is named
4289    /// verbatim alongside the rendered chart name and the budget) and
4290    /// the author can shorten in one edit. The gate runs across every
4291    /// `:kind` — `:nome` is the substrate-wide identity axis any
4292    /// future renderer the substrate adds can derive a
4293    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4294    /// the drift footgun where a future kind grows a chart-emitting
4295    /// render path while the validate cascade doesn't catch it.
4296    ///
4297    /// Runs *after* [`Self::validate_nome`] so the narrower
4298    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4299    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4300    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4301    /// specific shape error rather than the chart-name-budget error,
4302    /// preserving the legitimate "well-shaped `:nome` that happens to
4303    /// overflow the joint cap" arm for this gate.
4304    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4305        let nome = self.nome();
4306        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4307            ManifestError::NomeChartNameBudgetExceeded {
4308                nome: nome.to_string(),
4309                reason,
4310            }
4311        })
4312    }
4313
4314    /// Reject `:versao` values that don't parse as [`semver::Version`].
4315    /// The top-level Caixa version flows directly into every
4316    /// substrate-side artifact that carries a "this is which version of
4317    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4318    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4319    /// SemVer-2-strict at `helm template` / `helm install` time per
4320    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4321    /// `feira publish` Zig-style `v<versao>` git tag
4322    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4323    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4324    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4325    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4326    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4327    /// `skopeo push`, the lacre closure's pinned versions
4328    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4329    /// `:upgrade-from :from` references peers in this exact `versao`
4330    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4331    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4332    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4333    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4334    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4335    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4336    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4337    /// into the version field a peer `:deps :versao` accepts;
4338    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4339    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4340    /// derive macro stores the raw String) and the failure surfaced at
4341    /// the *first* downstream consumer that strict-parses it: at
4342    /// `helm install` time as a chart-version rejection, at
4343    /// `feira publish` time as a malformed git tag, at lacre-resolve
4344    /// time as a `semver::Error` not naming the offending caixa, at
4345    /// `feira upgrade --to <versao>` time as an unresolvable
4346    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4347    /// and without any field naming the offending `:versao`.
4348    ///
4349    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4350    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4351    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4352    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4353    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4354    /// variant, carrying the offending `:versao` verbatim + a
4355    /// parser-shaped reason naming the specific violation, so the
4356    /// diagnostic is self-locating (the author can grep their
4357    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4358    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4359    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4360    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4361    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4362    /// now structurally equivalent (every value past validate is
4363    /// round-trippable through [`semver::Version::parse`] without
4364    /// re-checking at the renderer, resolver, or operator hot-upgrade
4365    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4366    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4367    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4368    ///
4369    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4370    /// the derive macro stores the raw String) is gated by the
4371    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4372    /// consulted, mirroring the empty-first cascade every per-axis
4373    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4374    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4375    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4376    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4377        let versao = self.versao();
4378        if versao.is_empty() {
4379            return Err(ManifestError::VersaoEmpty);
4380        }
4381        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4382            versao: versao.to_string(),
4383            reason: e.to_string(),
4384        })?;
4385        Ok(())
4386    }
4387
4388    /// Reject `:restart-window` values the shared
4389    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4390    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4391    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4392    /// `Option<Duration>` routed through the shared codec via `with =
4393    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4394    /// view-construction path ([`Self::supervisor_view`]) folds the
4395    /// raw string through the same shared codec and soft-swallows the
4396    /// parse error as `None` to keep the view best-effort. Without
4397    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4398    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4399    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4400    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4401    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4402    /// edge case) silently produced a `SupervisorSpec` with
4403    /// `restart_window: None`, indistinguishable from the canonical
4404    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4405    /// `MaxIntensity / Period` invariant turns into a never-reset
4406    /// supervisor far from the source `caixa.lisp`, with no field
4407    /// naming the offending `:restart-window`. Lifting the gate to a
4408    /// Caixa-level validator mirrors the trajectory of the peer
4409    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4410    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4411    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4412    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4413    ///
4414    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4415    /// (the shared codec backing `:supervisor :restart-window` as
4416    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4417    /// `:politicas :circuit-breaker :window` — all three covered by
4418    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4419    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4420    /// variant, carrying the offending raw string + a parser-shaped
4421    /// reason naming the canonical authoring form, so the diagnostic
4422    /// is self-locating (the author can grep their `caixa.lisp` for
4423    /// `:restart-window "<value>"` and fix it in one edit) and
4424    /// uniform with every other manifest-level validate diagnostic.
4425    /// With this gate the four `:restart-window`-shaped surfaces (the
4426    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4427    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4428    /// now structurally equivalent — every value past the codec is in
4429    /// one accepted set, by construction.
4430    ///
4431    /// `None` (the canonical "omit the slot to express no reset"
4432    /// shape) is accepted trivially — the gate is a no-op when the
4433    /// author didn't author a window. The empty string is rejected by
4434    /// the shared codec (its digit-only gate refuses an empty
4435    /// magnitude), surfacing the same `RestartWindowMalformed`
4436    /// diagnostic as every other rejected non-canonical shape.
4437    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4438        let Some(s) = self.restart_window() else {
4439            return Ok(());
4440        };
4441        crate::supervisor::duration_codec::parse(s)
4442            .map(|_| ())
4443            .map_err(|reason| ManifestError::RestartWindowMalformed {
4444                restart_window: s.to_string(),
4445                reason,
4446            })
4447    }
4448
4449    /// Reject per-entry values on the three Caixa-level code-surface
4450    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4451    /// layout checker's `root.join(p)` sandbox would silently subvert.
4452    /// Same three structural footguns the peer
4453    /// [`BehaviorSpec::validate`] (b0c8389) and
4454    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4455    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4456    /// `:upgrade-from :state-change :script` axes, here lifted onto
4457    /// the three top-level code-path axes through the shared
4458    /// [`is_sandboxed_relative_path`] predicate:
4459    ///
4460    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4461    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4462    ///     [`Path::join`] as the base itself — `root.join("")` ==
4463    ///     `root`, so the existence check (`self.exists(&root)`)
4464    ///     trivially passes (the project root exists), and the layout
4465    ///     silently treats the project root as a biblioteca / exe /
4466    ///     servico entry. The `:bibliotecas` loop then hands the root
4467    ///     to `tatara_lisp::read` at `feira build` time as if the root
4468    ///     directory itself were a Lisp source file — a parse error
4469    ///     far from the source `caixa.lisp` with no field naming the
4470    ///     offending entry.
4471    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4472    ///     [`Path::join`] *replaces* the base when the right-hand side
4473    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4474    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4475    ///     The existence check then silently consults whatever the
4476    ///     escaped path resolves to — for `:bibliotecas`, the layout
4477    ///     has no `starts_with`-fence (only `:exe` is fenced under
4478    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4479    ///     `:bibliotecas` entry that happens to resolve on disk
4480    ///     silently passes. For `:exe` / `:servicos` the fence catches
4481    ///     the absolute case downstream as `ExeOutsideDir` /
4482    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4483    ///     doesn't exist), but with a downstream-shaped diagnostic
4484    ///     that names the resolved escape path rather than the
4485    ///     authoring footgun at the source.
4486    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4487    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4488    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4489    ///     through [`Path::join`] as a traversal above the caixa root.
4490    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4491    ///     *component-aware* (not canonical-path-aware), so
4492    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4493    ///     is **true** even though the canonical resolution
4494    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4495    ///     — the fence silently lets the parent-escape through, and
4496    ///     the existence check passes if that escape-target happens
4497    ///     to exist. Caught regardless of where the `..` sits
4498    ///     (leading, mid-path, trailing) so the gate matches the peer
4499    ///     predicate's full coverage.
4500    ///
4501    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4502    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4503    /// same per-slot diagnostic shape every peer per-axis path-gate
4504    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4505    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4506    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4507    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4508    /// canonical foreign-code-slot diagnostic, so a manifest with
4509    /// multiple malformed slots surfaces the lexicographically-earliest
4510    /// slot's diagnostic deterministically.
4511    ///
4512    /// Lifted to the typed surface as a Caixa-level validator (peer
4513    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
4514    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
4515    /// and wired into [`crate::StandardLayout::verify`] before the
4516    /// existence-check loops so the diagnostic names the offending
4517    /// slot at the source caixa.lisp rather than reporting a
4518    /// downstream `MissingEntry` / `ExeOutsideDir` /
4519    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
4520    /// The fourth typed code-path surface — every author-supplied
4521    /// path on the manifest — is now structurally accept-shaped
4522    /// past validate, peer with `:behavior :on-*` and
4523    /// `:upgrade-from :state-change :script`.
4524    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
4525        /// Per-slot file-type contract for the three Caixa-level
4526        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
4527        /// Each variant names the predicate the per-entry file-type
4528        /// gate consults; [`Self::None`] opts the slot out of any
4529        /// file-type contract. Lifted as a typed local enum so the
4530        /// per-slot dispatch is exhaustive at the `match` — adding a
4531        /// future axis to the typed-substrate `:` slot set (the
4532        /// future `:assets` resource axis the M5 roadmap names, the
4533        /// future `:nix-flake` derivation axis the caixa-flake
4534        /// emitter consults) lands as one variant + one `match` arm,
4535        /// not a coordinated rewrite of every per-slot bool flag.
4536        ///
4537        /// Peer of the typed-substrate per-slot variant disciplines
4538        /// already established on this surface
4539        /// ([`crate::supervisor::RestartStrategy`] +
4540        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
4541        /// supervision-tree axis,
4542        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
4543        /// placement axis, [`crate::aplicacao::WitTarget`] on the
4544        /// `:contratos` payload-target axis): the typed `enum` is
4545        /// the substrate's single source of truth for the per-axis
4546        /// dispatch, and every consumer (the per-arm body here, the
4547        /// future feira-lint per-slot diagnostic renderer, the M4
4548        /// per-axis admission webhook) reaches for the same typed
4549        /// surface rather than re-deriving the partition from inline
4550        /// flag combinations.
4551        enum CodePathFileType {
4552            /// `:exe` — nix-build derivation output, no terminating-
4553            /// extension contract (the canonical `"exe/<name>"`
4554            /// fixtures the layout's `ExeOutsideDir` error message
4555            /// documents carry no extension by convention).
4556            None,
4557            /// `:bibliotecas` — tatara-lisp source files the
4558            /// `feira build` loop reads through `tatara_lisp::read`
4559            /// at parse time. Routes to [`is_lisp_extension`].
4560            LispSource,
4561            /// `:servicos` — ComputeUnit-CR YAML files the
4562            /// caixa-helm / caixa-flux renderers consume through
4563            /// `serde_yaml::from_str`. Routes to
4564            /// [`is_computeunit_yaml_extension`].
4565            ComputeUnitYaml,
4566        }
4567
4568        // The per-slot [`CodePathFileType`] selects which axes carry the
4569        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
4570        // source axis (the `feira build` loop at
4571        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
4572        // `tatara_lisp::read` at parse time) — the lifted
4573        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
4574        // `:exe` is the nix-built executable surface (per the canonical
4575        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
4576        // error message documents and every in-tree
4577        // `caixa_with_code_paths` positive control uses) — its file-type
4578        // contract is "nix-build derivation output", not a typed source
4579        // file, so [`CodePathFileType::None`] opts the slot out of any
4580        // file-type gate. `:servicos` is the `.computeunit.yaml`
4581        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
4582        // renderers consume each entry through `serde_yaml::from_str` as
4583        // a typed `ComputeUnit` CR) — the lifted
4584        // [`is_computeunit_yaml_extension`] predicate gates the compound
4585        // `.computeunit.yaml` suffix. All three axes are surfaced through
4586        // the same iteration so the sandbox-shape + duplicate gates
4587        // apply uniformly; the typed file-type dispatch fires per-slot
4588        // exactly where the downstream consumer's accepted set demands
4589        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
4590        // compounding lift on the peer 64772a9 `:bibliotecas`
4591        // `.lisp`-gate trajectory — the second of the three code-path
4592        // axes to land on a typed compound-suffix gate, with the same
4593        // self-locating per-slot diagnostic shape every peer per-axis
4594        // file-type lift uses (`*NonLispExtension { slot, path }` /
4595        // `*NonComputeUnitYamlExtension { slot, path }`).
4596        for (slot, list, file_type) in [
4597            (
4598                ":bibliotecas",
4599                &self.bibliotecas,
4600                CodePathFileType::LispSource,
4601            ),
4602            (":exe", &self.exe, CodePathFileType::None),
4603            (
4604                ":servicos",
4605                &self.servicos,
4606                CodePathFileType::ComputeUnitYaml,
4607            ),
4608        ] {
4609            // Per-slot set-not-multiset gate on the typed code-path axis.
4610            // Every peer Vec-shaped author-supplied list past validate is
4611            // a set, not a multiset: `:membros :caixa`
4612            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
4613            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
4614            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
4615            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
4616            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
4617            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
4618            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
4619            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
4620            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
4621            // the three code-path lists are the last Vec-shaped author-
4622            // supplied slots on the typed Caixa surface still admitting a
4623            // duplicate entry silently. Scope is per-list (`:bibliotecas`
4624            // duplicates are flagged within `:bibliotecas`, not across
4625            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
4626            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
4627            // legitimate dev-vs-runtime shape on the dep axis, fenced
4628            // separately by [`crate::dep::validate_no_self_dep`]). On the
4629            // code-path axis a cross-slot collision is structurally
4630            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
4631            // fence — `:exe` and `:servicos` entries are confined to their
4632            // own directory trees, so the only way a string could appear
4633            // on two code-path lists is the (rare, structurally invalid)
4634            // case where `:bibliotecas` carries an `"exe/<x>"` or
4635            // `"servicos/<x>.yaml"`-shaped path.
4636            //
4637            // Without the gate three authoring footguns silently passed:
4638            //
4639            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
4640            //     canonical copy-paste-the-wrong-file footgun. `feira
4641            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
4642            //     list and re-parses the same file twice, wasting work
4643            //     and silently masking the author's intent to declare a
4644            //     *second* biblioteca.
4645            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
4646            //     Binario surface. The future `caixa-flake` `nix flake`
4647            //     emitter that materializes each `:exe` entry as a flake
4648            //     `packages.<exe-name>` derivation would collide on the
4649            //     duplicate package name and surface a flake-eval error
4650            //     far from the source `caixa.lisp`.
4651            //   - `:servicos ("servicos/x.computeunit.yaml"
4652            //     "servicos/x.computeunit.yaml")` — the same footgun on
4653            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
4654            //     renderers already refuse `:servicos.len() != 1` with
4655            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
4656            //     that diagnostic surfaces "too many servicos" without
4657            //     naming "duplicate entry" — the typed self-locating
4658            //     "which entry is the duplicate" framing only lands at
4659            //     this gate.
4660            //
4661            // Same `seen.insert(entry.as_str())` shape every peer per-list
4662            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
4663            // 86c769b, `:deps` 359fba5) and the same "structural shape
4664            // checks fire before the duplicate check on the same entry"
4665            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
4666            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
4667            // empty entry first, not the duplicate on the later pair).
4668            let mut seen = std::collections::HashSet::new();
4669            for entry in list {
4670                let path = Path::new(entry);
4671                match is_sandboxed_relative_path(path) {
4672                    Ok(()) => {}
4673                    Err(PathShapeViolation::Empty) => {
4674                        return Err(ManifestError::CodePathEmpty { slot });
4675                    }
4676                    Err(PathShapeViolation::Absolute) => {
4677                        return Err(ManifestError::CodePathAbsolute {
4678                            slot,
4679                            path: path.to_path_buf(),
4680                        });
4681                    }
4682                    Err(PathShapeViolation::ParentEscape) => {
4683                        return Err(ManifestError::CodePathParentEscape {
4684                            slot,
4685                            path: path.to_path_buf(),
4686                        });
4687                    }
4688                }
4689                // The per-slot file-type gate dispatched through the
4690                // typed [`CodePathFileType`] selector above. Each variant
4691                // routes to the lifted predicate the downstream consumer
4692                // demands:
4693                //
4694                //   - [`LispSource`] → [`is_lisp_extension`] for
4695                //     `:bibliotecas` (the `feira build` loop's
4696                //     `tatara_lisp::read` consumer);
4697                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
4698                //     for `:servicos` (the caixa-helm / caixa-flux
4699                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
4700                //     accepted set);
4701                //   - [`None`] for `:exe` — the nix-build derivation-
4702                //     output axis has no terminating-extension contract.
4703                //
4704                // Fires after the sandbox-shape arms so a path that is
4705                // *both* sandbox-escaping and wrong-extension surfaces
4706                // the more fundamental sandbox-shape diagnostic first
4707                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
4708                // `ParentEscape` → `NonLispExtension` arm-ordering on
4709                // `:behavior :on-*` c97815a, and `EmptyScript` →
4710                // `AbsoluteScript` → `ParentEscapeScript` →
4711                // `NonLispExtensionScript` on
4712                // `:upgrade-from :state-change :script` 33cc830), and
4713                // before the duplicate gate so the narrower per-entry
4714                // file-type shape dominates the cross-entry uniqueness
4715                // diagnostic (a
4716                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
4717                // `:servicos` surfaces
4718                // `CodePathNonComputeUnitYamlExtension` on the first
4719                // entry rather than `CodePathDuplicate` on the pair —
4720                // peer with the 64772a9 `:bibliotecas`
4721                // `("lib/x.txt" "lib/x.txt")` ordering).
4722                match file_type {
4723                    CodePathFileType::None => {}
4724                    CodePathFileType::LispSource => {
4725                        if !is_lisp_extension(path) {
4726                            return Err(ManifestError::CodePathNonLispExtension {
4727                                slot,
4728                                path: path.to_path_buf(),
4729                            });
4730                        }
4731                    }
4732                    CodePathFileType::ComputeUnitYaml => {
4733                        if !is_computeunit_yaml_extension(path) {
4734                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
4735                                slot,
4736                                path: path.to_path_buf(),
4737                            });
4738                        }
4739                    }
4740                }
4741                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
4742                    ManifestError::CodePathDuplicate {
4743                        slot,
4744                        path: path.to_path_buf(),
4745                    }
4746                })?;
4747            }
4748        }
4749        Ok(())
4750    }
4751
4752    /// Reject `:etiquetas` lists with an empty entry or with two entries
4753    /// agreeing on the same string. `:etiquetas` is the universal
4754    /// registry-search-tag axis on [`Caixa`] (every kind carries the
4755    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4756    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
4757    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
4758    /// a [`std::collections::BTreeSet`] alongside the four substrate-
4759    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
4760    /// Two authoring footguns silently passed validate without this gate:
4761    ///
4762    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
4763    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
4764    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
4765    ///     `chart.metadata.keywords` admits the value without a strict
4766    ///     parser-side gate, but the empty keyword has no operational
4767    ///     meaning — it indexes nothing in the future caixa-registry
4768    ///     search axis and clutters the rendered chart with a no-op tag.
4769    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
4770    ///     copy-paste-the-wrong-tag footgun) silently passed validate
4771    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
4772    ///     at chart render — a "second wins / one silently disappears"
4773    ///     shape divergent from every peer typed-graph set gate
4774    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
4775    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
4776    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4777    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4778    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4779    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
4780    ///     on `:upgrade-from`, the per-instruction-class singularity
4781    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
4782    ///     [`crate::UpgradeError::DuplicateStateChange`] /
4783    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
4784    ///     discipline is uniform: every Vec-shaped author-supplied list
4785    ///     past validate is set-not-multiset, by construction.
4786    ///
4787    /// Past the empty arm the gate enforces the chart-keyword shape
4788    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
4789    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
4790    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
4791    /// continuation. Closes the canonical paste-from-doc footguns the
4792    /// bare empty + duplicate arms left open: paste-from-aligned-doc
4793    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
4794    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
4795    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
4796    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
4797    /// — the author meant three separate list entries), path-separator
4798    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
4799    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
4800    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
4801    /// control bytes that would silently land as malformed search tags
4802    /// in the rendered Chart.yaml `keywords:` array and break the
4803    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
4804    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
4805    /// established on the sibling universal-axis `Vec<String>` surface
4806    /// — the second universal-axis Vec<String> surface to land the
4807    /// empty-first-then-shape-then-duplicate per-entry cascade.
4808    ///
4809    /// Same empty-first cascade discipline every peer per-axis gate
4810    /// uses: the per-entry empty arm fires before the per-entry shape
4811    /// arm fires before the cross-entry duplicate arm, so an
4812    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
4813    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
4814    /// has no value" defect) before either the shape or the duplicate
4815    /// diagnostic. Walks the list in declaration order so the
4816    /// first-collision diagnostic surfaces the lexicographically-
4817    /// earliest offending position, peer with every other duplicate
4818    /// gate on this surface.
4819    ///
4820    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
4821    /// caixa-build gate alongside the peer universal gates
4822    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4823    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
4824    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
4825    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4826    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4827    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4828    /// slot sets. The future caixa-registry search axis can reach for
4829    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
4830    /// chart-keyword-shaped string without re-deriving the precondition.
4831    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
4832        let mut seen = std::collections::HashSet::new();
4833        for etiqueta in self.etiquetas() {
4834            if etiqueta.is_empty() {
4835                return Err(ManifestError::EtiquetaEmpty);
4836            }
4837            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
4838                ManifestError::EtiquetaInvalid {
4839                    etiqueta: etiqueta.clone(),
4840                    reason,
4841                }
4842            })?;
4843            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
4844                ManifestError::EtiquetaDuplicate {
4845                    etiqueta: etiqueta.clone(),
4846                }
4847            })?;
4848        }
4849        Ok(())
4850    }
4851
4852    /// Reject `:autores` lists with an empty entry or with two entries
4853    /// agreeing on the same string. `:autores` is the universal
4854    /// maintainer-axis on [`Caixa`] (every kind carries the
4855    /// `Vec<String>` slot) and lands verbatim as the Helm chart
4856    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
4857    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
4858    /// to a `Maintainer { name, email: None }` without dedup). Two
4859    /// authoring footguns silently passed validate without this gate:
4860    ///
4861    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
4862    ///     blank-doc footgun) rendered as
4863    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
4864    ///     empty maintainer name has no operational meaning — it
4865    ///     identifies no one in the substrate's authorship index and
4866    ///     clutters the rendered chart with a no-op maintainer.
4867    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
4868    ///     the copy-paste-the-wrong-author footgun) silently passed
4869    ///     validate and rendered as two identical maintainer entries.
4870    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
4871    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
4872    ///     rendered `keywords:` array at chart-render time), the
4873    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
4874    ///     entries stack verbatim in the chart, divergent from every
4875    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
4876    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
4877    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
4878    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
4879    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
4880    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
4881    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
4882    ///     `:etiquetas`).
4883    ///
4884    /// Past the empty arm the gate enforces the chart-maintainer-name
4885    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
4886    /// the structural single-line printable-UTF-8 floor every realistic
4887    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
4888    /// or trailing whitespace, no ASCII control characters anywhere,
4889    /// Unicode bytes accepted. Closes the canonical paste-from-doc
4890    /// footguns the bare empty + duplicate arms left open:
4891    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
4892    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
4893    /// pasted a multi-line block of author records into one `:autores`
4894    /// entry instead of splitting into one entry per author),
4895    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
4896    /// and the paste-from-binary-blob control bytes that would silently
4897    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
4898    /// `maintainers:` array. Mirrors the shape-predicate cascade
4899    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
4900    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
4901    /// establish past their own empty arms on the sibling universal-axis
4902    /// `Option<String>` surfaces — the first universal-axis Vec<String>
4903    /// surface to land the empty-first-then-shape-then-duplicate per-entry
4904    /// cascade.
4905    ///
4906    /// Same empty-first cascade discipline every peer per-axis gate
4907    /// uses: the per-entry empty arm fires before the per-entry shape
4908    /// arm before the cross-entry duplicate arm. Walks the list in
4909    /// declaration order so the first-collision diagnostic surfaces the
4910    /// lexicographically-earliest offending position, peer with every
4911    /// other duplicate gate on this surface.
4912    ///
4913    /// Universal-axis (every kind carries `:autores`), so wired at the
4914    /// caixa-build gate alongside the peer universal gates
4915    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
4916    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
4917    /// [`Self::validate_code_paths`] — before the kind-coherence gates
4918    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
4919    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
4920    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
4921    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
4922    /// slot sets.
4923    pub fn validate_autores(&self) -> Result<(), ManifestError> {
4924        let mut seen = std::collections::HashSet::new();
4925        for autor in self.autores() {
4926            if autor.is_empty() {
4927                return Err(ManifestError::AutorEmpty);
4928            }
4929            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
4930                ManifestError::AutorInvalid {
4931                    autor: autor.clone(),
4932                    reason,
4933                }
4934            })?;
4935            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
4936                ManifestError::AutorDuplicate {
4937                    autor: autor.clone(),
4938                }
4939            })?;
4940        }
4941        Ok(())
4942    }
4943
4944    /// Reject `:repositorio` values whose shape the shared
4945    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
4946    /// `repositorio: Option<String>` slot on [`Caixa`] is the
4947    /// universal git-shaped homepage axis every kind carries — the
4948    /// substrate routes the same string through two load-bearing
4949    /// consumers:
4950    ///
4951    ///   - [`caixa-helm`] folds it verbatim into the rendered
4952    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
4953    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
4954    ///     the chart `README.md` `repo = …` interpolation
4955    ///     (`caixa-helm/src/lib.rs:359`).
4956    ///   - [`caixa-flux`] folds it verbatim into the standalone
4957    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
4958    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
4959    ///     `GitRepository.spec.url` the cluster's source-controller
4960    ///     polls — the load-bearing deploy-time axis.
4961    ///
4962    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
4963    /// substitute a placeholder when the slot is absent (`None` → the
4964    /// fallback fires); a `Some("")` *skips the fallback* and silently
4965    /// passes the empty string through to `Chart.yaml home: ""` /
4966    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
4967    /// controller both reject the empty URL far from the source
4968    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
4969    /// Similarly a malformed `:repositorio` (whitespace, control char,
4970    /// missing `:` separator, leading `-`) silently lands in the
4971    /// rendered artifacts and breaks at `git clone` / `helm template`
4972    /// / `flux reconcile` time.
4973    ///
4974    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
4975    /// same shared predicate the peer [`crate::DepSource::validate`]
4976    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
4977    /// gate the two `git URL`-shaped surfaces on the typed Caixa
4978    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
4979    /// structurally equivalent: every value past validate is
4980    /// guaranteed-acceptable by the predicate's union of constraints
4981    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
4982    /// control chars, ASCII only, no leading `:`, contains a `:`
4983    /// separator). The predicate accepts every documented authoring
4984    /// shape — `github:org/repo` shorthand, `https://host/path`,
4985    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
4986    /// scp-style SSH, `file:///path` — and refuses the canonical
4987    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
4988    /// injection footguns at validate time. Maps the predicate's
4989    /// `String` reason verbatim into the
4990    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
4991    /// offending value + parser-shaped reason so the diagnostic is
4992    /// self-locating (the author can grep their `caixa.lisp` for
4993    /// `:repositorio "<value>"` and fix it in one edit).
4994    ///
4995    /// `None` (the canonical "omit the slot to express no published
4996    /// homepage" shape) is accepted trivially — the gate is a no-op
4997    /// when the author didn't declare a value. `Some("")` is gated by
4998    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
4999    /// shape predicate is consulted, mirroring the empty-first cascade
5000    /// every peer per-axis identity gate uses
5001    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
5002    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
5003    /// [`crate::DepError::FonteRepoEmpty`] →
5004    /// [`crate::DepError::FonteRepoInvalid`]).
5005    ///
5006    /// Universal-axis (every kind carries `:repositorio`), so wired at
5007    /// the caixa-build gate alongside the peer universal gates
5008    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5009    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5010    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
5011    /// before the kind-coherence gates
5012    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5013    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5014    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5015    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5016    /// specific slot sets.
5017    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
5018        let Some(s) = self.repositorio() else {
5019            return Ok(());
5020        };
5021        if s.is_empty() {
5022            return Err(ManifestError::RepositorioEmpty);
5023        }
5024        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
5025            repositorio: s.to_string(),
5026            reason,
5027        })
5028    }
5029
5030    /// Reject `:descricao` values that are the empty string. The flat
5031    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
5032    /// free-form-prose homepage axis every kind carries — the
5033    /// substrate routes the same string through two load-bearing
5034    /// consumers in the [`caixa-helm`] renderer:
5035    ///
5036    ///   - `build_chart_yaml` folds it verbatim into the rendered
5037    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
5038    ///     field (`caixa-helm/src/lib.rs:232-235`).
5039    ///   - `build_readme` folds it verbatim into the rendered chart
5040    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
5041    ///
5042    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
5043    /// substitute a `caixa.nome`-derived placeholder when the slot is
5044    /// absent (`None` → the fallback fires); a `Some("")` *skips the
5045    /// fallback* and silently passes the empty string through to
5046    /// `Chart.yaml description: ""` / a blank chart `README.md`
5047    /// header. Helm's chart spec requires a non-empty `description:`
5048    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
5049    /// `WARNING [chart.metadata.description]: description is required`),
5050    /// so the empty `Some("")` silently lands in the rendered
5051    /// artifacts and breaks at `helm lint` / `helm install` time far
5052    /// from the source `caixa.lisp`, with no field naming the
5053    /// offending `:descricao`.
5054    ///
5055    /// `None` (the canonical "omit the slot to defer to the renderer's
5056    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
5057    /// the gate is a no-op when the author didn't declare a value.
5058    /// `Some("")` is gated by the narrower
5059    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
5060    /// shape every peer per-axis empty gate uses
5061    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5062    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5063    /// [`ManifestError::RepositorioEmpty`]).
5064    ///
5065    /// Universal-axis (every kind carries `:descricao`), so wired at
5066    /// the caixa-build gate alongside the peer universal gates
5067    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5068    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5069    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5070    /// [`Self::validate_code_paths`] — before the kind-coherence
5071    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5072    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5073    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5074    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5075    /// specific slot sets.
5076    ///
5077    /// Past the empty arm the gate enforces the chart-description
5078    /// shape predicate via [`crate::render::is_chart_description_shape`]:
5079    /// the structural single-line UTF-8 floor every realistic chart
5080    /// description in the wild matches — 1..=512 bytes, no leading
5081    /// or trailing whitespace, no ASCII control characters anywhere
5082    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
5083    /// carriage return, and every other control byte), Unicode
5084    /// continuation bytes accepted (the canonical fixtures carry
5085    /// `→` and `—`). Closes the canonical paste-from-doc footguns
5086    /// the bare empty-arm gate left open: paste-from-aligned-doc
5087    /// leading / trailing whitespace (`" Checkout flow."`,
5088    /// `"Checkout flow. "`), paste-from-multiline-doc newline
5089    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
5090    /// (`"Checkout\rflow."`), tab-from-aligned-doc
5091    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
5092    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
5093    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
5094    /// [`Self::validate_edicao`] establish past their own empty arms
5095    /// on the sibling universal-axis `Option<String>` Caixa-level
5096    /// value-shape surfaces.
5097    ///
5098    /// The empty-first cascade discipline mirrors every peer per-axis
5099    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
5100    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
5101    /// diagnostic surfaces on `Some("")` rather than the broader
5102    /// shape-predicate diagnostic — peer with how
5103    /// [`ManifestError::LicencaEmpty`] runs before
5104    /// [`ManifestError::LicencaInvalid`],
5105    /// [`ManifestError::EdicaoEmpty`] runs before
5106    /// [`ManifestError::EdicaoInvalid`],
5107    /// [`ManifestError::RepositorioEmpty`] runs before
5108    /// [`ManifestError::RepositorioInvalid`].
5109    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
5110        let Some(s) = self.descricao() else {
5111            return Ok(());
5112        };
5113        if s.is_empty() {
5114            return Err(ManifestError::DescricaoEmpty);
5115        }
5116        crate::render::is_chart_description_shape(s).map_err(|reason| {
5117            ManifestError::DescricaoInvalid {
5118                descricao: s.to_string(),
5119                reason,
5120            }
5121        })?;
5122        Ok(())
5123    }
5124
5125    /// Reject `:licenca` values that are the empty string. The flat
5126    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
5127    /// SPDX-shaped license-expression axis every kind carries — the
5128    /// substrate routes the same string through the [`caixa-helm`]
5129    /// renderer's `build_readme` which folds it verbatim into the
5130    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
5131    /// section (`caixa-helm/src/lib.rs:361`) via
5132    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
5133    /// fallback only fires on `None`; a `Some("")` *skips the
5134    /// fallback* and silently passes the empty string through to a
5135    /// chart `README.md` whose `License` section renders as the bare
5136    /// trailing period (`.\n`) — peer footgun with the
5137    /// `Some("")`-skips-`unwrap_or_else` shape the
5138    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
5139    /// gates close on the sibling free-form-prose and git-URL axes.
5140    ///
5141    /// `None` (the canonical "omit the slot to defer to the
5142    /// renderer's `MIT` fallback" shape every existing fixture
5143    /// carries) is accepted trivially — the gate is a no-op when the
5144    /// author didn't declare a value. `Some("")` is gated by the
5145    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
5146    /// empty-arm shape every peer per-axis empty gate uses
5147    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5148    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5149    /// [`ManifestError::RepositorioEmpty`],
5150    /// [`ManifestError::DescricaoEmpty`]).
5151    ///
5152    /// Universal-axis (every kind carries `:licenca`), so wired at
5153    /// the caixa-build gate alongside the peer universal gates
5154    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5155    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5156    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5157    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
5158    /// — before the kind-coherence gates
5159    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5160    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5161    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5162    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5163    /// specific slot sets.
5164    ///
5165    /// Past the empty arm the gate enforces the SPDX-expression shape
5166    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
5167    /// structural alphabet floor every realistic SPDX expression in
5168    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
5169    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
5170    /// single ASCII space (token separator). Closes the canonical
5171    /// paste-from-doc footguns the bare empty-arm gate left open:
5172    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
5173    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
5174    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
5175    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
5176    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
5177    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
5178    /// Apache-2.0"`), and semicolon-list-separator confusion
5179    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
5180    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
5181    /// establish past their own empty arms.
5182    ///
5183    /// The empty-first cascade discipline mirrors every peer per-axis
5184    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
5185    /// [`ManifestError::LicencaInvalid`], so the narrower empty
5186    /// diagnostic surfaces on `Some("")` rather than the broader
5187    /// shape-predicate diagnostic — peer with how
5188    /// [`ManifestError::EdicaoEmpty`] runs before
5189    /// [`ManifestError::EdicaoInvalid`],
5190    /// [`ManifestError::RepositorioEmpty`] runs before
5191    /// [`ManifestError::RepositorioInvalid`].
5192    ///
5193    /// A future tightening on this axis can extend the alphabet
5194    /// floor into a full SPDX expression parser + license-id
5195    /// allowlist (rejecting alphabet-valid values that don't name a
5196    /// real SPDX license identifier — e.g., `"NotAReal"` is
5197    /// alphabet-valid but no `NotAReal` license-id exists). That
5198    /// parser only becomes meaningful past a real SPDX-spec
5199    /// dependency; this gate establishes the structural floor by
5200    /// refusing every non-SPDX-alphabet value at validate time.
5201    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
5202        let Some(s) = self.licenca() else {
5203            return Ok(());
5204        };
5205        if s.is_empty() {
5206            return Err(ManifestError::LicencaEmpty);
5207        }
5208        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
5209            ManifestError::LicencaInvalid {
5210                licenca: s.to_string(),
5211                reason,
5212            }
5213        })?;
5214        Ok(())
5215    }
5216
5217    /// Reject `:edicao` values that are the empty string. The flat
5218    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
5219    /// language-edition axis every kind carries — it determines the
5220    /// tatara-lisp macro surface + compatibility flags the substrate
5221    /// applies when building a caixa, and lands verbatim in the
5222    /// `Caixa::template` author-time scaffold (the canonical
5223    /// `:edicao "2026"` line every `feira init` emits via
5224    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
5225    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
5226    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
5227    /// `caixa-core/src/render.rs:2510`) via
5228    /// `edicao: Some("2026".into())`.
5229    ///
5230    /// `None` (the canonical "omit the slot to defer to the
5231    /// substrate's default edition" shape every existing
5232    /// [`caixa-resolver`] integration test fixture carries via
5233    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5234    /// is accepted trivially — the gate is a no-op when the author
5235    /// didn't declare a value. `Some("")` is gated by the narrower
5236    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
5237    /// shape every peer per-axis empty gate uses
5238    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5239    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5240    /// [`ManifestError::RepositorioEmpty`],
5241    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
5242    ///
5243    /// Universal-axis (every kind carries `:edicao`), so wired at
5244    /// the caixa-build gate alongside the peer universal gates
5245    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5246    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5247    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5248    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5249    /// [`Self::validate_code_paths`] — before the kind-coherence
5250    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5251    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5252    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5253    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5254    /// specific slot sets.
5255    ///
5256    /// Past the empty arm the gate enforces the canonical year-shape
5257    /// predicate: every documented tatara-lisp edition is a 4-digit
5258    /// ASCII decimal year (`"2026"` is the only edition currently
5259    /// minted; future-introduced siblings will follow the same
5260    /// shape, peer with Cargo's `[package] edition` grammar which
5261    /// every value Cargo has ever accepted matches — `"2015"`,
5262    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
5263    /// 4 ASCII decimal bytes is rejected with the narrower
5264    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
5265    /// shape-predicate cascade [`Self::validate_repositorio`]
5266    /// establishes past its own empty arm
5267    /// ([`ManifestError::RepositorioEmpty`] →
5268    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
5269    /// paste-from-doc footguns the bare empty-arm gate left open:
5270    ///
5271    ///   - leading / trailing whitespace from a paste-from-doc
5272    ///     (`"2026 "`, `" 2026"`)
5273    ///   - control characters / CRLF from a paste-from-multiline-doc
5274    ///     (`"2026\n"`)
5275    ///   - non-ASCII look-alikes from a fullwidth keyboard
5276    ///     (`"2026"`) which would silently land as a non-ASCII
5277    ///     string in the rendered caixa.lisp
5278    ///   - free-form non-year values (`"x"`, `"latest"`,
5279    ///     `"nightly"`) that have no operational meaning on the
5280    ///     substrate's build-time edition selector
5281    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
5282    ///     `"r2026"`) — common version-tag idioms that don't apply
5283    ///     to the year-shaped edition axis
5284    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
5285    ///     edition is a year, not a fractional version
5286    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
5287    ///     `"00026"`) that don't name a year
5288    ///
5289    /// `None` (the canonical "omit the slot to defer to the
5290    /// substrate's default edition" shape every existing
5291    /// [`caixa-resolver`] integration test fixture carries via
5292    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5293    /// is accepted trivially — the gate is a no-op when the author
5294    /// didn't declare a value. The empty-first cascade discipline
5295    /// mirrors every peer per-axis identity gate:
5296    /// [`ManifestError::EdicaoEmpty`] runs before
5297    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
5298    /// diagnostic surfaces on `Some("")` rather than the broader
5299    /// shape-predicate diagnostic — peer with how
5300    /// [`ManifestError::NomeEmpty`] runs before
5301    /// [`ManifestError::NomeInvalid`],
5302    /// [`ManifestError::VersaoEmpty`] runs before
5303    /// [`ManifestError::VersaoInvalid`],
5304    /// [`ManifestError::RepositorioEmpty`] runs before
5305    /// [`ManifestError::RepositorioInvalid`].
5306    ///
5307    /// A future tightening on this axis can extend the shape
5308    /// predicate into a known-edition allowlist (rejecting
5309    /// year-shaped values that don't name a tatara-lisp edition
5310    /// the substrate actually understands — e.g., `"1999"` is
5311    /// year-shaped but no `1999` edition exists). That allowlist
5312    /// only becomes meaningful past the introduction of a sibling
5313    /// edition to `"2026"`; this gate establishes the structural
5314    /// floor by refusing every non-year-shaped value at validate
5315    /// time.
5316    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
5317        let Some(s) = self.edicao() else {
5318            return Ok(());
5319        };
5320        if s.is_empty() {
5321            return Err(ManifestError::EdicaoEmpty);
5322        }
5323        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
5324            return Err(ManifestError::EdicaoInvalid {
5325                edicao: s.to_string(),
5326                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
5327            });
5328        }
5329        Ok(())
5330    }
5331
5332    /// Compose the supervisor-related flat slots into a single
5333    /// [`SupervisorSpec`] for validation. Returns `None` when the
5334    /// caixa isn't a `:kind Supervisor`.
5335    ///
5336    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
5337    /// simple (one form, no nested `:supervisor (…)` block); this view
5338    /// is the "typed shape" the operator + supervisor reconciler
5339    /// consume.
5340    #[must_use]
5341    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
5342        if !self.kind().is_supervisor() {
5343            return None;
5344        }
5345        // Fold through the shared `supervisor::duration_codec::parse`
5346        // — the same parser the serde-routed `with = "duration_codec"`
5347        // on `SupervisorSpec::restart_window`, the `:politicas
5348        // :timeout` codec, and the `:politicas :circuit-breaker
5349        // :window` codec all consume. The prior inline f64-shaped
5350        // duplicate (`parse_window_inline`) admitted every magnitude
5351        // the integer-magnitude gate (1c55a2a) rejects on the three
5352        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
5353        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
5354        // `None` (i.e. "no reset"), divergent from the shared codec's
5355        // integer-magnitude discipline by construction. The fold
5356        // closes the divergence: every value the typed
5357        // `SupervisorSpec` carries past `supervisor_view` is in the
5358        // shared codec's accepted set. The `.ok()` here preserves the
5359        // existing soft-swallow shape on this view-construction path;
5360        // the new [`Caixa::validate_restart_window`] (sibling of
5361        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
5362        // the offending raw string at build time so authoring tools
5363        // (`feira lint`, the future layout-side wire-up) surface a
5364        // self-locating diagnostic instead of a silently dropped
5365        // window.
5366        let restart_window = self
5367            .restart_window()
5368            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
5369        Some(SupervisorSpec {
5370            // Route the author-omitted `:estrategia` arm through the
5371            // substrate-canonical
5372            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
5373            // `pub const` rather than the transitively-derived
5374            // [`RestartStrategy::default`] route the prior
5375            // `.unwrap_or_default()` fold reached for — one source of
5376            // truth for the Erlang/OTP `one_for_one` half of Learn You
5377            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
5378            // supervisor canonical default that also backs the
5379            // [`crate::supervisor::Default for RestartStrategy`] impl
5380            // and the [`crate::supervisor::Default for SupervisorSpec`]
5381            // impl's struct-literal `estrategia` field, all now routed
5382            // through the same lifted constant. Prior to the lift the
5383            // composition site carried `.unwrap_or_default()` with no
5384            // compile-time link back to the shared OTP-canonical
5385            // default that the peer paired
5386            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
5387            // arm on the sibling `:max-restarts` axis routes through —
5388            // so a future rebrand of the OTP-canonical strategy default
5389            // (a widening to `rest_for_one` once the substrate
5390            // discovers startup-order-coupled child cohorts as the more
5391            // common shape, a per-cluster overlay the operator pins
5392            // through the MESH-COMPOSITION §III.2 supervision-canary
5393            // `:estrategia-overrides` roadmap slot) would have had to
5394            // migrate the paired `MaxIntensity` + `Period` halves
5395            // through the lifted constants and the `one_for_one` half
5396            // through a `RestartStrategy::default()` route in lockstep
5397            // or the three halves of the same OTP-canonical default
5398            // would silently drift out of pairing. Byte-parity against
5399            // the lifted constant closes the split. Pinned by
5400            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
5401            // in the tests module.
5402            estrategia: self
5403                .estrategia()
5404                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
5405            // Route the author-omitted `:max-restarts` arm through the
5406            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
5407            // typed `pub const` rather than the raw `5` literal — one
5408            // source of truth for the Erlang/OTP-canonical
5409            // `{intensity, 5, 60}` `MaxIntensity` default that also
5410            // backs the serde-side wire-format author-omitted arm on
5411            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
5412            // `#[serde(default = "default_max_restarts")]` and the
5413            // [`Default for SupervisorSpec`] impl's struct-literal
5414            // default field. Prior to the lift the composition site
5415            // carried a raw `5` with no compile-time link back to the
5416            // serde-side default, so a future rebrand of the OTP-
5417            // canonical default (a tightening to Elixir's `3`, a
5418            // widening to a per-cluster overlay the operator pins
5419            // through the MESH-COMPOSITION §III.2 supervision-canary
5420            // `:supervisor :max-restarts-overrides` roadmap slot)
5421            // would have had to be threaded through both open-coded
5422            // copies in lockstep or the wire-format author-omitted arm
5423            // and this view-construction author-omitted arm would
5424            // silently disagree on which restart-budget an omitted
5425            // `:max-restarts` resolves to. Pinned by
5426            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
5427            // in the tests module.
5428            max_restarts: self
5429                .max_restarts()
5430                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
5431            restart_window,
5432            children: self.children().to_vec(),
5433        })
5434    }
5435
5436    /// A minimal starter manifest emitted by `feira init`.
5437    #[must_use]
5438    pub fn template(nome: &str) -> String {
5439        format!(
5440            "(defcaixa\n  \
5441               :nome        {nome:?}\n  \
5442               :versao      \"0.1.0\"\n  \
5443               :kind        Biblioteca\n  \
5444               :edicao      \"2026\"\n  \
5445               :descricao   \"FIXME — describe this caixa\"\n  \
5446               :autores     ()\n  \
5447               :etiquetas   ()\n  \
5448               :deps        ()\n  \
5449               :deps-dev    ()\n  \
5450               :bibliotecas (\"lib/{nome}.lisp\"))\n"
5451        )
5452    }
5453
5454    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
5455    /// back after mutation (e.g. `feira add`).
5456    ///
5457    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
5458    /// The derive-macro `compile_from_sexp` path is the inverse, so any
5459    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
5460    #[must_use]
5461    pub fn to_lisp(&self) -> String {
5462        let json = serde_json::to_value(self).expect("Caixa serialize");
5463        let sexp = tatara_lisp::domain::json_to_sexp(&json);
5464        let tatara_lisp::Sexp::List(items) = sexp else {
5465            return format!("(defcaixa {sexp})\n");
5466        };
5467        let mut out = String::from("(defcaixa");
5468        let mut i = 0;
5469        while i + 1 < items.len() {
5470            out.push_str("\n  ");
5471            out.push_str(&items[i].to_string());
5472            out.push(' ');
5473            out.push_str(&items[i + 1].to_string());
5474            i += 2;
5475        }
5476        out.push_str(")\n");
5477        out
5478    }
5479}
5480
5481/// Errors raised by top-level [`Caixa`] validators that don't fit
5482/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
5483/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
5484/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
5485/// through every substrate-side artifact's `metadata.name` /
5486/// version derivation.
5487///
5488/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
5489/// doc-comment anticipates) can hold one of each per-axis error
5490/// family without reshaping individual diagnostics; this enum is
5491/// the first such per-Caixa-identity family.
5492#[derive(Debug, Error, PartialEq, Eq)]
5493pub enum ManifestError {
5494    #[error(
5495        ":nome is empty (every caixa must name itself; the value flows \
5496         into every K8s artifact's `metadata.name` derivation and into \
5497         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5498    )]
5499    NomeEmpty,
5500    #[error(
5501        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5502         apiserver enforces this rule on every `metadata.name` the \
5503         caixa's substrate-side renderers derive from `:nome` — the \
5504         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5505         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5506         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5507         name; use a lowercase alphanumeric + hyphen identifier like \
5508         `\"checkout\"` or `\"cart-v2\"`)"
5509    )]
5510    NomeInvalid { nome: String, reason: String },
5511    #[error(
5512        ":nome {nome:?} overflows the joint-length budget on the canonical \
5513         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
5514         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
5515         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
5516         `chart:` slot, `caixa-tatara`'s `release_name` + \
5517         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
5518         joint name through the canonical `lareira_chart_name` helper, and \
5519         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
5520         DNS-1123 label cap on every chart-name-derived `metadata.name` \
5521         reject any joint name exceeding 63 bytes; the narrower \
5522         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
5523         arm gates the chart-name budget downstream renderers inherit)"
5524    )]
5525    NomeChartNameBudgetExceeded { nome: String, reason: String },
5526    #[error(
5527        ":versao is empty (every caixa must pin its own version; the value flows \
5528         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
5529         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
5530         `:latest` tags, the lacre closure's `concrete_versao`, and the \
5531         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
5532    )]
5533    VersaoEmpty,
5534    #[error(
5535        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
5536         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
5537         with optional `-prerelease` and `+build` — across every artifact derived \
5538         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
5539         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
5540         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
5541         and the `:upgrade-from :from` peers that match against this exact shape; \
5542         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
5543         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
5544         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
5545    )]
5546    VersaoInvalid { versao: String, reason: String },
5547    #[error(
5548        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
5549         substrate consumes this string through the shared \
5550         `supervisor::duration_codec` — the same parser routed via `with = \
5551         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
5552         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
5553         the canonical authoring form is `<integer><unit>` where the unit is one \
5554         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
5555         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
5556         Without this gate a malformed `:restart-window` silently produced a \
5557         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
5558         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
5559         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
5560         layer with the offending value named verbatim. Omit the slot entirely to \
5561         express \"no reset\"; carry a positive integer duration to express the \
5562         sliding window)"
5563    )]
5564    RestartWindowMalformed {
5565        restart_window: String,
5566        reason: String,
5567    },
5568    #[error(
5569        "{slot} entry is an empty path string — every {slot} entry must name \
5570         a file relative to the caixa root; omit the entry to omit the file \
5571         (the layout checker's `root.join(\"\")` resolves to the caixa root \
5572         itself, so an empty entry silently aliases the project root as a \
5573         declared {slot} file, then fails downstream at parse / existence \
5574         time with a diagnostic that names the root rather than the offending \
5575         entry)"
5576    )]
5577    CodePathEmpty { slot: &'static str },
5578    #[error(
5579        "{slot} entry {} is an absolute path — entries must be relative to \
5580         the caixa root, since `Path::join` replaces the base with an absolute \
5581         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
5582         outside the caixa root sandbox; rewrite the entry as a relative path \
5583         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
5584         `\"servicos/<name>.computeunit.yaml\"`)",
5585        path.display()
5586    )]
5587    CodePathAbsolute { slot: &'static str, path: PathBuf },
5588    #[error(
5589        "{slot} entry {} contains a `..` component — entries must not traverse \
5590         above the caixa root (the layout's `starts_with(<dir>)` fence on \
5591         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
5592         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
5593         has no such fence, so a leading `..` escapes unconditionally if the \
5594         resolved target happens to exist)",
5595        path.display()
5596    )]
5597    CodePathParentEscape { slot: &'static str, path: PathBuf },
5598    #[error(
5599        "{slot} entry {} does not terminate in the `.lisp` extension — every \
5600         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
5601         loop reads through `tatara_lisp::read` at parse time, so any other \
5602         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
5603         structurally a parser error far from the source caixa.lisp, with \
5604         no field naming the offending `:bibliotecas` entry. Pin a relative \
5605         path under the caixa root whose terminating extension is \
5606         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
5607         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
5608         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
5609         (33cc830) axes already carry through the same lifted \
5610         `is_lisp_extension` predicate",
5611        path.display()
5612    )]
5613    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
5614    #[error(
5615        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
5616         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
5617         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
5618         through `serde_yaml::from_str` at chart / FluxCD bundle render \
5619         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
5620         off-by-one-segment `.computeunit-yaml`, the editor-backup \
5621         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
5622         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
5623         source caixa.lisp, with no field naming the offending `:servicos` \
5624         entry. Pin a relative path under the caixa root whose terminating \
5625         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
5626         `\"servicos/<name>.computeunit.yaml\"`, \
5627         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
5628         contract the sibling `:bibliotecas` axis (64772a9) already carries \
5629         on the tatara-lisp-source axis through the peer lifted \
5630         `is_lisp_extension` predicate, here on the compound-suffix axis \
5631         `Path::extension` can't express on its own through the lifted \
5632         `is_computeunit_yaml_extension` predicate",
5633        path.display()
5634    )]
5635    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
5636    #[error(
5637        "{slot} entry {} appears more than once (the code-path list is \
5638         a set, not a multiset; every peer Vec-shaped author-supplied \
5639         list past validate is set-not-multiset — `:membros :caixa`, \
5640         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5641         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
5642         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
5643         code-path lists are the last Vec-shaped author-supplied slots on \
5644         the typed Caixa surface still admitting a duplicate entry. \
5645         `:bibliotecas` duplicates re-parse the same file at \
5646         `feira build` time and silently mask the author's intent to \
5647         declare a *second* biblioteca; `:exe` duplicates collide on the \
5648         flake `packages.<name>` derivation key at the future \
5649         `caixa-flake` materializer; `:servicos` duplicates surface as the \
5650         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
5651         rejection far from the source `caixa.lisp`. Drop the duplicate \
5652         or rename it to the actual second file intended)",
5653        path.display()
5654    )]
5655    CodePathDuplicate { slot: &'static str, path: PathBuf },
5656    #[error(
5657        ":etiquetas entry is empty (every tag must carry a non-empty \
5658         registry-search identifier; the empty entry has no operational \
5659         meaning — it indexes nothing in the future caixa-registry search \
5660         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
5661         with a no-op tag; omit the entry to express \"no tag on this \
5662         position\")"
5663    )]
5664    EtiquetaEmpty,
5665    #[error(
5666        ":etiquetas entry {etiqueta:?} appears more than once (the \
5667         registry-search tag set is a set, not a multiset; duplicate \
5668         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
5669         at chart render — a \"second wins / one silently disappears\" \
5670         shape divergent from every peer typed-graph set gate \
5671         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
5672         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
5673         duplicate or rename it to the actual tag intended)"
5674    )]
5675    EtiquetaDuplicate { etiqueta: String },
5676    #[error(
5677        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
5678         {reason} (the substrate consumes this string through the shared \
5679         `crate::render::is_chart_keyword_shape` predicate — the same \
5680         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
5681         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
5682         continuation. The canonical authoring shapes are short kebab-case \
5683         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
5684         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
5685         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
5686         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
5687         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
5688         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
5689         `\"mesh,http,grpc\"` — the author meant to author three separate \
5690         list entries; path-separator confusion `\"caixa/servico\"`; \
5691         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
5692         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
5693         `\"café\"` — every legitimate search tag is strict ASCII; \
5694         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
5695         passed `from_lisp` + `validate_etiquetas` + \
5696         `StandardLayout::verify` and landed in the rendered \
5697         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
5698         malformed search tag — Artifact Hub's keyword index + the future \
5699         caixa-registry's keyword index would either silently drop the \
5700         tag or fail to index it far from the source caixa.lisp; the gate \
5701         moves the diagnostic to the manifest layer with the offending \
5702         value named verbatim)"
5703    )]
5704    EtiquetaInvalid { etiqueta: String, reason: String },
5705    #[error(
5706        ":autores entry is empty (every maintainer must carry a non-empty \
5707         identifier; the empty entry has no operational meaning — it \
5708         identifies no one in the substrate's authorship index and renders \
5709         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
5710         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
5711         omit the entry to express \"no maintainer on this position\")"
5712    )]
5713    AutorEmpty,
5714    #[error(
5715        ":autores entry {autor:?} appears more than once (the maintainer \
5716         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
5717         `maintainers:` rendering does *no* dedup — duplicate entries \
5718         stack verbatim in `Chart.yaml` as two identical \
5719         `Maintainer {{ name, email: None }}` records, divergent from every \
5720         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
5721         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
5722         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
5723         rename it to the actual author intended)"
5724    )]
5725    AutorDuplicate { autor: String },
5726    #[error(
5727        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
5728         {reason} (the substrate consumes this string through the shared \
5729         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
5730         single-line-UTF-8 floor every realistic chart maintainer name carries: \
5731         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
5732         characters anywhere, Unicode bytes accepted. The canonical authoring \
5733         shapes are short single-line identifiers like `\"pleme-io\"`, \
5734         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
5735         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
5736         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
5737         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
5738         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
5739         records into one entry instead of splitting into one entry per author; \
5740         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
5741         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
5742         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5743         `validate_autores` + `StandardLayout::verify` and landed in the \
5744         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
5745         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
5746         round-trip — every chart-aware UI (`helm list`, `helm search`, \
5747         Artifact Hub maintainer index) would render the maintainer name in a \
5748         single-line column far from the source caixa.lisp; the gate moves the \
5749         diagnostic to the manifest layer with the offending value named \
5750         verbatim)"
5751    )]
5752    AutorInvalid { autor: String, reason: String },
5753    #[error(
5754        ":repositorio is the empty string (every published caixa names its \
5755         git source via a non-empty `:repositorio` locator — the value \
5756         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
5757         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
5758         `GitRepository.spec.url` via `caixa-flux`'s \
5759         `ClusterBundleOpts::for_caixa`; both consumers' \
5760         `Option::unwrap_or_else` fallbacks only fire when the slot is \
5761         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
5762         `url: \"\"` in the rendered artifacts and breaks at `helm \
5763         template` / FluxCD source-controller reconcile time far from the \
5764         source caixa.lisp; omit the slot entirely to defer to the \
5765         renderer's `https://github.com/pleme-io/<nome>` / \
5766         `caixa.nome`-derived fallback, or carry a canonical authoring \
5767         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
5768         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
5769         `\"file:///path\"`)"
5770    )]
5771    RepositorioEmpty,
5772    #[error(
5773        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
5774         (the substrate consumes this string through the shared \
5775         `crate::render::is_git_repo_url` predicate — the same parser the \
5776         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
5777         value through via `DepSource::validate`; the canonical authoring \
5778         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
5779         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
5780         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
5781         scp-style SSH form. Without this gate a malformed `:repositorio` \
5782         (whitespace from a paste-from-doc; control characters / CRLF \
5783         from a paste-from-multiline-doc; a leading `-` from a \
5784         CLI-argument-injection footgun; a missing `:` separator from a \
5785         bare `org/repo` shape git treats as a relative filesystem path) \
5786         silently landed in the rendered `Chart.yaml home:` and the \
5787         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
5788         FluxCD reconcile time far from the source caixa.lisp; the gate \
5789         moves the diagnostic to the manifest layer with the offending \
5790         value named verbatim)"
5791    )]
5792    RepositorioInvalid { repositorio: String, reason: String },
5793    #[error(
5794        ":descricao is the empty string (every published caixa names \
5795         its purpose via a non-empty `:descricao` summary — the value \
5796         flows verbatim into the rendered `lareira-<nome>` Helm \
5797         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
5798         `build_chart_yaml` and into the chart `README.md` header via \
5799         `build_readme`; both consumers' `Option::unwrap_or_else` \
5800         `caixa.nome`-derived fallbacks only fire when the slot is \
5801         `None`, so an empty `Some(\"\")` silently lands as \
5802         `description: \"\"` / a blank `README.md` header in the \
5803         rendered artifacts and breaks at `helm lint` time \
5804         (`WARNING [chart.metadata.description]: description is \
5805         required` on `apiVersion: v2` charts) far from the source \
5806         caixa.lisp; omit the slot entirely to defer to the \
5807         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
5808         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
5809         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
5810         Servico.\"`)"
5811    )]
5812    DescricaoEmpty,
5813    #[error(
5814        ":descricao {descricao:?} is not a valid chart-description shape: \
5815         {reason} (the substrate consumes this string through the shared \
5816         `crate::render::is_chart_description_shape` predicate — the same \
5817         single-line-UTF-8 floor every realistic chart description carries: \
5818         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
5819         characters anywhere, Unicode prose bytes accepted. The canonical \
5820         authoring shapes are short single-line summaries like `\"Canonical \
5821         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
5822         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
5823         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
5824         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
5825         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
5826         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
5827         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
5828         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
5829         `validate_descricao` + `StandardLayout::verify` and landed in the \
5830         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
5831         field + `README.md` header paragraph as a YAML-illegal multi-line \
5832         scalar or a silently-trimmed whitespace round-trip — every \
5833         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
5834         render the description in a single-line column far from the source \
5835         caixa.lisp; the gate moves the diagnostic to the manifest layer \
5836         with the offending value named verbatim)"
5837    )]
5838    DescricaoInvalid { descricao: String, reason: String },
5839    #[error(
5840        ":licenca is the empty string (every published caixa names \
5841         its license via a non-empty `:licenca` SPDX expression — the \
5842         value flows verbatim into the rendered `lareira-<nome>` Helm \
5843         chart's `README.md` `## License` section via `caixa-helm`'s \
5844         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
5845         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
5846         only fires when the slot is `None`, so an empty `Some(\"\")` \
5847         silently lands as a bare trailing period in the rendered \
5848         chart `README.md` `License` section far from the source \
5849         caixa.lisp; omit the slot entirely to defer to the \
5850         renderer's `MIT` fallback, or carry a canonical SPDX \
5851         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
5852         `\"Apache-2.0 OR MIT\"`)"
5853    )]
5854    LicencaEmpty,
5855    #[error(
5856        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
5857         (the substrate consumes this string through the shared \
5858         `crate::render::is_spdx_expression_shape` predicate — the same \
5859         alphabet-floor parser every peer per-axis value-shape gate routes \
5860         its value through; the canonical authoring shapes are single \
5861         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
5862         compound expressions like `\"Apache-2.0 OR MIT\"`, \
5863         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
5864         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
5865         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
5866         like `\"LicenseRef-MyLicense\"` / \
5867         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
5868         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
5869         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
5870         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
5871         a smart-quote paste; underscore-instead-of-hyphen typo \
5872         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
5873         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
5874         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
5875         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
5876         `README.md` `## License` section + a future SPDX-aware \
5877         `Chart.yaml license:` emitter would refuse the value at \
5878         `helm lint` time far from the source caixa.lisp; the gate moves \
5879         the diagnostic to the manifest layer with the offending value \
5880         named verbatim)"
5881    )]
5882    LicencaInvalid { licenca: String, reason: String },
5883    #[error(
5884        ":edicao is the empty string (every published caixa names \
5885         its language edition via a non-empty `:edicao` value — the \
5886         edition determines the tatara-lisp macro surface + \
5887         compatibility flags the substrate applies when building \
5888         the caixa; the canonical `Caixa::template` scaffold every \
5889         `feira init` emits carries `:edicao \"2026\"` verbatim and \
5890         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
5891         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
5892         construction, so an empty `Some(\"\")` silently lands as a \
5893         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
5894         a future renderer-side consumer that folds it through \
5895         `Option::unwrap_or_else` will skip the fallback and pass the \
5896         empty edition through to the substrate's build-time edition \
5897         selector far from the source caixa.lisp; omit the slot \
5898         entirely to defer to the substrate's default edition, or \
5899         carry a canonical edition like `\"2026\"`)"
5900    )]
5901    EdicaoEmpty,
5902    #[error(
5903        ":edicao {edicao:?} is not a valid edition: {reason} (every \
5904         documented tatara-lisp edition is a 4-digit ASCII decimal \
5905         year — `\"2026\"` is the only edition currently minted; \
5906         future-introduced siblings will follow the same shape, peer \
5907         with Cargo's `[package] edition` grammar which every value \
5908         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
5909         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
5910         paste-from-doc footguns silently passed: a trailing space \
5911         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
5912         from a paste-from-multiline-doc, a fullwidth-keyboard \
5913         look-alike (`\"2026\"`), a free-form non-year value \
5914         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
5915         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
5916         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
5917         wrong-length numeric value (`\"26\"`, `\"202\"`, \
5918         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
5919         rendered caixa.lisp and broke at the substrate's \
5920         build-time edition selector far from the source caixa.lisp; \
5921         omit the slot entirely to defer to the substrate's default \
5922         edition, or carry a canonical 4-digit ASCII decimal year \
5923         like `\"2026\"`)"
5924    )]
5925    EdicaoInvalid { edicao: String, reason: String },
5926}
5927
5928#[cfg(test)]
5929mod tests {
5930    use super::*;
5931
5932    #[test]
5933    fn template_round_trips() {
5934        let src = Caixa::template("demo");
5935        let c = Caixa::from_lisp(&src).expect("template must parse");
5936        assert_eq!(c.nome, "demo");
5937        assert_eq!(c.versao, "0.1.0");
5938        assert_eq!(c.kind, CaixaKind::Biblioteca);
5939        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
5940        assert!(c.deps.is_empty());
5941        assert!(c.deps_dev.is_empty());
5942    }
5943
5944    #[test]
5945    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
5946        // Fail-before-pass-after pin on [`Caixa::nome`] +
5947        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
5948        // accessor projects the top-level manifest's per-`:nome` /
5949        // per-`:versao` [`String`] storage through the `pub const fn`
5950        // [`String::as_str`] (const-stable since Rust 1.87, well within
5951        // the workspace MSRV) — any future accidental downgrade to
5952        // non-`const` fails the corresponding `<name>_via_const_fn`
5953        // wrapper at caixa-core build time with E0015 (`cannot call
5954        // non-const method`), strictly stronger than a runtime
5955        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
5956        // scalar-accessor family pins on the sibling `const`-eval-
5957        // surface passes ([`crate::CaixaVersion::as_str`] at the
5958        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
5959        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
5960        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
5961        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
5962        // axis, [`crate::supervisor::ChildSpec::nome`] /
5963        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
5964        // M2 supervisor-tree axis,
5965        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
5966        // upgrade axis, [`crate::dep::Dep::nome`] /
5967        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
5968        // axis, and the per-`:contratos`
5969        // [`crate::aplicacao::WitContract::source`] /
5970        // [`crate::aplicacao::WitContract::destination`] /
5971        // [`crate::aplicacao::WitContract::world_ref`] trio the
5972        // sibling pin at 279823b already anchors).
5973        const fn nome_via_const_fn(c: &Caixa) -> &str {
5974            c.nome()
5975        }
5976        const fn versao_via_const_fn(c: &Caixa) -> &str {
5977            c.versao()
5978        }
5979        let src = Caixa::template("demo");
5980        let c = Caixa::from_lisp(&src).expect("template must parse");
5981        assert_eq!(nome_via_const_fn(&c), c.nome());
5982        assert_eq!(versao_via_const_fn(&c), c.versao());
5983        assert_eq!(c.nome(), "demo");
5984        assert_eq!(c.versao(), "0.1.0");
5985    }
5986
5987    #[test]
5988    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
5989        // Fail-before-pass-after pin on the five per-`Caixa`
5990        // `Option<String> → Option<&str>` scalar accessors
5991        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
5992        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
5993        // manifest's optional universal-axis surface, plus
5994        // [`Caixa::restart_window`] on the M2 supervisor-tree
5995        // per-`SupervisorSpec` peer raw-window-string projection axis).
5996        // Each accessor destructures the typed slot's `Option<String>`
5997        // storage through the `match &self.<field> { Some(s) =>
5998        // Some(s.as_str()), None => None }` shape — routing through
5999        // [`String::as_str`] (const-stable since Rust 1.87, well within
6000        // the workspace MSRV) rather than the non-const
6001        // [`Option::as_deref`] the pre-lift bodies carried — and any
6002        // future accidental downgrade to non-`const` fails the
6003        // corresponding `<name>_via_const_fn` wrapper at caixa-core
6004        // build time with E0015 (`cannot call non-const method`),
6005        // strictly stronger than a runtime `assert!` and strictly
6006        // stronger than a module-scope `const _: () = assert!(…)` pin
6007        // (which cannot be formed on a `&Caixa` fixture because the
6008        // type's `String` / `Option<String>` carriers rule out
6009        // `const`-context value construction; the `const fn` wrapper
6010        // is the load-bearing shape that side-steps the destructor-in-
6011        // const restriction on the value axis while still pinning the
6012        // `const`-fn posture on the callee — mirror of the sibling
6013        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
6014        // pin's discipline verbatim on the peer non-`Option`
6015        // `String → &str` axis at the same struct).
6016        //
6017        // Peer of the sibling per-M2/M3-slot `Option<String> →
6018        // Option<&str>` accessor family pin
6019        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
6020        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
6021        // [`WitContract::subject`] / [`WitContract::slot`] on the
6022        // per-`:contratos` payload-carrier trio,
6023        // [`Placement::shard_key`] / [`Placement::affinity`] on the
6024        // per-`:placement` optional-scalar pair).
6025        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
6026            c.licenca()
6027        }
6028        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
6029            c.repositorio()
6030        }
6031        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
6032            c.descricao()
6033        }
6034        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
6035            c.edicao()
6036        }
6037        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
6038            c.restart_window()
6039        }
6040        // Sweep both the `Some`-carrying arm (author-declared slot,
6041        // the byte-string projection payload) and the `None`-carrying
6042        // arm (author-omitted slot, the default-path projection) on
6043        // every accessor so the `const fn` wrapper family pins each
6044        // axis's canonical two-arm partition through the same const
6045        // dispatch as the runtime path.
6046        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6047        c1.licenca = Some("MIT".to_string());
6048        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
6049        c1.descricao = Some("demo caixa".to_string());
6050        c1.edicao = Some("2024".to_string());
6051        c1.restart_window = Some("60s".to_string());
6052        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
6053        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
6054        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
6055        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
6056        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
6057        assert_eq!(c1.licenca(), Some("MIT"));
6058        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
6059        assert_eq!(c1.descricao(), Some("demo caixa"));
6060        assert_eq!(c1.edicao(), Some("2024"));
6061        assert_eq!(c1.restart_window(), Some("60s"));
6062        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6063        c2.licenca = None;
6064        c2.repositorio = None;
6065        c2.descricao = None;
6066        c2.edicao = None;
6067        c2.restart_window = None;
6068        assert_eq!(licenca_via_const_fn(&c2), None);
6069        assert_eq!(repositorio_via_const_fn(&c2), None);
6070        assert_eq!(descricao_via_const_fn(&c2), None);
6071        assert_eq!(edicao_via_const_fn(&c2), None);
6072        assert_eq!(restart_window_via_const_fn(&c2), None);
6073    }
6074
6075    #[test]
6076    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
6077        // Fail-before-pass-after pin on the two outer-[`Caixa`]
6078        // `Copy`-return accessors — [`Caixa::kind`] on the required
6079        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
6080        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
6081        // axis. Both accessors project a `Copy`-carrier field
6082        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
6083        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
6084        // `Option<RestartStrategy>: Copy`) by value through a bare
6085        // `self.<field>` field-access — no dispatch, no destructor, no
6086        // heap. Any future accidental downgrade to non-`const` fails
6087        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
6088        // build time with E0015 (`cannot call non-const method`),
6089        // strictly stronger than a runtime `assert!` and strictly
6090        // stronger than a module-scope `const _: () = assert!(…)` pin
6091        // (which cannot be formed on a `&Caixa` fixture because the
6092        // type's `String` / `Vec` / `Option<Composite>` carriers rule
6093        // out `const`-context value construction; the `const fn`
6094        // wrapper is the load-bearing shape that side-steps the
6095        // destructor-in-const restriction on the value axis while still
6096        // pinning the `const`-fn posture on the callee — mirror of the
6097        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
6098        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
6099        // pins' discipline verbatim on the peer outer-`Caixa`
6100        // `String → &str` + `Option<String> → Option<&str>` axes at the
6101        // same struct).
6102        //
6103        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
6104        // family on the inner-altitude nested-spec typed-slot
6105        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
6106        // + [`crate::supervisor::ChildSpec::restart`] on the M2
6107        // supervisor-tree axis (pinned at 152c868), and
6108        // [`crate::aplicacao::Placement::estrategia`] +
6109        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
6110        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
6111        // unlifted altitude for the `Copy`-return-accessor family.
6112        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
6113            c.kind()
6114        }
6115        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
6116            c.estrategia()
6117        }
6118        // Sweep every arm of both discriminant partitions the accessors
6119        // fan on — every [`CaixaKind`] variant the six-arm required
6120        // discriminant carries (Biblioteca / Binario / Servico /
6121        // Supervisor / Aplicacao / Acao) and both arms of the
6122        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
6123        // (`Some(<strategy>)` on an author-declared supervisor and
6124        // `None` on the author-omitted default arm every non-Supervisor
6125        // caixa carries by `#[serde(default)]`) — so the `const fn`
6126        // wrapper family pins the closed-set partition through the
6127        // same const dispatch as the runtime path.
6128        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6129        c1.kind = CaixaKind::Servico;
6130        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
6131        assert_eq!(kind_via_const_fn(&c1), c1.kind());
6132        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
6133        assert_eq!(c1.kind(), CaixaKind::Servico);
6134        assert_eq!(
6135            c1.estrategia(),
6136            Some(crate::supervisor::RestartStrategy::OneForAll)
6137        );
6138        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6139        c2.kind = CaixaKind::Aplicacao;
6140        c2.estrategia = None;
6141        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
6142        assert_eq!(estrategia_via_const_fn(&c2), None);
6143        // Anchor the remaining discriminant arms so any future
6144        // reordering of [`CaixaKind`]'s six-variant enum surfaces
6145        // through the wrapper dispatch, not just through the direct
6146        // method call.
6147        for kind in [
6148            CaixaKind::Biblioteca,
6149            CaixaKind::Binario,
6150            CaixaKind::Servico,
6151            CaixaKind::Supervisor,
6152            CaixaKind::Aplicacao,
6153            CaixaKind::Acao,
6154        ] {
6155            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6156            c.kind = kind;
6157            assert_eq!(kind_via_const_fn(&c), kind);
6158        }
6159    }
6160
6161    #[test]
6162    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
6163        // Fail-before-pass-after pin on the five outer-[`Caixa`]
6164        // `Vec<String> → &[String]` slice-return accessors on the
6165        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
6166        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
6167        // Each body is a bare `self.<field>.as_slice()` dispatch through
6168        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
6169        // the workspace MSRV). Any future accidental downgrade to
6170        // non-`const` fails the corresponding `<name>_via_const_fn`
6171        // wrapper at caixa-core build time with E0015 (`cannot call
6172        // non-const method`) — mirror of the sibling
6173        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
6174        // discipline on the peer outer-`Caixa` `Copy`-return accessor
6175        // axis, and peer of the sibling composite-carrier slice-return
6176        // pin below on the peer outer-`Caixa` composite-slice axis.
6177        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
6178            c.autores()
6179        }
6180        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
6181            c.etiquetas()
6182        }
6183        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
6184            c.bibliotecas()
6185        }
6186        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
6187            c.exe()
6188        }
6189        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
6190            c.servicos()
6191        }
6192        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
6193        // `servicos` — the template's `Vec::new()` default) and the
6194        // populated arm (mutated below) on every accessor so the
6195        // `const fn` wrapper family pins each axis's two-arm partition
6196        // through the same const dispatch as the runtime path.
6197        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
6198        // so that arm's "empty" fixture is the populated arm the
6199        // mutation sweep covers.
6200        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6201        assert!(autores_via_const_fn(&c_empty).is_empty());
6202        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
6203        assert!(exe_via_const_fn(&c_empty).is_empty());
6204        assert!(servicos_via_const_fn(&c_empty).is_empty());
6205        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6206        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
6207        c_full.etiquetas = vec!["compounding".to_string()];
6208        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
6209        c_full.exe = vec!["exe/cli.lisp".to_string()];
6210        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
6211        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
6212        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
6213        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
6214        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
6215        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
6216        assert_eq!(
6217            bibliotecas_via_const_fn(&c_full),
6218            &["lib/one.lisp", "lib/two.lisp"]
6219        );
6220        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
6221        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
6222        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
6223        assert_eq!(
6224            servicos_via_const_fn(&c_full),
6225            &["servicos/one.computeunit.yaml"]
6226        );
6227    }
6228
6229    #[test]
6230    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
6231        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
6232        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
6233        // / [`Caixa::deps_dev`] on the dep-graph axis,
6234        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
6235        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
6236        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
6237        // bare `self.<field>.as_slice()` dispatch through
6238        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
6239        // the workspace MSRV) — peer of the sibling `String`-payload
6240        // slice-return pin above on the peer outer-`Caixa` universal-
6241        // axis surface, and peer of the sibling inner-composite-
6242        // altitude reference-return pin family
6243        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
6244        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
6245        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
6246        // (all pinned at 0b23e0f).
6247        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
6248            c.deps()
6249        }
6250        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
6251            c.deps_dev()
6252        }
6253        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
6254            c.upgrade_from()
6255        }
6256        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
6257            c.children()
6258        }
6259        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
6260            c.membros()
6261        }
6262        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
6263            c.contratos()
6264        }
6265        // Empty-arm sweep on all six composite-carrier axes — every
6266        // `Caixa::template` starts with `Vec::new()` on each.
6267        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6268        assert!(deps_via_const_fn(&c_empty).is_empty());
6269        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
6270        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
6271        assert!(children_via_const_fn(&c_empty).is_empty());
6272        assert!(membros_via_const_fn(&c_empty).is_empty());
6273        assert!(contratos_via_const_fn(&c_empty).is_empty());
6274        // Populate `:membros` / `:contratos` directly via struct literals
6275        // — the parser-side validation path fans on `:kind`-gated cross-
6276        // slot invariants irrelevant to the accessor dispatch under test.
6277        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6278        c_full.membros = vec![
6279            crate::aplicacao::Membro {
6280                caixa: "demo-a".to_string(),
6281                versao: "^0.1.0".to_string(),
6282            },
6283            crate::aplicacao::Membro {
6284                caixa: "demo-b".to_string(),
6285                versao: "^0.2.0".to_string(),
6286            },
6287        ];
6288        c_full.contratos = vec![crate::aplicacao::WitContract {
6289            de: "demo-a".to_string(),
6290            para: "demo-b".to_string(),
6291            wit: "wasi:http/proxy".to_string(),
6292            endpoint: Some("/edge".to_string()),
6293            subject: None,
6294            slot: None,
6295        }];
6296        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
6297        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
6298        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
6299        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
6300        // Alias-borrow check on the four remaining composite-carrier
6301        // slice-return arms — the wrapper's return borrow must alias the
6302        // caller's borrow so any future accessor re-routing that skips
6303        // the storage field surfaces through the assertion.
6304        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
6305        assert!(std::ptr::eq(
6306            deps_dev_via_const_fn(&c_full),
6307            c_full.deps_dev()
6308        ));
6309        assert!(std::ptr::eq(
6310            upgrade_from_via_const_fn(&c_full),
6311            c_full.upgrade_from()
6312        ));
6313        assert!(std::ptr::eq(
6314            children_via_const_fn(&c_full),
6315            c_full.children()
6316        ));
6317    }
6318
6319    #[test]
6320    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
6321        // Fail-before-pass-after pin on the six outer-[`Caixa`]
6322        // `Option<Composite> → Option<&Composite>` reference-return
6323        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
6324        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
6325        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
6326        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
6327        // Each body is a bare `self.<field>.as_ref()` dispatch through
6328        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
6329        // the workspace MSRV of 1.89). Any future accidental downgrade
6330        // to non-`const` fails the corresponding `<name>_via_const_fn`
6331        // wrapper at caixa-core build time with E0015 (`cannot call
6332        // non-const method`), strictly stronger than a runtime `assert!`
6333        // and strictly stronger than a module-scope `const _: () =
6334        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
6335        // because the type's `String` / `Vec` / `Option<Composite>`
6336        // carriers rule out `const`-context value construction; the
6337        // `const fn` wrapper is the load-bearing shape that side-steps
6338        // the destructor-in-const restriction on the value axis while
6339        // still pinning the `const`-fn posture on the callee — mirror
6340        // of the sibling
6341        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
6342        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
6343        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
6344        // pins' discipline verbatim on the peer outer-`Caixa` axes at
6345        // the same struct).
6346        //
6347        // Closes the outer-`Caixa` `Option<&Composite>` composite-
6348        // reference-return sub-family — the last unlifted altitude on
6349        // the outer-`Caixa` accessor-family const-eval surface after
6350        // the sibling `Copy`-return / universal-axis-`&str` /
6351        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
6352        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
6353        // 231a968 (the last of these pins the `Vec<T> → &[T]`
6354        // composite-slice arm the six accessors here close as their
6355        // `Option<Composite> → Option<&Composite>` peer). Peer of the
6356        // sibling inner-altitude nested-spec composite-reference-return
6357        // pin family — [`crate::AplicacaoSpec::politicas`] /
6358        // [`crate::AplicacaoSpec::placement`] /
6359        // [`crate::AplicacaoSpec::entrada`] on the inner
6360        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
6361        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
6362        // the same shape so both altitudes of the reference-return
6363        // discipline (per-`Caixa` outer-slot presence + per-
6364        // `AplicacaoSpec` inner-slot presence) route through one typed
6365        // const dispatch on the substrate primitive.
6366        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
6367            c.limits()
6368        }
6369        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
6370            c.behavior()
6371        }
6372        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
6373            c.politicas()
6374        }
6375        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
6376            c.placement()
6377        }
6378        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
6379            c.entrada()
6380        }
6381        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
6382            c.ci()
6383        }
6384        // Both-arm sweep on every accessor: the `None` author-omitted
6385        // arm (template default — no M2/M3/CI slot declared) and the
6386        // `Some(<composite>)` authored arm (mutated below via struct-
6387        // literal seeds, side-stepping the parser-side `:kind`-gated
6388        // cross-slot invariants irrelevant to the accessor dispatch
6389        // under test). Both arms route through the `const fn` wrapper
6390        // family so the two-arm `Option` partition is pinned through
6391        // the same const dispatch as the runtime path.
6392        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6393        assert!(limits_via_const_fn(&c_empty).is_none());
6394        assert!(behavior_via_const_fn(&c_empty).is_none());
6395        assert!(politicas_via_const_fn(&c_empty).is_none());
6396        assert!(placement_via_const_fn(&c_empty).is_none());
6397        assert!(entrada_via_const_fn(&c_empty).is_none());
6398        assert!(ci_via_const_fn(&c_empty).is_none());
6399        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6400        c_full.limits = Some(LimitsSpec::default());
6401        c_full.behavior = Some(crate::BehaviorSpec::default());
6402        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
6403        c_full.placement = Some(crate::aplicacao::Placement::default());
6404        c_full.entrada = Some(crate::aplicacao::Entrada {
6405            host: "demo.quero.cloud".to_string(),
6406            para: "demo".to_string(),
6407            paths: Vec::new(),
6408            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
6409        });
6410        c_full.ci = Some(canteiro_types::CiRun {
6411            workspace: "pleme-io".into(),
6412            repo: "caixa".into(),
6413            nodes: vec![],
6414        });
6415        assert!(limits_via_const_fn(&c_full).is_some());
6416        assert!(behavior_via_const_fn(&c_full).is_some());
6417        assert!(politicas_via_const_fn(&c_full).is_some());
6418        assert!(placement_via_const_fn(&c_full).is_some());
6419        assert!(entrada_via_const_fn(&c_full).is_some());
6420        assert!(ci_via_const_fn(&c_full).is_some());
6421        // Alias-borrow check on every arm: the wrapper's inner-`Option`
6422        // reference must alias the caller's borrow so any future accessor
6423        // re-routing that skips the storage field surfaces through the
6424        // assertion.
6425        assert!(std::ptr::eq(
6426            limits_via_const_fn(&c_full).unwrap(),
6427            c_full.limits().unwrap()
6428        ));
6429        assert!(std::ptr::eq(
6430            behavior_via_const_fn(&c_full).unwrap(),
6431            c_full.behavior().unwrap()
6432        ));
6433        assert!(std::ptr::eq(
6434            politicas_via_const_fn(&c_full).unwrap(),
6435            c_full.politicas().unwrap()
6436        ));
6437        assert!(std::ptr::eq(
6438            placement_via_const_fn(&c_full).unwrap(),
6439            c_full.placement().unwrap()
6440        ));
6441        assert!(std::ptr::eq(
6442            entrada_via_const_fn(&c_full).unwrap(),
6443            c_full.entrada().unwrap()
6444        ));
6445        assert!(std::ptr::eq(
6446            ci_via_const_fn(&c_full).unwrap(),
6447            c_full.ci().unwrap()
6448        ));
6449    }
6450
6451    #[test]
6452    fn register_populates_registry() {
6453        Caixa::register().expect("first register call in this test process must succeed");
6454        let kws = tatara_lisp::domain::registered_keywords();
6455        assert!(kws.contains(&"defcaixa"));
6456    }
6457
6458    #[test]
6459    fn to_lisp_round_trips() {
6460        let src = Caixa::template("demo");
6461        let c1 = Caixa::from_lisp(&src).unwrap();
6462        let emitted = c1.to_lisp();
6463        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
6464        assert_eq!(c1, c2);
6465    }
6466
6467    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
6468    //
6469    // The compounding pin: the variant stores only the typed
6470    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
6471    // (canonical keyword, description, consumer) routes through the enum's
6472    // own accessors at Display time. Prior to that closure the variant
6473    // carried each accessor's return value as a stored `&'static str`
6474    // snapshot alongside `dialeto`; a caller could construct the variant
6475    // with a snapshot that drifted from what `dialeto`'s accessors would
6476    // return, and every downstream user-facing projection would silently
6477    // disagree with the classification. Storing only the axis makes the
6478    // drift structurally impossible.
6479
6480    #[test]
6481    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
6482        // Single-field construction is the whole compounding shape — a
6483        // future re-introduction of a snapshot field (a `palavra_canonica:
6484        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
6485        // would re-open the drift surface and this construction would fail
6486        // to compile with "missing field" until every snapshot was seeded
6487        // at the call site again. The compile-time guarantee is the
6488        // invariant; the assertion below only witnesses that the
6489        // construction is well-formed after the closure.
6490        let err = LeituraError::DialetoEstrangeiro {
6491            dialeto: crate::dialeto::CaixaDialeto::Molde,
6492        };
6493        assert!(matches!(
6494            err,
6495            LeituraError::DialetoEstrangeiro {
6496                dialeto: crate::dialeto::CaixaDialeto::Molde,
6497            }
6498        ));
6499    }
6500
6501    #[test]
6502    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
6503        // For every foreign-dialect classification the variant surfaces —
6504        // [`crate::dialeto::CaixaDialeto::Molde`] and
6505        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
6506        // variants [`Caixa::from_lisp`] raises this error for — the
6507        // rendered [`std::fmt::Display`] byte-string must interpolate each
6508        // typed accessor's return verbatim. A future re-introduction of a
6509        // stored `&'static str` snapshot alongside `dialeto` that Display
6510        // read instead of the accessor would fail this pin as soon as the
6511        // two disagreed; a future accessor rebrand (a per-dialect
6512        // consumer rename, a canonical-keyword shift once the substrate
6513        // migration named in [`crate::dialeto`] completes) reaches every
6514        // consumer through one typed dispatch and this pin verifies the
6515        // display path is one of them.
6516        for d in [
6517            crate::dialeto::CaixaDialeto::Molde,
6518            crate::dialeto::CaixaDialeto::MoldePosicional,
6519        ] {
6520            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
6521            assert!(
6522                rendered.contains(d.palavra_canonica()),
6523                "Display must interpolate `dialeto.palavra_canonica()` \
6524                 verbatim — a stored snapshot would silently drift from \
6525                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
6526            );
6527            assert!(
6528                rendered.contains(d.descricao()),
6529                "Display must interpolate `dialeto.descricao()` verbatim. \
6530                 dialect: {d}, rendered: {rendered:?}"
6531            );
6532            assert!(
6533                rendered.contains(d.consumidor()),
6534                "Display must interpolate `dialeto.consumidor()` verbatim. \
6535                 dialect: {d}, rendered: {rendered:?}"
6536            );
6537        }
6538    }
6539
6540    #[test]
6541    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
6542        // The end-to-end pin the compounding closure defends: a
6543        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
6544        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
6545        // rendered Display byte-string names the Molde accessors'
6546        // returns verbatim. Any future path that constructed the variant
6547        // with a mismatched snapshot (a stored `palavra_canonica:
6548        // "defcaixa"` on a `Molde` classification) would land Display
6549        // pointing at `defcaixa` while the typed axis said `Molde` — the
6550        // exact drift the closure removes.
6551        let src = r#"
6552          (defcaixa
6553            :name "x"
6554            :kind :Biblioteca
6555            :ecosystem :rust-single-crate
6556            :package {:name "x" :version "0.1.0"})
6557        "#;
6558        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
6559        match err {
6560            LeituraError::DialetoEstrangeiro { dialeto } => {
6561                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
6562                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
6563                assert!(rendered.contains(dialeto.palavra_canonica()));
6564                assert!(rendered.contains(dialeto.consumidor()));
6565                assert!(rendered.contains(dialeto.descricao()));
6566            }
6567            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
6568        }
6569    }
6570
6571    #[test]
6572    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
6573        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
6574        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
6575        // positional-arity `defmolde` form written under a `(defcaixa …)`
6576        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
6577        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
6578        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
6579        // so no test exercised the positional-arity path through
6580        // `Caixa::from_lisp` specifically; the sibling
6581        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
6582        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
6583        // two arms route through the lifted
6584        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
6585        // typed predicate — the same predicate the pre-lift `foreign =>`
6586        // wildcard resolved to today — and this pin makes the
6587        // positional-arity arm's byte-shape at the gate explicit rather
6588        // than implied by wildcard-absorption. A future regression that
6589        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
6590        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
6591        // from the two-arity closure) would fail this pin at caixa-core
6592        // test time rather than surfacing far from the change as a
6593        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
6594        // …)` silently parsing past the derive.
6595        let src = r#"
6596          (defcaixa todoku-go
6597            :kind :Biblioteca
6598            :ecosystem :go
6599            :package {:name "todoku-go" :version "0.3.0"})
6600        "#;
6601        let err =
6602            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
6603        match err {
6604            LeituraError::DialetoEstrangeiro { dialeto } => {
6605                assert_eq!(
6606                    dialeto,
6607                    crate::dialeto::CaixaDialeto::MoldePosicional,
6608                    "DialetoEstrangeiro must carry the MoldePosicional \
6609                     variant verbatim — the positional-arity `defmolde` \
6610                     form under a `(defcaixa …)` head is the \
6611                     `MoldePosicional` arm's canonical byte-shape"
6612                );
6613                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
6614                assert!(
6615                    rendered.contains(dialeto.palavra_canonica()),
6616                    "Display must interpolate `dialeto.palavra_canonica()` \
6617                     verbatim on the MoldePosicional arm; rendered: \
6618                     {rendered:?}"
6619                );
6620                assert!(
6621                    rendered.contains(dialeto.consumidor()),
6622                    "Display must interpolate `dialeto.consumidor()` \
6623                     verbatim on the MoldePosicional arm; rendered: \
6624                     {rendered:?}"
6625                );
6626                assert!(
6627                    rendered.contains(dialeto.descricao()),
6628                    "Display must interpolate `dialeto.descricao()` \
6629                     verbatim on the MoldePosicional arm; rendered: \
6630                     {rendered:?}"
6631                );
6632            }
6633            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
6634        }
6635    }
6636
6637    #[test]
6638    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
6639        // Load-bearing byte-parity pin: for every arm in
6640        // [`crate::dialeto::CaixaDialeto::ALL`], the
6641        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
6642        // partition must agree with the lifted
6643        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
6644        // typed predicate — i.e. from_lisp raises
6645        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
6646        // `d.is_molde_family()` returns `true`, and does NOT raise
6647        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
6648        // predicate returns `false` (the arm's source falls through to
6649        // the derive — parses cleanly on
6650        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
6651        // [`LeituraError::Leitura`] on
6652        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
6653        //
6654        // Pre-lift the gate hand-rolled a three-arm match
6655        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
6656        // whose `foreign =>` wildcard expressed no compile-time link
6657        // back to the substrate primitive's arm-family; a future fifth
6658        // dialect the [`crate::dialeto`] module doc's "third dialect"
6659        // hazard actualises would fall silently onto the wildcard
6660        // regardless of whether it belonged to the `defmolde` family or
6661        // to a distinct `defcaixa`-family. Post-lift the partition
6662        // resolves through
6663        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
6664        // typed dispatch, and this pin refuses any future regression
6665        // that silently split the from_lisp partition from the typed
6666        // predicate — the two paths now migrate as one on any future
6667        // arm addition.
6668        //
6669        // Sibling in shape to the peer
6670        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
6671        // (e9d2315) that pins the same byte-parity between
6672        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
6673        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
6674        // `== "defmolde"` classifier — extends the discipline from the
6675        // two paths within the [`crate::dialeto`] primitive onto the
6676        // third external consumer of the `defmolde`-family partition
6677        // (the [`Caixa::from_lisp`] gate that raises
6678        // [`LeituraError::DialetoEstrangeiro`]).
6679        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
6680            (
6681                crate::dialeto::CaixaDialeto::Pacote,
6682                r#"
6683                  (defcaixa
6684                    :nome   "checkout"
6685                    :versao "0.1.0"
6686                    :kind   Biblioteca
6687                    :edicao "2026"
6688                    :descricao "canonical Pacote source"
6689                    :autores ()
6690                    :etiquetas ()
6691                    :deps ()
6692                    :deps-dev ()
6693                    :bibliotecas ("lib/checkout.lisp"))
6694                "#,
6695            ),
6696            (
6697                crate::dialeto::CaixaDialeto::Molde,
6698                r#"
6699                  (defcaixa
6700                    :name "base64"
6701                    :kind :Biblioteca
6702                    :ecosystem :rust-single-crate
6703                    :package {:name "base64" :version "0.22.1"}
6704                    :workflows [:auto-release])
6705                "#,
6706            ),
6707            (
6708                crate::dialeto::CaixaDialeto::MoldePosicional,
6709                r#"
6710                  (defcaixa todoku-go
6711                    :kind :Biblioteca
6712                    :ecosystem :go
6713                    :package {:name "todoku-go" :version "0.3.0"})
6714                "#,
6715            ),
6716            (
6717                crate::dialeto::CaixaDialeto::Desconhecido,
6718                r#"(defcaixa :licenca "MIT")"#,
6719            ),
6720        ];
6721
6722        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
6723        // must appear in the fixture table so the pin's arm-set stays
6724        // synchronised with the enum's arm-set. Fails at test time if a
6725        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
6726        // (with a corresponding `is_molde_family` return) forgot to
6727        // extend this fixture table with a canonical source for the new
6728        // arm — the pin cannot cover an arm it has no source for.
6729        for &expected in crate::dialeto::CaixaDialeto::ALL {
6730            assert!(
6731                fixtures.iter().any(|(d, _)| *d == expected),
6732                "fixture table must carry a canonical source for every \
6733                 CaixaDialeto arm; missing: {expected:?}"
6734            );
6735        }
6736
6737        for &(expected_dialect, src) in fixtures {
6738            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
6739                panic!(
6740                    "fixture source for {expected_dialect:?} must classify \
6741                     cleanly, got err: {err:?}"
6742                )
6743            });
6744            assert_eq!(
6745                classified, expected_dialect,
6746                "fixture source for {expected_dialect:?} must classify as \
6747                 {expected_dialect:?} (drift here defeats the byte-parity \
6748                 pin below — a source labelled for one arm but classifying \
6749                 as another would silently satisfy or violate the pin for \
6750                 the wrong reason)"
6751            );
6752
6753            let outcome = Caixa::from_lisp(src);
6754            match (expected_dialect.is_molde_family(), &outcome) {
6755                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
6756                    assert_eq!(
6757                        *dialeto, expected_dialect,
6758                        "DialetoEstrangeiro must carry the same typed arm \
6759                         the classifier returned — a drift here would let \
6760                         from_lisp raise the error while pointing at the \
6761                         wrong dialect (e.g. rejecting a \
6762                         MoldePosicional source as Molde). arm: \
6763                         {expected_dialect:?}"
6764                    );
6765                }
6766                (true, other) => panic!(
6767                    "arm {expected_dialect:?} has is_molde_family() = true \
6768                     so from_lisp must raise DialetoEstrangeiro carrying \
6769                     {expected_dialect:?}; got: {other:?}"
6770                ),
6771                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
6772                    "arm {expected_dialect:?} has is_molde_family() = false \
6773                     so from_lisp must NOT raise DialetoEstrangeiro; got \
6774                     one carrying: {dialeto:?}. This means the typed \
6775                     predicate and the from_lisp partition disagree on \
6776                     this arm — exactly the drift this pin refuses."
6777                ),
6778                (false, _) => {
6779                    // A non-molde arm's source falls through to the
6780                    // derive: Pacote sources parse to Ok(_); Desconhecido
6781                    // sources surface as LeituraError::Leitura from the
6782                    // derive's own unknown-keyword rejection. Either
6783                    // shape is acceptable here — the pin's promise is
6784                    // narrower: "no DialetoEstrangeiro on
6785                    // is_molde_family() == false".
6786                }
6787            }
6788        }
6789    }
6790
6791    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
6792
6793    #[test]
6794    fn limits_round_trip_via_json() {
6795        use crate::LimitsSpec;
6796        use std::time::Duration;
6797        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6798        c.limits = Some(LimitsSpec {
6799            memory: Some(64 * 1024 * 1024),
6800            fuel: Some(1_000_000),
6801            wall_clock: Some(Duration::from_secs(30)),
6802            cpu: Some(500),
6803        });
6804        let json = serde_json::to_string(&c).unwrap();
6805        assert!(json.contains("\"limits\""));
6806        assert!(json.contains("\"64MiB\""));
6807        assert!(json.contains("\"30s\""));
6808        assert!(json.contains("\"500m\""));
6809        let back: Caixa = serde_json::from_str(&json).unwrap();
6810        assert_eq!(c.limits, back.limits);
6811    }
6812
6813    #[test]
6814    fn behavior_round_trip_via_json() {
6815        use crate::BehaviorSpec;
6816        use std::path::PathBuf;
6817        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6818        c.behavior = Some(BehaviorSpec {
6819            on_init: Some(PathBuf::from("lib/init.lisp")),
6820            on_call: Some(PathBuf::from("lib/handlers.lisp")),
6821            ..Default::default()
6822        });
6823        let json = serde_json::to_string(&c).unwrap();
6824        let back: Caixa = serde_json::from_str(&json).unwrap();
6825        assert_eq!(c.behavior, back.behavior);
6826    }
6827
6828    #[test]
6829    fn upgrade_from_round_trip_via_json() {
6830        use crate::{UpgradeFromEntry, UpgradeInstruction};
6831        use std::path::PathBuf;
6832        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6833        c.upgrade_from = vec![UpgradeFromEntry {
6834            from: "0.1.0".into(),
6835            instructions: vec![
6836                UpgradeInstruction::LoadModule {
6837                    module: "demo".into(),
6838                },
6839                UpgradeInstruction::StateChange {
6840                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
6841                },
6842                UpgradeInstruction::SoftPurge {
6843                    module: "demo-old".into(),
6844                },
6845            ],
6846        }];
6847        let json = serde_json::to_string(&c).unwrap();
6848        let back: Caixa = serde_json::from_str(&json).unwrap();
6849        assert_eq!(c.upgrade_from, back.upgrade_from);
6850    }
6851
6852    #[test]
6853    fn supervisor_view_returns_typed_shape() {
6854        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
6855        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
6856        c.kind = CaixaKind::Supervisor;
6857        c.bibliotecas.clear();
6858        c.estrategia = Some(RestartStrategy::OneForOne);
6859        c.max_restarts = Some(5);
6860        c.restart_window = Some("60s".into());
6861        c.children = vec![ChildSpec {
6862            caixa: "worker".into(),
6863            versao: "^0.1".into(),
6864            restart: RestartPolicy::Permanent,
6865        }];
6866        let view = c.supervisor_view().expect("Supervisor kind has a view");
6867        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
6868        assert_eq!(view.max_restarts, 5);
6869        assert_eq!(
6870            view.restart_window,
6871            Some(std::time::Duration::from_secs(60))
6872        );
6873        assert_eq!(view.children.len(), 1);
6874        view.validate().unwrap();
6875    }
6876
6877    #[test]
6878    fn supervisor_view_none_for_non_supervisor_kinds() {
6879        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6880        assert!(c.supervisor_view().is_none());
6881    }
6882
6883    #[test]
6884    fn declared_mesh_slots_empty_for_bare_caixa() {
6885        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6886        assert!(c.declared_mesh_slots().is_empty());
6887    }
6888
6889    #[test]
6890    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
6891        use crate::{Entrada, Membro};
6892        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6893        // Set a non-adjacent pair (:membros + :entrada) to pin that the
6894        // canonical declaration order is preserved regardless of which
6895        // subset is populated.
6896        c.membros = vec![Membro {
6897            caixa: "a".into(),
6898            versao: "^0.1".into(),
6899        }];
6900        c.entrada = Some(Entrada {
6901            host: "x.example.com".into(),
6902            para: "a".into(),
6903            paths: vec![],
6904            port: 8080,
6905        });
6906        assert_eq!(
6907            c.declared_mesh_slots(),
6908            vec![
6909                crate::render::M3_AUTHOR_KEY_MEMBROS,
6910                crate::render::M3_AUTHOR_KEY_ENTRADA,
6911            ]
6912        );
6913    }
6914
6915    #[test]
6916    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
6917        // Scalar-value pin: the five author-facing kebab-case labels the
6918        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
6919        // mesh slot axis, one arm per typed slot. Mirrors the peer
6920        // scalar-value pin the sibling
6921        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
6922        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
6923        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
6924        // carry (f49c8b0), so both altitudes of the typed-slot algebra
6925        // (per-Servico M2 + per-Aplicacao M3) share the same
6926        // "one canonical byte-string per arm" discipline. A future
6927        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
6928        // `:politicas` → `:policies`, `:placement` → `:distribution`,
6929        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
6930        // and every consumer that reaches for the label picks it up at
6931        // build time rather than at runtime as a downstream mismatch.
6932        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
6933        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
6934        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
6935        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
6936        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
6937    }
6938
6939    #[test]
6940    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
6941        // Production-through-const pin: the five per-arm labels the
6942        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
6943        // `Vec` route through the lifted
6944        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
6945        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
6946        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
6947        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
6948        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
6949        // declaration order. A future re-order or drift at the tagger
6950        // (a rename that reaches the tagger but not the const, or vice
6951        // versa) surfaces here at build time rather than at runtime as
6952        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6953        // `slots: <stale-kebab-case>` diagnostic far from the rename's
6954        // commit. Mirror of the peer
6955        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
6956        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
6957        // axis.
6958        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
6959        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
6960        c.membros = vec![Membro {
6961            caixa: "a".into(),
6962            versao: "^0.1".into(),
6963        }];
6964        c.contratos = vec![WitContract {
6965            de: "a".into(),
6966            para: "a".into(),
6967            wit: "wasi:http/proxy".into(),
6968            endpoint: Some("/x".into()),
6969            subject: None,
6970            slot: None,
6971        }];
6972        c.politicas = Some(MeshPolicy::default());
6973        c.placement = Some(Placement {
6974            estrategia: PlacementStrategy::Replicated,
6975            clusters: vec!["rio".into()],
6976            affinity: None,
6977            shard_key: None,
6978        });
6979        c.entrada = Some(Entrada {
6980            host: "x.example.com".into(),
6981            para: "a".into(),
6982            paths: vec![],
6983            port: 8080,
6984        });
6985        assert_eq!(
6986            c.declared_mesh_slots(),
6987            vec![
6988                crate::render::M3_AUTHOR_KEY_MEMBROS,
6989                crate::render::M3_AUTHOR_KEY_CONTRATOS,
6990                crate::render::M3_AUTHOR_KEY_POLITICAS,
6991                crate::render::M3_AUTHOR_KEY_PLACEMENT,
6992                crate::render::M3_AUTHOR_KEY_ENTRADA,
6993            ]
6994        );
6995    }
6996
6997    #[test]
6998    fn declared_supervisor_slots_empty_for_bare_caixa() {
6999        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7000        assert!(c.declared_supervisor_slots().is_empty());
7001    }
7002
7003    #[test]
7004    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
7005        use crate::RestartStrategy;
7006        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7007        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
7008        // that the canonical declaration order is preserved regardless
7009        // of which subset is populated.
7010        c.estrategia = Some(RestartStrategy::OneForOne);
7011        c.restart_window = Some("60s".into());
7012        assert_eq!(
7013            c.declared_supervisor_slots(),
7014            vec![
7015                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7016                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7017            ]
7018        );
7019    }
7020
7021    #[test]
7022    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7023        // Scalar-value pin: the four author-facing kebab-case labels the
7024        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
7025        // supervision-tree slot axis, one arm per typed slot. Mirrors the
7026        // peer scalar-value pins the sibling
7027        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
7028        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
7029        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
7030        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
7031        // top-level M3 slot consts carry, so all three kind-scoped
7032        // typed-slot-family author-facing-label axes route through one
7033        // canonical per-arm declaration. A future rebrand
7034        // (`:estrategia` → `:strategy` for English uniformity,
7035        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
7036        // `MaxIntensity` name, `:restart-window` → `:period` matching
7037        // OTP's `Period` name, `:children` → `:workers` matching Elixir
7038        // idiom) lands as an edit to exactly one const, and every
7039        // consumer that reaches for the label picks it up at build time
7040        // rather than at runtime as a downstream mismatch.
7041        assert_eq!(
7042            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7043            ":estrategia"
7044        );
7045        assert_eq!(
7046            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
7047            ":max-restarts"
7048        );
7049        assert_eq!(
7050            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7051            ":restart-window"
7052        );
7053        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
7054    }
7055
7056    #[test]
7057    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
7058        // Production-through-const pin: the four per-arm labels the
7059        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
7060        // return `Vec` route through the lifted
7061        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
7062        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
7063        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
7064        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
7065        // canonical declaration order. A future re-order or drift at the
7066        // tagger (a rename that reaches the tagger but not the const, or
7067        // vice versa) surfaces here at build time rather than at runtime
7068        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
7069        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7070        // commit. Mirror of the peer
7071        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
7072        // (f49c8b0) and
7073        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
7074        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
7075        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
7076        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7077        c.estrategia = Some(RestartStrategy::OneForOne);
7078        c.max_restarts = Some(5);
7079        c.restart_window = Some("60s".into());
7080        c.children = vec![ChildSpec {
7081            caixa: "worker".into(),
7082            versao: "^0.1".into(),
7083            restart: RestartPolicy::Permanent,
7084        }];
7085        assert_eq!(
7086            c.declared_supervisor_slots(),
7087            vec![
7088                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7089                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
7090                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7091                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
7092            ]
7093        );
7094    }
7095
7096    #[test]
7097    fn declared_servico_slots_empty_for_bare_caixa() {
7098        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7099        assert!(c.declared_servico_slots().is_empty());
7100    }
7101
7102    #[test]
7103    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
7104        use crate::{UpgradeFromEntry, UpgradeInstruction};
7105        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7106        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
7107        // the canonical declaration order is preserved regardless of
7108        // which subset is populated.
7109        c.limits = Some(crate::LimitsSpec {
7110            fuel: Some(1_000_000),
7111            ..Default::default()
7112        });
7113        c.upgrade_from = vec![UpgradeFromEntry {
7114            from: "0.1.0".into(),
7115            instructions: vec![UpgradeInstruction::Restart],
7116        }];
7117        assert_eq!(
7118            c.declared_servico_slots(),
7119            vec![
7120                crate::render::M2_AUTHOR_KEY_LIMITS,
7121                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
7122            ]
7123        );
7124    }
7125
7126    #[test]
7127    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7128        // Scalar-value pin: the three author-facing kebab-case labels
7129        // the `(defcaixa … :<slot> (…))` surface admits on the M2
7130        // top-level slot axis, one arm per typed slot. Mirrors the peer
7131        // scalar-value pin the sibling renderer-side
7132        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
7133        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
7134        // consts carry, so both halves of the M2 top-level slot dual
7135        // axis (author-facing kebab-case label + renderer-side
7136        // camelCase overlay-container wire key) route through one
7137        // canonical per-arm declaration. A future rebrand
7138        // (`:limits` → `:sandbox` matching Lunatic per-process
7139        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
7140        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
7141        // matching Erlang's verbatim appup name) lands as an edit to
7142        // exactly one const, and every consumer that reaches for the
7143        // label picks it up at build time rather than at runtime as a
7144        // downstream mismatch.
7145        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
7146        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
7147        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
7148    }
7149
7150    #[test]
7151    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
7152        // Production-through-const pin: the three per-arm labels the
7153        // [`Caixa::declared_servico_slots`] tagger pushes onto its
7154        // return `Vec` route through the lifted
7155        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
7156        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
7157        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
7158        // declaration order. A future re-order or drift at the tagger
7159        // (a rename that reaches the tagger but not the const, or vice
7160        // versa) surfaces here at build time rather than at runtime as
7161        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
7162        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7163        // commit. Mirror of the peer
7164        // [`crate::behavior::BehaviorSpec::declared_slots`] production
7165        // tagger pin (889dc18) on the sibling per-callback axis.
7166        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
7167        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7168        c.limits = Some(crate::LimitsSpec {
7169            fuel: Some(1_000_000),
7170            ..Default::default()
7171        });
7172        c.behavior = Some(BehaviorSpec {
7173            on_init: Some(PathBuf::from("lib/init.lisp")),
7174            ..Default::default()
7175        });
7176        c.upgrade_from = vec![UpgradeFromEntry {
7177            from: "0.1.0".into(),
7178            instructions: vec![UpgradeInstruction::Restart],
7179        }];
7180        assert_eq!(
7181            c.declared_servico_slots(),
7182            vec![
7183                crate::render::M2_AUTHOR_KEY_LIMITS,
7184                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
7185                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
7186            ]
7187        );
7188    }
7189
7190    #[test]
7191    fn existing_manifests_unaffected_by_new_optional_slots() {
7192        // Regression test: a caixa.lisp authored before M2 typed slots
7193        // should still parse + serialize cleanly. The bare `defcaixa`
7194        // emitted by `Caixa::template` has none of the new fields.
7195        let src = Caixa::template("legacy");
7196        let c = Caixa::from_lisp(&src).unwrap();
7197        assert!(c.limits.is_none());
7198        assert!(c.behavior.is_none());
7199        assert!(c.upgrade_from.is_empty());
7200        assert!(c.estrategia.is_none());
7201        assert!(c.children.is_empty());
7202
7203        // And to_lisp emits a manifest with the new slots in the
7204        // empty/default state — round-trippable.
7205        let emitted = c.to_lisp();
7206        let back = Caixa::from_lisp(&emitted).unwrap();
7207        assert_eq!(c, back);
7208    }
7209
7210    #[test]
7211    fn validate_deps_accepts_canonical_caixa() {
7212        // Positive control: the bare template — zero deps, zero
7213        // deps_dev — passes the gate trivially. A future axis added to
7214        // `Dep::validate` mustn't regress an empty-deps caixa to a
7215        // build error.
7216        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7217        c.validate_deps().unwrap();
7218    }
7219
7220    #[test]
7221    fn validate_deps_rejects_invalid_versao_in_deps() {
7222        // Fail-before-pass-after pin: a malformed `:deps :versao`
7223        // surfaces at validate_deps() time, not at lacre-resolve time.
7224        // Mirrors `rejects_invalid_membro_versao_requirement` and
7225        // `validate_rejects_invalid_child_versao_requirement` on the
7226        // other two `:versao` axes.
7227        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7228        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
7229        let err = c.validate_deps().unwrap_err();
7230        assert!(
7231            matches!(
7232                err,
7233                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7234                    if nome == "caixa-teia" && versao == "^bad-version"
7235            ),
7236            "got {err:?}"
7237        );
7238    }
7239
7240    #[test]
7241    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
7242        // Parity pin: `:deps-dev` must run through the same per-entry
7243        // validator as `:deps` — a typo in either axis surfaces the
7244        // same diagnostic. Without this leg, `:deps-dev` would be a
7245        // second-class citizen of the typed surface and an author
7246        // could land a build that passes validate_deps but fails at
7247        // `feira lock`-time when the dev-dep is resolved for a test
7248        // build.
7249        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7250        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
7251        let err = c.validate_deps().unwrap_err();
7252        assert!(
7253            matches!(
7254                err,
7255                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7256                    if nome == "tatara-check" && versao == "^^0.1"
7257            ),
7258            "got {err:?}"
7259        );
7260    }
7261
7262    #[test]
7263    fn validate_deps_runs_deps_before_deps_dev() {
7264        // Order pin: when both lists carry typos, the `:deps`
7265        // diagnostic surfaces first. The author's mental model is
7266        // "runtime deps are load-bearing; dev deps are scaffolding";
7267        // surfacing the runtime axis first matches that hierarchy.
7268        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7269        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
7270        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
7271        let err = c.validate_deps().unwrap_err();
7272        assert!(
7273            matches!(
7274                err,
7275                crate::dep::DepError::VersaoInvalid { ref nome, .. }
7276                    if nome == "runtime-dep"
7277            ),
7278            "expected `:deps` typo to surface first, got {err:?}"
7279        );
7280    }
7281
7282    #[test]
7283    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
7284        // Positive control sweep across both lists. Pin every
7285        // canonical Cargo-shaped form so a future tightening of the
7286        // accepted set surfaces here as a test failure (parity with
7287        // `accepts_canonical_membro_versao_forms` and
7288        // `validate_accepts_canonical_child_versao_forms`).
7289        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7290        c.deps = vec![
7291            Dep::simple("caret", "^0.1"),
7292            Dep::simple("tilde", "~0.1.2"),
7293            Dep::simple("exact", "0.1.0"),
7294            Dep::simple("wildcard", "*"),
7295            Dep::simple("multi-range", ">=0.1, <2"),
7296        ];
7297        c.deps_dev = vec![
7298            Dep::simple("dev-caret", "^0.1"),
7299            Dep::simple("dev-wildcard", "*"),
7300        ];
7301        c.validate_deps().unwrap();
7302    }
7303
7304    #[test]
7305    fn validate_deps_diagnostic_carries_offending_dep() {
7306        // Diagnostic-shape pin: the error names the offending entry's
7307        // `:nome` + `:versao` verbatim and carries a non-empty
7308        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
7309        // run can render the diagnostic without re-parsing.
7310        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7311        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
7312        let err = c.validate_deps().unwrap_err();
7313        let crate::dep::DepError::VersaoInvalid {
7314            nome,
7315            versao,
7316            reason,
7317        } = err
7318        else {
7319            panic!("expected VersaoInvalid, got other variant");
7320        };
7321        assert_eq!(nome, "caixa-teia");
7322        assert_eq!(versao, "not-a-req");
7323        assert!(
7324            !reason.is_empty(),
7325            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7326        );
7327    }
7328
7329    #[test]
7330    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
7331        // Cross-axis pin: `validate_deps` walks both :deps and
7332        // :deps-dev through `Dep::validate`, and the new fonte gate
7333        // (`:tag` + `:branch` both set — the canonical "pin drift"
7334        // footgun) must surface from the :deps-dev arm with the
7335        // offending entry's :nome named. Pin the :deps-dev arm
7336        // explicitly so a future shortcut that only walks :deps
7337        // surfaces here as a regression.
7338        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7339        c.deps_dev = vec![Dep {
7340            nome: "dev-only".into(),
7341            versao: "^0.1".into(),
7342            fonte: Some(crate::DepSource::Git {
7343                repo: "github:p/x".into(),
7344                tag: Some("v1".into()),
7345                rev: None,
7346                branch: Some("main".into()),
7347            }),
7348            opcional: false,
7349            caracteristicas: vec![],
7350        }];
7351        let err = c.validate_deps().unwrap_err();
7352        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
7353            panic!("expected FontePinAmbiguous from :deps-dev walk");
7354        };
7355        assert_eq!(nome, "dev-only");
7356        assert!(pins.contains(":tag") && pins.contains(":branch"));
7357    }
7358
7359    #[test]
7360    fn validate_deps_rejects_empty_repo_in_deps() {
7361        // Parity pin on the :deps arm: an empty :repo on the runtime
7362        // deps list surfaces the same FonteRepoEmpty diagnostic the
7363        // dep.rs per-entry tests pin, naming the offending entry.
7364        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7365        c.deps = vec![Dep {
7366            nome: "runtime".into(),
7367            versao: "^0.1".into(),
7368            fonte: Some(crate::DepSource::Git {
7369                repo: String::new(),
7370                tag: Some("v1".into()),
7371                rev: None,
7372                branch: None,
7373            }),
7374            opcional: false,
7375            caracteristicas: vec![],
7376        }];
7377        let err = c.validate_deps().unwrap_err();
7378        assert!(
7379            matches!(
7380                err,
7381                crate::dep::DepError::FonteRepoEmpty { ref nome }
7382                    if nome == "runtime"
7383            ),
7384            "got {err:?}"
7385        );
7386    }
7387
7388    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
7389
7390    #[test]
7391    fn validate_deps_rejects_duplicate_nome_in_deps() {
7392        // Fail-before-pass-after pin: two `:deps` entries naming the same
7393        // caixa carry two `:versao` / `:fonte` / feature triples that the
7394        // caixa-resolver's lacre pipeline collapses (the second silently
7395        // overwrites the first at `concrete_versao`-resolve time). The
7396        // gate surfaces the duplicate at validate-time, naming the
7397        // offending caixa + the list, before the resolver-side silent
7398        // drop. Mirrors the peer typed-graph duplicate gates
7399        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
7400        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7401        c.deps = vec![
7402            Dep::simple("caixa-teia", "^0.1"),
7403            Dep::simple("caixa-teia", "^0.2"),
7404        ];
7405        let err = c.validate_deps().unwrap_err();
7406        assert!(
7407            matches!(
7408                err,
7409                crate::dep::DepError::DuplicateNome { ref nome, list }
7410                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7411            ),
7412            "got {err:?}"
7413        );
7414    }
7415
7416    #[test]
7417    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
7418        // Parity pin: `:deps-dev` runs through the same per-list
7419        // duplicate check as `:deps` — neither axis is a second-class
7420        // citizen of the set-not-multiset discipline.
7421        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7422        c.deps_dev = vec![
7423            Dep::simple("tatara-check", "*"),
7424            Dep::simple("tatara-check", "^0.1"),
7425        ];
7426        let err = c.validate_deps().unwrap_err();
7427        assert!(
7428            matches!(
7429                err,
7430                crate::dep::DepError::DuplicateNome { ref nome, list }
7431                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
7432            ),
7433            "got {err:?}"
7434        );
7435    }
7436
7437    #[test]
7438    fn validate_deps_accepts_cross_list_same_nome() {
7439        // The Cargo `[dependencies]` + `[dev-dependencies]` override
7440        // convention is preserved: a name appearing in *both* lists is
7441        // valid (the dev-pin overrides at test/dev time). Only
7442        // within-list duplicates are structurally incoherent — pin the
7443        // permissive cross-list semantics so a future shortcut that
7444        // collapses the two seen-sets into one surfaces here as a test
7445        // failure.
7446        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7447        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
7448        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
7449        c.validate_deps().unwrap();
7450    }
7451
7452    #[test]
7453    fn validate_deps_accepts_distinct_nome_in_both_lists() {
7454        // Positive control: distinct names within each list pass — the
7455        // gate's identity element on the canonical authoring shape.
7456        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7457        c.deps = vec![
7458            Dep::simple("caixa-teia", "^0.1"),
7459            Dep::simple("pleme-mesh", "*"),
7460        ];
7461        c.deps_dev = vec![
7462            Dep::simple("tatara-check", "*"),
7463            Dep::simple("dev-shim", "^0.1"),
7464        ];
7465        c.validate_deps().unwrap();
7466    }
7467
7468    #[test]
7469    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
7470        // Diagnostic-precedence pin: a malformed `:versao` on the
7471        // duplicating entry surfaces its narrower `VersaoInvalid`
7472        // diagnostic first, before the cross-entry duplicate gate fires
7473        // — the canonical "per-entry shape before cross-entry uniqueness"
7474        // precedence every peer set-not-multiset gate establishes
7475        // (`*_invalid_fires_before_duplicate_check` pins on
7476        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
7477        // `validate_upgrade_from`).
7478        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7479        c.deps = vec![
7480            Dep::simple("caixa-teia", "^0.1"),
7481            Dep::simple("caixa-teia", "^bad-version"),
7482        ];
7483        let err = c.validate_deps().unwrap_err();
7484        assert!(
7485            matches!(
7486                err,
7487                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7488                    if nome == "caixa-teia" && versao == "^bad-version"
7489            ),
7490            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
7491        );
7492    }
7493
7494    #[test]
7495    fn validate_deps_duplicate_diagnostic_names_first_collision() {
7496        // First-collision determinism pin: with three entries naming the
7497        // same caixa, the first colliding pair surfaces — not the last.
7498        // Mirrors the peer first-collision posture on every
7499        // duplicate-target gate
7500        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
7501        // — the second entry is the first collision; this gate uses the
7502        // same shape: the second entry's `:nome` lands in the diagnostic
7503        // because `seen.insert(first.nome)` already populated the set).
7504        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7505        c.deps = vec![
7506            Dep::simple("caixa-teia", "^0.1"),
7507            Dep::simple("caixa-teia", "^0.2"),
7508            Dep::simple("caixa-teia", "^0.3"),
7509        ];
7510        let err = c.validate_deps().unwrap_err();
7511        // The diagnostic carries the offending caixa name; the
7512        // implementation surfaces on the *second* entry (the first
7513        // collision), so the test pins the `:nome` value.
7514        assert!(
7515            matches!(
7516                err,
7517                crate::dep::DepError::DuplicateNome { ref nome, list }
7518                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7519            ),
7520            "got {err:?}"
7521        );
7522    }
7523
7524    #[test]
7525    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
7526        // Cross-list precedence pin: when both lists carry duplicates,
7527        // the `:deps` diagnostic surfaces first — same author-mental-
7528        // model ordering the `validate_deps_runs_deps_before_deps_dev`
7529        // pin establishes for malformed `:versao` (runtime axis before
7530        // dev axis).
7531        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7532        c.deps = vec![
7533            Dep::simple("runtime-dep", "^0.1"),
7534            Dep::simple("runtime-dep", "^0.2"),
7535        ];
7536        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
7537        let err = c.validate_deps().unwrap_err();
7538        assert!(
7539            matches!(
7540                err,
7541                crate::dep::DepError::DuplicateNome { ref nome, list }
7542                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7543            ),
7544            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
7545        );
7546    }
7547
7548    #[test]
7549    fn validate_deps_empty_lists_pass_duplicate_gate() {
7550        // Empty-set identity pin: the bare template (zero deps, zero
7551        // deps_dev) passes the duplicate gate as the gate's identity
7552        // element. A future tighten that conflates "empty" with
7553        // "missing" would regress this baseline.
7554        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7555        c.validate_deps().unwrap();
7556    }
7557
7558    #[test]
7559    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
7560        // Diagnostic-shape pin: the `list:` field tags which list the
7561        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
7562        // `feira lint` run can route the author to the right block in
7563        // their caixa.lisp without re-deriving the list from context.
7564        // Same self-locating shape every peer per-axis diagnostic
7565        // already exposes.
7566        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7567        c.deps_dev = vec![
7568            Dep::simple("dev-thing", "*"),
7569            Dep::simple("dev-thing", "^0.1"),
7570        ];
7571        let err = c.validate_deps().unwrap_err();
7572        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
7573            panic!("expected DuplicateNome from :deps-dev walk");
7574        };
7575        assert_eq!(nome, "dev-thing");
7576        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
7577    }
7578
7579    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
7580
7581    #[test]
7582    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
7583        // Thread-through pin on `:deps`: the per-entry
7584        // `Dep::validate_caracteristicas` gate fires inside
7585        // `Caixa::validate_deps`'s linear walk, so a malformed feature
7586        // list on any `:deps` entry surfaces as a `DepError` from
7587        // `validate_deps` — the same reachability shape every per-entry
7588        // `Dep::validate` arm threads through. Without this pin a future
7589        // shortcut that skips the per-entry `Dep::validate` call on the
7590        // cross-entry-uniqueness path would mask the within-entry
7591        // `:caracteristicas` gates.
7592        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7593        c.deps = vec![Dep {
7594            nome: "caixa-teia".into(),
7595            versao: "^0.1".into(),
7596            fonte: None,
7597            opcional: false,
7598            caracteristicas: vec!["http".into(), "http".into()],
7599        }];
7600        let err = c.validate_deps().unwrap_err();
7601        let crate::dep::DepError::CaracteristicaDuplicate {
7602            nome,
7603            caracteristica,
7604        } = err
7605        else {
7606            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
7607        };
7608        assert_eq!(nome, "caixa-teia");
7609        assert_eq!(caracteristica, "http");
7610    }
7611
7612    #[test]
7613    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
7614        // Peer thread-through pin on `:deps-dev`: same reachability as
7615        // the `:deps` arm above, on the dev-only authoring axis. Pins
7616        // that the `validate_deps` walk visits both lists' per-entry
7617        // gates uniformly. The empty-feature arm carries here so both
7618        // new `:caracteristicas` arms are surfaced via at least one
7619        // `validate_deps` thread-through.
7620        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7621        c.deps_dev = vec![Dep {
7622            nome: "caixa-teia".into(),
7623            versao: "^0.1".into(),
7624            fonte: None,
7625            opcional: false,
7626            caracteristicas: vec![String::new()],
7627        }];
7628        let err = c.validate_deps().unwrap_err();
7629        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
7630            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
7631        };
7632        assert_eq!(nome, "caixa-teia");
7633    }
7634
7635    #[test]
7636    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
7637        // Thread-through pin on `:deps`: the per-entry
7638        // `Dep::validate_caracteristicas` value-shape gate (lifted via
7639        // `crate::render::is_cargo_feature_name`) fires inside
7640        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
7641        // a structurally invalid feature name on any `:deps` entry
7642        // surfaces as `DepError::CaracteristicaInvalid` from
7643        // `validate_deps` — the same reachability shape every per-entry
7644        // `Dep::validate` arm threads through. Without this pin a
7645        // future shortcut that skips the per-entry `Dep::validate` call
7646        // on the cross-entry-uniqueness path would mask the within-
7647        // entry `:caracteristicas` value-shape gate.
7648        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7649        c.deps = vec![Dep {
7650            nome: "caixa-teia".into(),
7651            versao: "^0.1".into(),
7652            fonte: None,
7653            opcional: false,
7654            caracteristicas: vec!["+http".into()],
7655        }];
7656        let err = c.validate_deps().unwrap_err();
7657        let crate::dep::DepError::CaracteristicaInvalid {
7658            nome,
7659            caracteristica,
7660            ..
7661        } = err
7662        else {
7663            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
7664        };
7665        assert_eq!(nome, "caixa-teia");
7666        assert_eq!(caracteristica, "+http");
7667    }
7668
7669    #[test]
7670    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
7671        // Peer thread-through pin on `:deps-dev`: same reachability as
7672        // the `:deps` arm above, on the dev-only authoring axis. The
7673        // `http/json` shape carries here so the segment-separator
7674        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
7675        // confusion footgun) is surfaced via the cross-entry walk too —
7676        // pinning that the `:deps-dev` list visits the same per-entry
7677        // value-shape gate as the `:deps` list.
7678        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7679        c.deps_dev = vec![Dep {
7680            nome: "caixa-teia".into(),
7681            versao: "^0.1".into(),
7682            fonte: None,
7683            opcional: false,
7684            caracteristicas: vec!["http/json".into()],
7685        }];
7686        let err = c.validate_deps().unwrap_err();
7687        let crate::dep::DepError::CaracteristicaInvalid {
7688            nome,
7689            caracteristica,
7690            ..
7691        } = err
7692        else {
7693            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
7694        };
7695        assert_eq!(nome, "caixa-teia");
7696        assert_eq!(caracteristica, "http/json");
7697    }
7698
7699    #[test]
7700    fn to_lisp_preserves_deps() {
7701        let src = r#"
7702(defcaixa
7703  :nome "x"
7704  :versao "0.1.0"
7705  :kind Biblioteca
7706  :deps ((:nome "a" :versao "^0.1")
7707         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
7708"#;
7709        let c1 = Caixa::from_lisp(src).unwrap();
7710        let emitted = c1.to_lisp();
7711        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
7712        assert_eq!(c1.deps, c2.deps);
7713    }
7714
7715    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
7716
7717    fn caixa_with_nome(nome: &str) -> Caixa {
7718        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
7719        c.nome = nome.to_string();
7720        c
7721    }
7722
7723    #[test]
7724    fn validate_nome_accepts_canonical_template() {
7725        // Positive control: the bare `feira init`-style template's
7726        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
7727        // not regress this baseline shape. A future tightening of the
7728        // accepted set surfaces here as a test failure first.
7729        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7730        c.validate_nome().unwrap();
7731    }
7732
7733    #[test]
7734    fn validate_nome_accepts_canonical_forms() {
7735        // Positive-set sweep: each realistic caixa-name shape the K8s
7736        // apiserver accepts as a `metadata.name` label must pass —
7737        // single-word, hyphen-joined, version-suffixed, single-char,
7738        // two-char, digit-start (DNS-1123 allows this; the stricter
7739        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
7740        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
7741        // the peer member-name axis.
7742        for nome in [
7743            "checkout",
7744            "cart-v2",
7745            "a",
7746            "db",
7747            "3rd-party-shim",
7748            "payment-retry",
7749            "0",
7750        ] {
7751            caixa_with_nome(nome)
7752                .validate_nome()
7753                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
7754        }
7755    }
7756
7757    #[test]
7758    fn validate_nome_rejects_empty() {
7759        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
7760        // an empty `:nome` (the derive macro stores the raw String);
7761        // the gate's empty arm names the offending axis with a narrower
7762        // diagnostic than the `NomeInvalid` parse arm would emit.
7763        let c = caixa_with_nome("");
7764        let err = c.validate_nome().unwrap_err();
7765        assert_eq!(err, ManifestError::NomeEmpty);
7766    }
7767
7768    #[test]
7769    fn validate_nome_rejects_uppercase() {
7770        // The canonical "I copied the TitleCase display name verbatim"
7771        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
7772        // admission on every derived artifact (Helm chart, ComputeUnit,
7773        // CNP, HTTPRoute, label values); the gate moves the diagnostic
7774        // to the source `caixa.lisp` and the reason suggests the
7775        // lowercased fix verbatim.
7776        let c = caixa_with_nome("MyApp");
7777        let err = c.validate_nome().unwrap_err();
7778        let ManifestError::NomeInvalid { nome, reason } = err else {
7779            panic!("expected NomeInvalid for uppercase :nome");
7780        };
7781        assert_eq!(nome, "MyApp");
7782        assert!(
7783            reason.contains("uppercase") && reason.contains("myapp"),
7784            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
7785        );
7786    }
7787
7788    #[test]
7789    fn validate_nome_rejects_underscore() {
7790        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
7791        // `_`; the apiserver rejects on admission across every derived
7792        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
7793        // and `:children :caixa` (31bfa43).
7794        let c = caixa_with_nome("my_app");
7795        let err = c.validate_nome().unwrap_err();
7796        assert!(
7797            matches!(
7798                err,
7799                ManifestError::NomeInvalid { ref nome, ref reason }
7800                    if nome == "my_app" && reason.contains('_')
7801            ),
7802            "got {err:?}"
7803        );
7804    }
7805
7806    #[test]
7807    fn validate_nome_rejects_dot() {
7808        // A `:nome` is a single DNS-1123 label, not a subdomain. The
7809        // "I want to namespace with `.`" footgun the gate redirects to
7810        // `-` via the shared predicate's reason wording.
7811        let c = caixa_with_nome("team.app");
7812        let err = c.validate_nome().unwrap_err();
7813        assert!(
7814            matches!(
7815                err,
7816                ManifestError::NomeInvalid { ref nome, ref reason }
7817                    if nome == "team.app" && reason.contains('.')
7818            ),
7819            "got {err:?}"
7820        );
7821    }
7822
7823    #[test]
7824    fn validate_nome_rejects_leading_hyphen() {
7825        // DNS-1123 boundary rule: the label must start with an ASCII
7826        // alphanumeric. Pin the leading-`-` arm explicitly.
7827        let c = caixa_with_nome("-app");
7828        let err = c.validate_nome().unwrap_err();
7829        assert!(
7830            matches!(
7831                err,
7832                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
7833            ),
7834            "got {err:?}"
7835        );
7836    }
7837
7838    #[test]
7839    fn validate_nome_rejects_trailing_hyphen() {
7840        // Symmetric arm of the boundary rule, pinned separately so a
7841        // future relaxation that only checks the leading position
7842        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
7843        // and `_with_trailing_hyphen` on the supervisor / aplicacao
7844        // axes.
7845        let c = caixa_with_nome("app-");
7846        let err = c.validate_nome().unwrap_err();
7847        assert!(
7848            matches!(
7849                err,
7850                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
7851            ),
7852            "got {err:?}"
7853        );
7854    }
7855
7856    #[test]
7857    fn validate_nome_rejects_unicode() {
7858        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
7859        // bytes are rejected by the K8s apiserver on every name axis.
7860        let c = caixa_with_nome("café");
7861        let err = c.validate_nome().unwrap_err();
7862        assert!(
7863            matches!(
7864                err,
7865                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
7866            ),
7867            "got {err:?}"
7868        );
7869    }
7870
7871    #[test]
7872    fn validate_nome_rejects_whitespace() {
7873        // The paste-from-sketch / paste-from-spec footgun. Internal
7874        // whitespace is rejected by every K8s name axis.
7875        let c = caixa_with_nome("my app");
7876        let err = c.validate_nome().unwrap_err();
7877        assert!(
7878            matches!(
7879                err,
7880                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
7881            ),
7882            "got {err:?}"
7883        );
7884    }
7885
7886    #[test]
7887    fn validate_nome_rejects_too_long() {
7888        // 64-byte boundary pin: the K8s apiserver rejects any
7889        // `metadata.name` over 63 bytes at admission; the diagnostic
7890        // names both the 63-byte cap and the actual length so the
7891        // author can shorten in one edit. Mirrors `_too_long` on the
7892        // peer member-/cluster-/child-name axes.
7893        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
7894        let c = caixa_with_nome(&over);
7895        let err = c.validate_nome().unwrap_err();
7896        let ManifestError::NomeInvalid { nome, reason } = err else {
7897            panic!("expected NomeInvalid for over-cap :nome");
7898        };
7899        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
7900        assert!(
7901            reason.contains("63") && reason.contains("64"),
7902            "diagnostic must name the cap + actual length, got {reason:?}"
7903        );
7904    }
7905
7906    #[test]
7907    fn nome_max_length_validates() {
7908        // The 63-byte cap exactly — the boundary-accepting case pinned
7909        // alongside `validate_nome_rejects_too_long` so a future cap
7910        // shift surfaces both arms simultaneously. Mirrors
7911        // `membro_caixa_max_length_validates`,
7912        // `placement_cluster_max_length_validates`,
7913        // `child_caixa_max_length_validates`.
7914        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
7915        caixa_with_nome(&at_cap).validate_nome().unwrap();
7916    }
7917
7918    #[test]
7919    fn nome_empty_takes_precedence_over_invalid() {
7920        // Order pin: the empty arm fires before the predicate is
7921        // consulted. Empty < invalid in self-locating-ness — the
7922        // narrower `NomeEmpty` diagnostic doesn't carry a useless
7923        // `nome: ""` reference into the parser-shaped reason. Mirrors
7924        // `membro_caixa_empty_takes_precedence_over_invalid` on the
7925        // peer axis (3f9d7a0).
7926        let c = caixa_with_nome("");
7927        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
7928    }
7929
7930    #[test]
7931    fn nome_invalid_diagnostic_carries_offending_nome() {
7932        // Diagnostic-shape pin: the error names the offending `:nome`
7933        // verbatim with a non-empty parser-shaped reason, so a `feira
7934        // lint` run can render the diagnostic without re-parsing.
7935        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
7936        let c = caixa_with_nome("MyApp");
7937        let err = c.validate_nome().unwrap_err();
7938        let ManifestError::NomeInvalid { nome, reason } = err else {
7939            panic!("expected NomeInvalid variant");
7940        };
7941        assert_eq!(nome, "MyApp");
7942        assert!(
7943            !reason.is_empty(),
7944            "NomeInvalid `reason` must carry the predicate's wording verbatim"
7945        );
7946    }
7947
7948    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
7949    //
7950    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
7951    // via DNS-1123; this second-axis gate caps the joint
7952    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
7953    // canonical [`crate::lareira_chart_name`] helper's doc comment
7954    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
7955    // "the M4 admission webhook will pin the joint-length invariant
7956    // when it lands". These tests pin it at the manifest-validate
7957    // layer instead, fail-before-pass-after on the 56-byte boundary.
7958
7959    #[test]
7960    fn validate_nome_chart_name_budget_accepts_canonical_template() {
7961        // Positive control: the bare `feira init`-style template's
7962        // `:nome` ("demo") sits far below the cap; the gate must not
7963        // regress this baseline. Same shape every peer
7964        // value-shape-gate baseline pin uses.
7965        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7966        c.validate_nome_chart_name_budget().unwrap();
7967    }
7968
7969    #[test]
7970    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
7971        // Positive-set sweep across the canonical author surface every
7972        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
7973        // `worker`, the `checkout-aplicacao` example members, the
7974        // `example-attest` caixa-tatara fixture). Every value sits
7975        // far below the 55-byte per-`:nome` budget. Same shape every
7976        // peer per-axis baseline pin uses.
7977        for nome in [
7978            "hello-rio",
7979            "cart",
7980            "checkout",
7981            "worker",
7982            "example-attest",
7983            "demo",
7984            "a",
7985        ] {
7986            caixa_with_nome(nome)
7987                .validate_nome_chart_name_budget()
7988                .unwrap_or_else(|e| {
7989                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
7990                });
7991        }
7992    }
7993
7994    #[test]
7995    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
7996        // Boundary-accepting case at the 55-byte per-`:nome` budget —
7997        // the joint chart name is exactly 63 bytes, the DNS-1123 label
7998        // cap. Pinned alongside the rejecting-arm test so a future cap
7999        // shift surfaces both arms simultaneously. Mirrors
8000        // `nome_max_length_validates` on the peer bare-`:nome` axis.
8001        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
8002        caixa_with_nome(&at_cap)
8003            .validate_nome_chart_name_budget()
8004            .unwrap();
8005    }
8006
8007    #[test]
8008    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
8009        // Fail-before-pass-after pin on the 56-byte boundary: the
8010        // smallest `:nome` length that overflows the joint chart-name
8011        // cap. The inner [`is_dns_1123_label`] gate
8012        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
8013        // this gate it silently passed the manifest-validate cascade
8014        // and surfaced as a `helm lint` / apiserver rejection on the
8015        // rendered chart name far from the source `caixa.lisp`, with
8016        // no field naming the overflow. With this gate the diagnostic
8017        // names the offending `:nome` verbatim alongside the rendered
8018        // chart name and the budget, so the author can shorten in one
8019        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
8020        // bare-`:nome` axis.
8021        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8022        let c = caixa_with_nome(&over);
8023        let err = c.validate_nome_chart_name_budget().unwrap_err();
8024        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
8025            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
8026        };
8027        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8028        assert_eq!(nome, over);
8029        assert!(
8030            reason.contains("63") && reason.contains("64") && reason.contains("55"),
8031            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
8032             and the per-`:nome` budget (55), got {reason:?}"
8033        );
8034    }
8035
8036    #[test]
8037    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
8038        // The 63-byte `:nome` boundary — passes the bare-`:nome`
8039        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
8040        // joint chart name that overflows the DNS-1123 label cap
8041        // structurally. The most stringent fail-before-pass-after
8042        // surface: every `:nome` in the 56..=63-byte range passed the
8043        // prior cascade and broke at admission.
8044        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
8045        let c = caixa_with_nome(&bare_max);
8046        // The bare-`:nome` gate accepts the 63-byte length.
8047        c.validate_nome().unwrap();
8048        // The new joint-length gate rejects it.
8049        let err = c.validate_nome_chart_name_budget().unwrap_err();
8050        assert!(
8051            matches!(
8052                err,
8053                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
8054                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
8055            ),
8056            "got {err:?}"
8057        );
8058    }
8059
8060    #[test]
8061    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
8062        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
8063        // name appears verbatim in the diagnostic so the author sees
8064        // exactly the string the apiserver / `helm lint` would have
8065        // rejected — no re-derivation required to grep the source.
8066        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
8067        // on the bare-`:nome` axis.
8068        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
8069        let c = caixa_with_nome(&over);
8070        let err = c.validate_nome_chart_name_budget().unwrap_err();
8071        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
8072            panic!("expected NomeChartNameBudgetExceeded variant");
8073        };
8074        assert_eq!(nome, over);
8075        let expected_chart = crate::lareira_chart_name(&over);
8076        assert!(
8077            reason.contains(&expected_chart),
8078            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
8079             got {reason:?}"
8080        );
8081        assert!(
8082            reason.contains("lareira-"),
8083            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
8084        );
8085    }
8086
8087    #[test]
8088    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
8089        // Order pin on the layout cascade: the narrower
8090        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
8091        // joint-length budget. A structurally-malformed `:nome` (here:
8092        // uppercase) surfaces its specific shape error rather than
8093        // the chart-name-budget error, even when the joint length
8094        // would also overflow — the narrower diagnostic is more
8095        // self-locating. Mirrors the cascade-precedence pins peer
8096        // gates already use (e.g. `EntradaParaEmpty` before
8097        // `EntradaParaInvalid`).
8098        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8099        let c = caixa_with_nome(&over);
8100        // The bare-shape gate fires first.
8101        let err = c.validate_nome().unwrap_err();
8102        assert!(
8103            matches!(err, ManifestError::NomeInvalid { .. }),
8104            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
8105        );
8106        // And the layout verify cascade surfaces that diagnostic, not
8107        // the budget arm. Inject a path-exists oracle so the cascade
8108        // gets past the manifest-presence check and into the
8109        // value-shape gates.
8110        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
8111        let err = crate::LayoutInvariants::verify(
8112            &layout,
8113            &c,
8114            std::path::Path::new("/tmp/caixa-test-fake-root"),
8115        )
8116        .unwrap_err();
8117        let issue = err.to_string();
8118        assert!(
8119            issue.contains("DNS-1123") || issue.contains("uppercase"),
8120            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
8121             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
8122        );
8123    }
8124
8125    #[test]
8126    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
8127        // Cross-axis envelope pin: the layout cascade wraps both
8128        // bare-`:nome` and joint-length-`:nome` failures through the
8129        // same [`LayoutError::NomeViolation`] envelope, since both
8130        // arms are on the `:nome` axis. The user's diagnostic stays
8131        // self-locating ("which axis"), and a future consumer that
8132        // dispatches on the layout-error variant (e.g. a `feira lint`
8133        // exit-code mapping) sees a single per-axis envelope. The
8134        // wrapped `issue:` carries the full inner diagnostic.
8135        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8136        let c = caixa_with_nome(&over);
8137        // The bare-shape gate accepts.
8138        c.validate_nome().unwrap();
8139        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
8140        let err = crate::LayoutInvariants::verify(
8141            &layout,
8142            &c,
8143            std::path::Path::new("/tmp/caixa-test-fake-root"),
8144        )
8145        .unwrap_err();
8146        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
8147            panic!("expected LayoutError::NomeViolation, got {err:?}");
8148        };
8149        assert_eq!(caixa, over);
8150        assert!(
8151            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
8152            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
8153        );
8154    }
8155
8156    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
8157
8158    fn caixa_with_versao(versao: &str) -> Caixa {
8159        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8160        c.versao = versao.to_string();
8161        c
8162    }
8163
8164    #[test]
8165    fn validate_versao_accepts_canonical_template() {
8166        // Positive control: the bare `feira init`-style template's
8167        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
8168        // must not regress this baseline shape. A future tightening of
8169        // the accepted set surfaces here as a test failure first.
8170        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8171        c.validate_versao().unwrap();
8172    }
8173
8174    #[test]
8175    fn validate_versao_accepts_canonical_forms() {
8176        // Positive-set sweep: each realistic SemVer-2 shape the
8177        // substrate's downstream consumers accept must pass — bare
8178        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
8179        // build metadata (`+build.42`), the combined form, and the
8180        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
8181        // the peer `:nome` axis (6c992f8).
8182        for versao in [
8183            "0.1.0",
8184            "0.0.0",
8185            "1.0.0",
8186            "0.2.0-rc.1",
8187            "1.0.0-alpha.0",
8188            "1.0.0+build.42",
8189            "1.0.0-rc.1+build.42",
8190            "10.20.30",
8191        ] {
8192            caixa_with_versao(versao)
8193                .validate_versao()
8194                .unwrap_or_else(|e| {
8195                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
8196                });
8197        }
8198    }
8199
8200    #[test]
8201    fn validate_versao_rejects_empty() {
8202        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
8203        // an empty `:versao` (the derive macro stores the raw String);
8204        // the gate's empty arm names the offending axis with a narrower
8205        // diagnostic than the `VersaoInvalid` parse arm would emit.
8206        // Mirrors `validate_nome_rejects_empty` (6c992f8).
8207        let c = caixa_with_versao("");
8208        let err = c.validate_versao().unwrap_err();
8209        assert_eq!(err, ManifestError::VersaoEmpty);
8210    }
8211
8212    #[test]
8213    fn validate_versao_rejects_git_tag_shape() {
8214        // The canonical "I copied the git tag verbatim" footgun —
8215        // `feira publish` *emits* `v<versao>` git tags, so a leaked
8216        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
8217        // shift every downstream consumer's version axis. `semver`
8218        // rejects the leading `v` at parse time; the gate moves the
8219        // diagnostic to the source `caixa.lisp`.
8220        let c = caixa_with_versao("v0.1.0");
8221        let err = c.validate_versao().unwrap_err();
8222        let ManifestError::VersaoInvalid { versao, reason } = err else {
8223            panic!("expected VersaoInvalid for git-tag-shape :versao");
8224        };
8225        assert_eq!(versao, "v0.1.0");
8226        assert!(
8227            !reason.is_empty(),
8228            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
8229        );
8230    }
8231
8232    #[test]
8233    fn validate_versao_rejects_missing_patch() {
8234        // The canonical "I shortened it" footgun — SemVer-2 requires
8235        // three parts. Cargo's `version =` field accepts the shortened
8236        // form as a requirement, conflating the two leaks across the
8237        // typed `:deps :versao` vs top-level `:versao` axes; the gate
8238        // pins the top-level axis to the strict three-part shape.
8239        let c = caixa_with_versao("0.1");
8240        let err = c.validate_versao().unwrap_err();
8241        assert!(
8242            matches!(
8243                err,
8244                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
8245            ),
8246            "got {err:?}"
8247        );
8248    }
8249
8250    #[test]
8251    fn validate_versao_rejects_requirement_shape() {
8252        // The canonical "I leaked a requirement into a version" footgun —
8253        // the typed `:deps :versao` / `:membros :versao` axes accept
8254        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
8255        // concrete `Version`. Without this gate the two typed surfaces
8256        // would silently overlap, and a top-level `^0.1` would surface
8257        // at `helm install` time as a Chart.yaml version rejection far
8258        // from the source `caixa.lisp`.
8259        let c = caixa_with_versao("^0.1");
8260        let err = c.validate_versao().unwrap_err();
8261        assert!(
8262            matches!(
8263                err,
8264                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
8265            ),
8266            "got {err:?}"
8267        );
8268    }
8269
8270    #[test]
8271    fn validate_versao_rejects_docker_tag_shape() {
8272        // The "I confused it with a docker tag" footgun — `latest`,
8273        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
8274        // SemVer rejects at parse time; the gate moves the diagnostic
8275        // to the source `caixa.lisp`.
8276        for bad in ["latest", "main", "stable"] {
8277            let c = caixa_with_versao(bad);
8278            let err = c.validate_versao().unwrap_err();
8279            assert!(
8280                matches!(
8281                    err,
8282                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
8283                ),
8284                "got {err:?} for {bad:?}"
8285            );
8286        }
8287    }
8288
8289    #[test]
8290    fn validate_versao_rejects_four_part_form() {
8291        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
8292        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
8293        // semver crate rejects the extra `.0` at parse time.
8294        let c = caixa_with_versao("0.1.0.0");
8295        let err = c.validate_versao().unwrap_err();
8296        assert!(
8297            matches!(
8298                err,
8299                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
8300            ),
8301            "got {err:?}"
8302        );
8303    }
8304
8305    #[test]
8306    fn versao_empty_takes_precedence_over_invalid() {
8307        // Order pin: the empty arm fires before the parser is consulted.
8308        // Empty < invalid in self-locating-ness — the narrower
8309        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
8310        // reference into the parser-shaped reason. Mirrors
8311        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
8312        // peer axis.
8313        let c = caixa_with_versao("");
8314        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
8315    }
8316
8317    #[test]
8318    fn versao_invalid_diagnostic_carries_offending_versao() {
8319        // Diagnostic-shape pin: the error names the offending `:versao`
8320        // verbatim with a non-empty parser-shaped reason, so a `feira
8321        // lint` run can render the diagnostic without re-parsing.
8322        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
8323        let c = caixa_with_versao("v0.1.0");
8324        let err = c.validate_versao().unwrap_err();
8325        let ManifestError::VersaoInvalid { versao, reason } = err else {
8326            panic!("expected VersaoInvalid variant");
8327        };
8328        assert_eq!(versao, "v0.1.0");
8329        assert!(
8330            !reason.is_empty(),
8331            "VersaoInvalid `reason` must carry the parser's wording verbatim"
8332        );
8333    }
8334
8335    #[test]
8336    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
8337        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
8338        // for `:upgrade-from :from` must also pass `validate_versao` —
8339        // the two `:versao`-typed surfaces (top-level `:versao`,
8340        // `:upgrade-from :from`) consume the *same* `semver::Version`
8341        // parser, so they must agree on the accepted set. Without this
8342        // pin, a future tightening of one axis could silently diverge
8343        // from the other. Mirrors the `:versao` requirement-axis
8344        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
8345        // commits established.
8346        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
8347            // From the canonical UpgradeFromEntry round-trip fixture
8348            // (`upgrade::tests::round_trip_load_module` peers).
8349            let entry = crate::UpgradeFromEntry {
8350                from: versao.to_string(),
8351                instructions: Vec::new(),
8352            };
8353            entry
8354                .validate()
8355                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
8356            caixa_with_versao(versao)
8357                .validate_versao()
8358                .unwrap_or_else(|e| {
8359                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
8360                });
8361        }
8362    }
8363
8364    // ── Caixa::validate_restart_window — supervisor restart-window
8365    //    folds through the shared `supervisor::duration_codec` ────────
8366
8367    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
8368        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8369        c.kind = CaixaKind::Supervisor;
8370        c.restart_window = window.map(str::to_string);
8371        c
8372    }
8373
8374    #[test]
8375    fn validate_restart_window_accepts_none() {
8376        // The canonical "omit the slot to express no reset" shape — a
8377        // `None` raw string is the absence of the typed
8378        // `:restart-window` slot, which is exactly the SupervisorSpec
8379        // "never reset" semantics. The gate must be a no-op here; a
8380        // future tightening that rejected `None` would force every
8381        // supervisor caixa to authoring-time pin a window even when
8382        // the OTP semantics call for none.
8383        caixa_with_restart_window(None)
8384            .validate_restart_window()
8385            .unwrap();
8386    }
8387
8388    #[test]
8389    fn validate_restart_window_accepts_canonical_forms() {
8390        // Positive-set sweep across the canonical authoring units the
8391        // shared `supervisor::duration_codec::parse` accepts —
8392        // matches the codec-side `parse_accepts_integer_canonical_units`
8393        // pin in supervisor::tests so a future codec-side tightening
8394        // surfaces simultaneously on both axes.
8395        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
8396            caixa_with_restart_window(Some(window))
8397                .validate_restart_window()
8398                .unwrap_or_else(|e| {
8399                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
8400                });
8401        }
8402    }
8403
8404    #[test]
8405    fn validate_restart_window_rejects_fractional_seconds() {
8406        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
8407        // as f64 to 1.5 → renders back as `"1500ms"` on first
8408        // serialize). Prior to the fold + this gate, the inline
8409        // `parse_window_inline` accepted f64 magnitudes and silently
8410        // produced a `Duration::from_secs_f64(1.5)`, divergent from
8411        // the shared codec's integer-magnitude discipline on the
8412        // serde-routed siblings. The gate now surfaces a self-locating
8413        // diagnostic at the manifest layer.
8414        let err = caixa_with_restart_window(Some("1.5s"))
8415            .validate_restart_window()
8416            .unwrap_err();
8417        let ManifestError::RestartWindowMalformed {
8418            restart_window,
8419            reason,
8420        } = err
8421        else {
8422            panic!("expected RestartWindowMalformed for fractional seconds");
8423        };
8424        assert_eq!(restart_window, "1.5s");
8425        assert!(
8426            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
8427            "diagnostic must carry shared-codec wording, got {reason:?}"
8428        );
8429    }
8430
8431    #[test]
8432    fn validate_restart_window_rejects_decimal_shaped_integer() {
8433        // The `"1.0s"` class — numerically `1s` exactly, but the
8434        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
8435        // gets the same canonical-form diagnostic.
8436        let err = caixa_with_restart_window(Some("1.0s"))
8437            .validate_restart_window()
8438            .unwrap_err();
8439        assert!(
8440            matches!(
8441                err,
8442                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8443                    if restart_window == "1.0s"
8444            ),
8445            "got {err:?}"
8446        );
8447    }
8448
8449    #[test]
8450    fn validate_restart_window_rejects_half_unit_minute() {
8451        // `"0.5m"` is the unit-fraction footgun — author writes a
8452        // human-readable half-minute, the prior inline parser silently
8453        // produced `Duration::from_secs_f64(30.0)` and serde
8454        // re-emitted as `"30s"`, rewriting author intent. The gate
8455        // closes the loop at the manifest layer.
8456        let err = caixa_with_restart_window(Some("0.5m"))
8457            .validate_restart_window()
8458            .unwrap_err();
8459        let ManifestError::RestartWindowMalformed {
8460            restart_window,
8461            reason,
8462        } = err
8463        else {
8464            panic!("expected RestartWindowMalformed");
8465        };
8466        assert_eq!(restart_window, "0.5m");
8467        assert!(
8468            reason.contains("\"30s\""),
8469            "diagnostic must point at the canonical-form remediation, got {reason:?}"
8470        );
8471    }
8472
8473    #[test]
8474    fn validate_restart_window_rejects_leading_sign() {
8475        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
8476        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
8477        // and was caught by the `num < 0.0` arm which silently
8478        // returned `None`, dropping the author-supplied window). The
8479        // shared codec's digit-only gate rejects both with a unified
8480        // canonical-form diagnostic; the manifest-layer wrapper names
8481        // the offending value.
8482        for bad in ["+30s", "-30s"] {
8483            let err = caixa_with_restart_window(Some(bad))
8484                .validate_restart_window()
8485                .unwrap_err();
8486            assert!(
8487                matches!(
8488                    err,
8489                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
8490                        if restart_window == bad
8491                ),
8492                "got {err:?} for {bad:?}"
8493            );
8494        }
8495    }
8496
8497    #[test]
8498    fn validate_restart_window_rejects_unknown_unit() {
8499        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
8500        // unit dispatch surfaces an `unknown duration unit` reason;
8501        // the manifest-layer wrapper names the offending value.
8502        let err = caixa_with_restart_window(Some("30x"))
8503            .validate_restart_window()
8504            .unwrap_err();
8505        let ManifestError::RestartWindowMalformed {
8506            restart_window,
8507            reason,
8508        } = err
8509        else {
8510            panic!("expected RestartWindowMalformed for unknown unit");
8511        };
8512        assert_eq!(restart_window, "30x");
8513        assert!(
8514            reason.contains("unknown duration unit"),
8515            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
8516        );
8517    }
8518
8519    #[test]
8520    fn validate_restart_window_rejects_garbage() {
8521        // Pure non-numeric magnitude (`"abc"`) falls through to the
8522        // shared codec's narrower `"bad duration magnitude"` arm. Same
8523        // diagnostic shape as the codec-side
8524        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
8525        let err = caixa_with_restart_window(Some("abc"))
8526            .validate_restart_window()
8527            .unwrap_err();
8528        let ManifestError::RestartWindowMalformed {
8529            restart_window,
8530            reason,
8531        } = err
8532        else {
8533            panic!("expected RestartWindowMalformed for garbage");
8534        };
8535        assert_eq!(restart_window, "abc");
8536        assert!(
8537            reason.contains("bad duration magnitude"),
8538            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
8539        );
8540    }
8541
8542    #[test]
8543    fn validate_restart_window_rejects_empty_string() {
8544        // The empty-after-trim edge case — distinct from the `None`
8545        // canonical "omit the slot" shape. The shared codec's
8546        // digit-only gate refuses an empty magnitude; the manifest
8547        // layer names the offending `""` so the author can grep for
8548        // the literal empty value in their `caixa.lisp` and either
8549        // remove the slot (the canonical "no reset" shape) or pin a
8550        // positive duration.
8551        let err = caixa_with_restart_window(Some(""))
8552            .validate_restart_window()
8553            .unwrap_err();
8554        assert!(
8555            matches!(
8556                err,
8557                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8558                    if restart_window.is_empty()
8559            ),
8560            "got {err:?}"
8561        );
8562    }
8563
8564    #[test]
8565    fn validate_restart_window_diagnostic_carries_offending_value() {
8566        // Diagnostic-shape pin (peer with
8567        // `nome_invalid_diagnostic_carries_offending_nome` /
8568        // `versao_invalid_diagnostic_carries_offending_versao`): the
8569        // error names the offending raw `:restart-window` verbatim
8570        // with a non-empty shared-codec-shaped reason, so a `feira
8571        // lint` run can render the diagnostic without re-parsing.
8572        let err = caixa_with_restart_window(Some("1.5s"))
8573            .validate_restart_window()
8574            .unwrap_err();
8575        let ManifestError::RestartWindowMalformed {
8576            restart_window,
8577            reason,
8578        } = err
8579        else {
8580            panic!("expected RestartWindowMalformed variant");
8581        };
8582        assert_eq!(restart_window, "1.5s");
8583        assert!(
8584            !reason.is_empty(),
8585            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
8586        );
8587    }
8588
8589    #[test]
8590    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
8591        // Behavioral parity pin after the fold (`parse_window_inline`
8592        // deletion): the canonical `"60s"` still produces
8593        // `Duration::from_secs(60)` on the typed view — the fold is
8594        // semantically equivalent to the prior inline parser on the
8595        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
8596        // pin, narrowed to the parser-side contract.
8597        let c = caixa_with_restart_window(Some("60s"));
8598        let view = c.supervisor_view().expect("Supervisor kind has a view");
8599        assert_eq!(
8600            view.restart_window,
8601            Some(std::time::Duration::from_secs(60))
8602        );
8603    }
8604
8605    #[test]
8606    fn supervisor_view_soft_swallows_what_validate_rejects() {
8607        // Parity pin between the view-construction path and the
8608        // manifest-level validator: the same `"1.5s"` that surfaces
8609        // `RestartWindowMalformed` at `validate_restart_window` time
8610        // becomes `restart_window: None` on the typed view (the fold
8611        // preserves the existing best-effort shape of `supervisor_view`).
8612        // The contract is: a layout-verifier / `feira lint` flow that
8613        // cares about the malformed-window axis MUST consult
8614        // `validate_restart_window` — relying solely on the view's
8615        // `None` swallows the diagnostic silently. This pin makes the
8616        // expectation a typed invariant.
8617        let c = caixa_with_restart_window(Some("1.5s"));
8618        let view = c.supervisor_view().expect("Supervisor kind has a view");
8619        assert_eq!(
8620            view.restart_window, None,
8621            "view-construction path soft-swallows the parse error to None"
8622        );
8623        // And the manifest-level validator does NOT soft-swallow:
8624        assert!(
8625            matches!(
8626                c.validate_restart_window().unwrap_err(),
8627                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8628                    if restart_window == "1.5s"
8629            ),
8630            "validator must surface the offending value",
8631        );
8632    }
8633
8634    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
8635
8636    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
8637        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8638        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
8639        c.exe = exe.into_iter().map(String::from).collect();
8640        c.servicos = servicos.into_iter().map(String::from).collect();
8641        c
8642    }
8643
8644    #[test]
8645    fn validate_code_paths_accepts_canonical_template() {
8646        // The bare `Caixa::template` shape is the gate's identity element
8647        // on the canonical authoring shape — `:bibliotecas
8648        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
8649        // that the gate is non-disruptive against every existing caixa.
8650        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8651        c.validate_code_paths().unwrap();
8652    }
8653
8654    #[test]
8655    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
8656        // Positive control sweep: a canonical-shaped path on every slot
8657        // passes. Mirrors the peer
8658        // `behavior::validate_every_slot_relative_is_ok` pin.
8659        let c = caixa_with_code_paths(
8660            vec!["lib/demo.lisp", "lib/helpers.lisp"],
8661            vec!["exe/demo", "exe/tool"],
8662            vec!["servicos/demo.computeunit.yaml"],
8663        );
8664        c.validate_code_paths().unwrap();
8665    }
8666
8667    #[test]
8668    fn validate_code_paths_accepts_all_empty_lists() {
8669        // The empty-list identity element: every Caixa with no declared
8670        // code paths trivially passes (Supervisor / Aplicacao kinds rely
8671        // on this — the OwnCode gate already rejected them before the
8672        // path-shape gate runs in the layout, but the validator itself
8673        // must accept the empty shape).
8674        let c = caixa_with_code_paths(vec![], vec![], vec![]);
8675        c.validate_code_paths().unwrap();
8676    }
8677
8678    #[test]
8679    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
8680        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
8681        let err = c.validate_code_paths().unwrap_err();
8682        assert!(
8683            matches!(
8684                err,
8685                ManifestError::CodePathEmpty {
8686                    slot: ":bibliotecas"
8687                }
8688            ),
8689            "got {err:?}",
8690        );
8691    }
8692
8693    #[test]
8694    fn validate_code_paths_rejects_empty_exe_entry() {
8695        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
8696        let err = c.validate_code_paths().unwrap_err();
8697        assert!(
8698            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
8699            "got {err:?}",
8700        );
8701    }
8702
8703    #[test]
8704    fn validate_code_paths_rejects_empty_servicos_entry() {
8705        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
8706        let err = c.validate_code_paths().unwrap_err();
8707        assert!(
8708            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
8709            "got {err:?}",
8710        );
8711    }
8712
8713    #[test]
8714    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
8715        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
8716        // so an absolute path that resolves on disk silently passes the
8717        // layout's existence check — the canonical sandbox-escape on
8718        // the biblioteca axis.
8719        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8720        let err = c.validate_code_paths().unwrap_err();
8721        let ManifestError::CodePathAbsolute { slot, path } = err else {
8722            panic!("expected CodePathAbsolute, got {err:?}");
8723        };
8724        assert_eq!(slot, ":bibliotecas");
8725        assert_eq!(path, PathBuf::from("/etc/passwd"));
8726    }
8727
8728    #[test]
8729    fn validate_code_paths_rejects_absolute_exe_entry() {
8730        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
8731        let err = c.validate_code_paths().unwrap_err();
8732        let ManifestError::CodePathAbsolute { slot, path } = err else {
8733            panic!("expected CodePathAbsolute, got {err:?}");
8734        };
8735        assert_eq!(slot, ":exe");
8736        assert_eq!(path, PathBuf::from("/usr/bin/env"));
8737    }
8738
8739    #[test]
8740    fn validate_code_paths_rejects_absolute_servicos_entry() {
8741        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
8742        let err = c.validate_code_paths().unwrap_err();
8743        let ManifestError::CodePathAbsolute { slot, path } = err else {
8744            panic!("expected CodePathAbsolute, got {err:?}");
8745        };
8746        assert_eq!(slot, ":servicos");
8747        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
8748    }
8749
8750    #[test]
8751    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
8752        // Canonical "I want a lib from a sibling caixa" footgun on the
8753        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
8754        // downstream, so a leading `..` traverses to the parent of the
8755        // caixa root with no diagnostic at layout time if the resolved
8756        // target exists.
8757        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
8758        let err = c.validate_code_paths().unwrap_err();
8759        let ManifestError::CodePathParentEscape { slot, path } = err else {
8760            panic!("expected CodePathParentEscape, got {err:?}");
8761        };
8762        assert_eq!(slot, ":bibliotecas");
8763        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
8764    }
8765
8766    #[test]
8767    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
8768        // Mid-path `..` defeats the layout's component-aware
8769        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
8770        // `starts_with(<root>/exe)` is true, but the canonical resolution
8771        // lives outside the caixa root. Caught regardless of where the
8772        // `..` sits — mirrors the peer
8773        // `behavior::validate_rejects_parent_escape_mid_path` pin.
8774        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
8775        let err = c.validate_code_paths().unwrap_err();
8776        let ManifestError::CodePathParentEscape { slot, path } = err else {
8777            panic!("expected CodePathParentEscape, got {err:?}");
8778        };
8779        assert_eq!(slot, ":exe");
8780        assert_eq!(path, PathBuf::from("exe/../../escape"));
8781    }
8782
8783    #[test]
8784    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
8785        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
8786        let err = c.validate_code_paths().unwrap_err();
8787        let ManifestError::CodePathParentEscape { slot, path } = err else {
8788            panic!("expected CodePathParentEscape, got {err:?}");
8789        };
8790        assert_eq!(slot, ":servicos");
8791        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
8792    }
8793
8794    #[test]
8795    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
8796        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
8797        // `:servicos`. A manifest with malformed entries on all three
8798        // surfaces surfaces the `:bibliotecas` defect first, mirroring
8799        // the canonical declaration order
8800        // `Caixa::declared_foreign_code_slots` already establishes for
8801        // the foreign-code-slot diagnostic.
8802        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
8803        let err = c.validate_code_paths().unwrap_err();
8804        assert!(
8805            matches!(
8806                err,
8807                ManifestError::CodePathEmpty {
8808                    slot: ":bibliotecas"
8809                }
8810            ),
8811            "got {err:?}",
8812        );
8813    }
8814
8815    #[test]
8816    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
8817        // Within-slot precedence pin: empty → absolute → parent-escape,
8818        // matching the [`PathShapeViolation`] arm-ordering every peer
8819        // `is_sandboxed_relative_path` caller follows (b0c8389
8820        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
8821        // `:bibliotecas` list whose first entry is empty *and* whose
8822        // later entries are absolute/parent-escape surfaces the empty
8823        // arm first, on the lexicographically-earliest offending entry.
8824        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
8825        let err = c.validate_code_paths().unwrap_err();
8826        assert!(
8827            matches!(
8828                err,
8829                ManifestError::CodePathEmpty {
8830                    slot: ":bibliotecas"
8831                }
8832            ),
8833            "got {err:?}",
8834        );
8835    }
8836
8837    #[test]
8838    fn validate_code_paths_first_offender_per_slot_wins() {
8839        // Within a single slot, the first declaration-order offender
8840        // surfaces — pins that the gate is left-to-right deterministic
8841        // (peer of every `*_first_collision_*` pin on duplicate gates).
8842        let c = caixa_with_code_paths(
8843            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
8844            vec![],
8845            vec![],
8846        );
8847        let err = c.validate_code_paths().unwrap_err();
8848        let ManifestError::CodePathAbsolute { slot, path } = err else {
8849            panic!("expected CodePathAbsolute, got {err:?}");
8850        };
8851        assert_eq!(slot, ":bibliotecas");
8852        assert_eq!(path, PathBuf::from("/etc/escape"));
8853    }
8854
8855    #[test]
8856    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
8857        // Diagnostic-shape pin (peer with
8858        // `nome_invalid_diagnostic_carries_offending_nome` /
8859        // `versao_invalid_diagnostic_carries_offending_versao`): the
8860        // error's Display surfaces both the offending `:slot` tag and
8861        // the offending path verbatim, so a `feira lint` run can render
8862        // the diagnostic without re-parsing.
8863        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
8864        let rendered = c.validate_code_paths().unwrap_err().to_string();
8865        assert!(
8866            rendered.contains(":bibliotecas"),
8867            "diagnostic must name the offending slot: {rendered}",
8868        );
8869        assert!(
8870            rendered.contains("/etc/passwd"),
8871            "diagnostic must quote the offending path: {rendered}",
8872        );
8873    }
8874
8875    #[test]
8876    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
8877        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
8878        // axis. Without the gate `feira build` re-parses the same lib
8879        // twice, wasting work and silently masking the author's intent
8880        // to declare a *second* biblioteca.
8881        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
8882        let err = c.validate_code_paths().unwrap_err();
8883        let ManifestError::CodePathDuplicate { slot, path } = err else {
8884            panic!("expected CodePathDuplicate, got {err:?}");
8885        };
8886        assert_eq!(slot, ":bibliotecas");
8887        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
8888    }
8889
8890    #[test]
8891    fn validate_code_paths_rejects_duplicate_exe_entry() {
8892        // Same footgun on the Binario surface. The future `caixa-flake`
8893        // emitter that materializes each `:exe` entry as a flake
8894        // `packages.<name>` derivation would collide on the duplicate
8895        // package key — surfaced here at the typed-validate layer with a
8896        // self-locating diagnostic instead.
8897        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
8898        let err = c.validate_code_paths().unwrap_err();
8899        let ManifestError::CodePathDuplicate { slot, path } = err else {
8900            panic!("expected CodePathDuplicate, got {err:?}");
8901        };
8902        assert_eq!(slot, ":exe");
8903        assert_eq!(path, PathBuf::from("exe/cli"));
8904    }
8905
8906    #[test]
8907    fn validate_code_paths_rejects_duplicate_servicos_entry() {
8908        // Same footgun on the Servico surface. The peer caixa-helm /
8909        // caixa-flux renderers refuse `:servicos.len() != 1` with the
8910        // narrower `UnsupportedServicoCount` diagnostic, but that
8911        // diagnostic surfaces "too many servicos" without naming
8912        // "duplicate entry" — the typed self-locating framing only lands
8913        // at this gate.
8914        let c = caixa_with_code_paths(
8915            vec![],
8916            vec![],
8917            vec![
8918                "servicos/demo.computeunit.yaml",
8919                "servicos/demo.computeunit.yaml",
8920            ],
8921        );
8922        let err = c.validate_code_paths().unwrap_err();
8923        let ManifestError::CodePathDuplicate { slot, path } = err else {
8924            panic!("expected CodePathDuplicate, got {err:?}");
8925        };
8926        assert_eq!(slot, ":servicos");
8927        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
8928    }
8929
8930    #[test]
8931    fn validate_code_paths_accepts_same_path_across_slots() {
8932        // Per-list scope pin: a `:bibliotecas` entry that happens to
8933        // collide with an `:exe` or `:servicos` entry as a *string* is
8934        // not a duplicate by this gate (each list gets its own HashSet),
8935        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
8936        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
8937        // shape on the dep axis). The structural `starts_with(<exe |
8938        // servicos>_dir)` fence at layout time prevents the realistic
8939        // cross-slot collision case from existing on disk, but the gate's
8940        // per-list scope is correct independent of that downstream fence.
8941        let c = caixa_with_code_paths(
8942            vec!["lib/x.lisp"],
8943            vec!["exe/x"],
8944            vec!["servicos/x.computeunit.yaml"],
8945        );
8946        c.validate_code_paths().unwrap();
8947    }
8948
8949    #[test]
8950    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
8951        // Within-slot ordering pin: structural defects (empty / absolute
8952        // / parent-escape) fire before the duplicate gate on the same
8953        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
8954        // surfaces the narrower `CodePathEmpty` for the empty entry
8955        // first, not the duplicate on the later pair — same arm-ordering
8956        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
8957        // `:autores` 86c769b, `:deps` 359fba5).
8958        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
8959        let err = c.validate_code_paths().unwrap_err();
8960        assert!(
8961            matches!(
8962                err,
8963                ManifestError::CodePathEmpty {
8964                    slot: ":bibliotecas"
8965                }
8966            ),
8967            "got {err:?}",
8968        );
8969    }
8970
8971    #[test]
8972    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
8973        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
8974        // duplicates surface before `:exe` duplicates, matching the
8975        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
8976        // order every peer per-slot diagnostic on this surface follows.
8977        let c = caixa_with_code_paths(
8978            vec!["lib/x.lisp", "lib/x.lisp"],
8979            vec!["exe/y", "exe/y"],
8980            vec![],
8981        );
8982        let err = c.validate_code_paths().unwrap_err();
8983        let ManifestError::CodePathDuplicate { slot, path } = err else {
8984            panic!("expected CodePathDuplicate, got {err:?}");
8985        };
8986        assert_eq!(slot, ":bibliotecas");
8987        assert_eq!(path, PathBuf::from("lib/x.lisp"));
8988    }
8989
8990    #[test]
8991    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
8992        // Diagnostic-shape pin (peer with
8993        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
8994        // on the structural arm): the duplicate-arm Display surfaces both
8995        // the offending `:slot` tag and the offending path verbatim, so a
8996        // `feira lint` run can render the diagnostic without re-parsing.
8997        let c = caixa_with_code_paths(
8998            vec![],
8999            vec![],
9000            vec![
9001                "servicos/demo.computeunit.yaml",
9002                "servicos/demo.computeunit.yaml",
9003            ],
9004        );
9005        let rendered = c.validate_code_paths().unwrap_err().to_string();
9006        assert!(
9007            rendered.contains(":servicos"),
9008            "diagnostic must name the offending slot: {rendered}",
9009        );
9010        assert!(
9011            rendered.contains("servicos/demo.computeunit.yaml"),
9012            "diagnostic must quote the offending path: {rendered}",
9013        );
9014    }
9015
9016    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
9017    //
9018    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
9019    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
9020    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
9021    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
9022    // at parse time — the same downstream consumer the peer `:behavior
9023    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
9024    // `:upgrade-from :state-change :script` (33cc830,
9025    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
9026    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
9027    // nix-built executable surface (`"exe/<name>"` shape per the canonical
9028    // [`crate::LayoutError::ExeOutsideDir`] error message and every
9029    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
9030    // is the `.computeunit.yaml` ComputeUnit-CR axis.
9031
9032    #[test]
9033    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
9034        // Canonical "I dragged the wrong file from the workspace tree"
9035        // footgun on the biblioteca axis. Without the gate `feira build`
9036        // hands the extensionless path to `tatara_lisp::read` and fails
9037        // with a parser-shaped diagnostic far from the source caixa.lisp,
9038        // with no field naming the offending `:bibliotecas` entry.
9039        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
9040            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9041            let err = c.validate_code_paths().unwrap_err();
9042            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9043                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9044            };
9045            assert_eq!(slot, ":bibliotecas");
9046            assert_eq!(path, PathBuf::from(relpath));
9047        }
9048    }
9049
9050    #[test]
9051    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
9052        // Wrong-extension sweep across common authoring footguns. Same
9053        // sweep posture as the peer
9054        // `behavior::validate_rejects_wrong_extension` (c97815a) and
9055        // `upgrade::tests::state_change_rejects_wrong_extension_script`
9056        // (33cc830) cases.
9057        for relpath in [
9058            "lib/demo.rs",
9059            "lib/demo.txt",
9060            "lib/demo.md",
9061            "lib/demo.json",
9062            "lib/demo.yaml",
9063            "lib/demo.toml",
9064            "lib/demo.lisp.bak",
9065            "lib/demo.lispx",
9066            "lib/demo.lis",
9067        ] {
9068            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9069            let err = c.validate_code_paths().unwrap_err();
9070            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9071                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9072            };
9073            assert_eq!(slot, ":bibliotecas");
9074            assert_eq!(path, PathBuf::from(relpath));
9075        }
9076    }
9077
9078    #[test]
9079    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
9080        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
9081        // contract. An uppercase `.LISP` shape that the layout's existence
9082        // check would (case-insensitively, on case-insensitive volumes)
9083        // match the on-disk file still mismatches the canonical form the
9084        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
9085        // contract. Mirrors the peer
9086        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
9087        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
9088        // (33cc830) sweeps.
9089        for relpath in [
9090            "lib/demo.LISP",
9091            "lib/demo.Lisp",
9092            "lib/demo.LiSp",
9093            "lib/demo.lISP",
9094        ] {
9095            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9096            let err = c.validate_code_paths().unwrap_err();
9097            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9098                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9099            };
9100            assert_eq!(slot, ":bibliotecas");
9101            assert_eq!(path, PathBuf::from(relpath));
9102        }
9103    }
9104
9105    #[test]
9106    fn validate_code_paths_accepts_canonical_lisp_shapes() {
9107        // Positive-control sweep through every canonical authoring shape
9108        // every in-tree fixture and the `Caixa::template` scaffold use.
9109        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
9110        // (c97815a) and the lifted predicate's own
9111        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
9112        // (33cc830).
9113        for relpath in [
9114            "lib/demo.lisp",
9115            "lib/handlers.lisp",
9116            "lib/migrations/v01-to-v02.lisp",
9117            "demo.lisp",
9118            "a.lisp",
9119            "./lib/demo.lisp",
9120            "lib/./handlers.lisp",
9121            "lib/migrations/v.0.1.lisp",
9122        ] {
9123            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9124            c.validate_code_paths()
9125                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
9126        }
9127    }
9128
9129    #[test]
9130    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
9131        // The file-type gate is per-slot — only `:bibliotecas` carries the
9132        // tatara-lisp-source contract. An extensionless `:exe` entry
9133        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
9134        // canonical shapes every in-tree fixture uses, and must continue
9135        // to pass validate. Pins that a future tightening that broadens
9136        // the `.lisp` gate to either axis surfaces as a test failure
9137        // rather than as a silent breaking change to existing valid
9138        // manifests.
9139        let c = caixa_with_code_paths(
9140            vec![],
9141            vec!["exe/demo", "exe/tool"],
9142            vec!["servicos/demo.computeunit.yaml"],
9143        );
9144        c.validate_code_paths().unwrap();
9145    }
9146
9147    #[test]
9148    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
9149        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
9150        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
9151        // sandbox-shape diagnostic first (the `.lisp` remediation would
9152        // be misleading when the offending path can never resolve under
9153        // the caixa root anyway). Mirrors the peer
9154        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
9155        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
9156        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
9157        // on `:upgrade-from :state-change :script` (33cc830).
9158        //
9159        // Empty wins (the strictly-smaller-scope structural arm).
9160        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
9161        assert!(
9162            matches!(
9163                c.validate_code_paths().unwrap_err(),
9164                ManifestError::CodePathEmpty {
9165                    slot: ":bibliotecas"
9166                }
9167            ),
9168            "empty must win over non-lisp-extension",
9169        );
9170        // Absolute wins (the path can't resolve under the caixa root).
9171        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
9172        let err = c.validate_code_paths().unwrap_err();
9173        let ManifestError::CodePathAbsolute { slot, .. } = err else {
9174            panic!("absolute must win over non-lisp-extension, got {err:?}");
9175        };
9176        assert_eq!(slot, ":bibliotecas");
9177        // ParentEscape wins (the path escapes the caixa root).
9178        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
9179        let err = c.validate_code_paths().unwrap_err();
9180        let ManifestError::CodePathParentEscape { slot, .. } = err else {
9181            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
9182        };
9183        assert_eq!(slot, ":bibliotecas");
9184    }
9185
9186    #[test]
9187    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
9188        // Within-slot precedence pin: the per-entry file-type shape gate
9189        // fires before the cross-entry duplicate gate, so the narrower
9190        // structural defect dominates the uniqueness diagnostic. A
9191        // `("lib/x.txt" "lib/x.txt")` shape surfaces
9192        // `CodePathNonLispExtension` on the first entry rather than
9193        // `CodePathDuplicate` on the pair — same posture every per-entry
9194        // shape-gate-precedes-duplicate cascade follows on this surface
9195        // (the empty / absolute / parent-escape arms already precede the
9196        // duplicate arm; the lifted file-type arm joins that set).
9197        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
9198        let err = c.validate_code_paths().unwrap_err();
9199        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9200            panic!("expected CodePathNonLispExtension, got {err:?}");
9201        };
9202        assert_eq!(slot, ":bibliotecas");
9203        assert_eq!(path, PathBuf::from("lib/x.txt"));
9204    }
9205
9206    #[test]
9207    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
9208        // Diagnostic-shape pin (peer with
9209        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
9210        // on the sandbox-shape arms and
9211        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
9212        // on the duplicate arm): the file-type-arm Display surfaces both
9213        // the offending `:slot` tag, the offending path verbatim, and the
9214        // expected `.lisp` extension named in the remediation text, so a
9215        // `feira lint` run can render the diagnostic without re-parsing.
9216        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
9217        let rendered = c.validate_code_paths().unwrap_err().to_string();
9218        assert!(
9219            rendered.contains(":bibliotecas"),
9220            "diagnostic must name the offending slot: {rendered}",
9221        );
9222        assert!(
9223            rendered.contains("lib/demo.rs"),
9224            "diagnostic must quote the offending path: {rendered}",
9225        );
9226        assert!(
9227            rendered.contains(".lisp"),
9228            "diagnostic must name the expected extension: {rendered}",
9229        );
9230    }
9231
9232    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
9233    //
9234    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
9235    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
9236    // contract. The peer caixa-helm / caixa-flux renderers consume each
9237    // `:servicos` entry through `serde_yaml::from_str` as a typed
9238    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
9239    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
9240    // axis `Path::extension` can't express on its own.
9241
9242    #[test]
9243    fn validate_code_paths_rejects_no_extension_servicos_entry() {
9244        // Canonical "I dragged the wrong file from the workspace tree"
9245        // footgun on the Servico axis. Without the gate the peer
9246        // caixa-helm / caixa-flux renderers hand the extensionless path
9247        // to `serde_yaml::from_str` and fail with a parser-shaped
9248        // diagnostic far from the source caixa.lisp, with no field
9249        // naming the offending `:servicos` entry.
9250        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
9251            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9252            let err = c.validate_code_paths().unwrap_err();
9253            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9254                panic!(
9255                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9256                     got {err:?}"
9257                );
9258            };
9259            assert_eq!(slot, ":servicos");
9260            assert_eq!(path, PathBuf::from(relpath));
9261        }
9262    }
9263
9264    #[test]
9265    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
9266        // Wrong-extension sweep across common authoring footguns on the
9267        // Servico axis. Bare `.yaml` is the canonical "I forgot the
9268        // `.computeunit` segment" typo; the off-by-one-segment shapes
9269        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
9270        // bare `Path::extension` view but mismatch the typed compound
9271        // suffix the renderers' `serde_yaml::from_str` consumer demands.
9272        // Same sweep-posture as the peer
9273        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
9274        // (64772a9) on the sibling tatara-lisp-source axis.
9275        for relpath in [
9276            "servicos/demo.yaml",
9277            "servicos/demo.yml",
9278            "servicos/demo.json",
9279            "servicos/demo.toml",
9280            "servicos/demo.txt",
9281            "servicos/demo.computeunit.yaml.bak",
9282            "servicos/demo.computeunit.yam",
9283            "servicos/demo.computeunit",
9284            "servicos/demo-computeunit.yaml",
9285            "servicos/demo_computeunit.yaml",
9286        ] {
9287            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9288            let err = c.validate_code_paths().unwrap_err();
9289            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9290                panic!(
9291                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9292                     got {err:?}"
9293                );
9294            };
9295            assert_eq!(slot, ":servicos");
9296            assert_eq!(path, PathBuf::from(relpath));
9297        }
9298    }
9299
9300    #[test]
9301    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
9302        // Case-sensitivity sweep — pins the strict lowercase
9303        // `.computeunit.yaml` contract. A case-folded shape that the
9304        // layout's existence check would (case-insensitively, on
9305        // case-insensitive volumes) match the on-disk file still
9306        // mismatches the canonical form the codec emits, breaking the
9307        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
9308        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
9309        // (64772a9) sweep on the sibling tatara-lisp-source axis.
9310        for relpath in [
9311            "servicos/demo.ComputeUnit.yaml",
9312            "servicos/demo.COMPUTEUNIT.yaml",
9313            "servicos/demo.computeunit.YAML",
9314            "servicos/demo.computeunit.Yaml",
9315            "servicos/demo.COMPUTEUNIT.YAML",
9316        ] {
9317            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9318            let err = c.validate_code_paths().unwrap_err();
9319            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9320                panic!(
9321                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9322                     got {err:?}"
9323                );
9324            };
9325            assert_eq!(slot, ":servicos");
9326            assert_eq!(path, PathBuf::from(relpath));
9327        }
9328    }
9329
9330    #[test]
9331    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
9332        // Degenerate hidden-file shape: a file name exactly equal to the
9333        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
9334        // the structural "Servico declared with no identity" footgun.
9335        // The substrate identifies each ComputeUnit by the file-stem
9336        // segment that precedes `.computeunit.yaml` (the rendered
9337        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
9338        // the M3 `:contratos` membership lookup), so an empty stem
9339        // leaves the Servico unidentifiable. Pinned at the typed-axis
9340        // level so a future regression that drops the `name.len() >
9341        // SUFFIX.len()` bound at the predicate surfaces here, not
9342        // piecemeal as a `lareira-` chart-name collision at render time.
9343        for relpath in ["servicos/.computeunit.yaml"] {
9344            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9345            let err = c.validate_code_paths().unwrap_err();
9346            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9347                panic!(
9348                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9349                     got {err:?}"
9350                );
9351            };
9352            assert_eq!(slot, ":servicos");
9353            assert_eq!(path, PathBuf::from(relpath));
9354        }
9355    }
9356
9357    #[test]
9358    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
9359        // Positive-control sweep through every canonical authoring shape
9360        // every in-tree fixture and the `Caixa::template` scaffold use.
9361        // Mirrors the peer
9362        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
9363        // and the lifted predicate's own
9364        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
9365        // render.rs.
9366        for relpath in [
9367            "servicos/demo.computeunit.yaml",
9368            "servicos/hello-rio.computeunit.yaml",
9369            "servicos/my-service.computeunit.yaml",
9370            "servicos/a.computeunit.yaml",
9371            "./servicos/demo.computeunit.yaml",
9372            "servicos/./demo.computeunit.yaml",
9373            "servicos/sub/nested.computeunit.yaml",
9374            "servicos/v0.1.computeunit.yaml",
9375        ] {
9376            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9377            c.validate_code_paths()
9378                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
9379        }
9380    }
9381
9382    #[test]
9383    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
9384        // The file-type gate is per-slot — only `:servicos` carries the
9385        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
9386        // entry and an extensionless `:exe` entry are the canonical
9387        // shapes every in-tree fixture uses, and must continue to pass
9388        // validate. Peer of
9389        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
9390        // (64772a9) — together pin that the typed
9391        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
9392        // cross-axis leakage in either direction.
9393        let c = caixa_with_code_paths(
9394            vec!["lib/demo.lisp"],
9395            vec!["exe/demo", "exe/tool"],
9396            vec!["servicos/demo.computeunit.yaml"],
9397        );
9398        c.validate_code_paths().unwrap();
9399    }
9400
9401    #[test]
9402    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
9403        // Cross-arm precedence pin: a `:servicos` entry that is *both*
9404        // sandbox-escaping and wrong-extension surfaces the more
9405        // fundamental sandbox-shape diagnostic first (the
9406        // `.computeunit.yaml` remediation would be misleading when the
9407        // offending path can never resolve under the caixa root
9408        // anyway). Mirrors the peer
9409        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
9410        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
9411        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
9412        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
9413        // table establishes.
9414        //
9415        // Empty wins (the strictly-smaller-scope structural arm).
9416        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
9417        assert!(
9418            matches!(
9419                c.validate_code_paths().unwrap_err(),
9420                ManifestError::CodePathEmpty { slot: ":servicos" }
9421            ),
9422            "empty must win over non-computeunit-yaml-extension",
9423        );
9424        // Absolute wins (the path can't resolve under the caixa root).
9425        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
9426        let err = c.validate_code_paths().unwrap_err();
9427        let ManifestError::CodePathAbsolute { slot, .. } = err else {
9428            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
9429        };
9430        assert_eq!(slot, ":servicos");
9431        // ParentEscape wins (the path escapes the caixa root).
9432        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
9433        let err = c.validate_code_paths().unwrap_err();
9434        let ManifestError::CodePathParentEscape { slot, .. } = err else {
9435            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
9436        };
9437        assert_eq!(slot, ":servicos");
9438    }
9439
9440    #[test]
9441    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
9442        // Within-slot precedence pin: the per-entry file-type shape gate
9443        // fires before the cross-entry duplicate gate, so the narrower
9444        // structural defect dominates the uniqueness diagnostic. A
9445        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
9446        // `CodePathNonComputeUnitYamlExtension` on the first entry
9447        // rather than `CodePathDuplicate` on the pair — same posture
9448        // every per-entry shape-gate-precedes-duplicate cascade follows
9449        // on this surface, peer of the 64772a9 `:bibliotecas`
9450        // `("lib/x.txt" "lib/x.txt")` ordering.
9451        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
9452        let err = c.validate_code_paths().unwrap_err();
9453        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9454            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
9455        };
9456        assert_eq!(slot, ":servicos");
9457        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
9458    }
9459
9460    #[test]
9461    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
9462     {
9463        // Diagnostic-shape pin (peer with
9464        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
9465        // on the sibling tatara-lisp-source axis): the file-type-arm
9466        // Display surfaces both the offending `:slot` tag, the
9467        // offending path verbatim, and the expected
9468        // `.computeunit.yaml` compound suffix named in the remediation
9469        // text, so a `feira lint` run can render the diagnostic without
9470        // re-parsing.
9471        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
9472        let rendered = c.validate_code_paths().unwrap_err().to_string();
9473        assert!(
9474            rendered.contains(":servicos"),
9475            "diagnostic must name the offending slot: {rendered}",
9476        );
9477        assert!(
9478            rendered.contains("servicos/demo.yaml"),
9479            "diagnostic must quote the offending path: {rendered}",
9480        );
9481        assert!(
9482            rendered.contains(".computeunit.yaml"),
9483            "diagnostic must name the expected compound suffix: {rendered}",
9484        );
9485    }
9486
9487    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
9488
9489    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
9490        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9491        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
9492        c
9493    }
9494
9495    #[test]
9496    fn validate_etiquetas_accepts_empty_list() {
9497        // The empty-list identity: every caixa with no declared tags
9498        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
9499        // so the gate is non-disruptive against every existing manifest.
9500        let c = caixa_with_etiquetas(vec![]);
9501        c.validate_etiquetas().unwrap();
9502    }
9503
9504    #[test]
9505    fn validate_etiquetas_accepts_canonical_forms() {
9506        // Positive control sweep: a canonical-shaped non-empty distinct
9507        // tag list passes, mirroring the example checkout-aplicacao
9508        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
9509        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
9510        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
9511        c.validate_etiquetas().unwrap();
9512    }
9513
9514    #[test]
9515    fn validate_etiquetas_rejects_empty_entry() {
9516        // Canonical paste-from-blank-doc footgun. Without the gate the
9517        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
9518        // no-op tag indexing nothing in the future caixa-registry.
9519        let c = caixa_with_etiquetas(vec![""]);
9520        let err = c.validate_etiquetas().unwrap_err();
9521        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9522    }
9523
9524    #[test]
9525    fn validate_etiquetas_rejects_duplicate_entry() {
9526        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
9527        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
9528        // collect at chart render — a "second wins / one silently
9529        // disappears" shape divergent from every peer typed-graph set
9530        // gate. The duplicate-arm names the offending tag verbatim.
9531        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
9532        let err = c.validate_etiquetas().unwrap_err();
9533        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
9534            panic!("expected EtiquetaDuplicate, got {err:?}");
9535        };
9536        assert_eq!(etiqueta, "demo");
9537    }
9538
9539    #[test]
9540    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
9541        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
9542        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
9543        // structural "this entry has no value" defect dominates the
9544        // cross-entry uniqueness diagnostic. Mirrors the peer
9545        // empty-before-duplicate cascades on `:caracteristicas`
9546        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
9547        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
9548        // `MembroDuplicate`).
9549        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
9550        let err = c.validate_etiquetas().unwrap_err();
9551        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9552    }
9553
9554    #[test]
9555    fn validate_etiquetas_duplicate_reports_first_collision() {
9556        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
9557        // duplicate (the lexicographically-earliest offending position
9558        // — the second `"a"` at index 2 collides with the first `"a"`
9559        // at index 0), not the later `"b"` collision at index 3,
9560        // peer with every other first-collision diagnostic posture on
9561        // this surface (`validate_load_singularity_reports_first_collision`,
9562        // `validate_cleanup_singularity_reports_first_collision`).
9563        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
9564        let err = c.validate_etiquetas().unwrap_err();
9565        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
9566            panic!("expected EtiquetaDuplicate, got {err:?}");
9567        };
9568        assert_eq!(etiqueta, "a");
9569    }
9570
9571    #[test]
9572    fn validate_etiquetas_case_sensitive() {
9573        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
9574        // mirroring the peer `:membros :caixa` / `:children :caixa`
9575        // exact-string-match discipline. The shape gate this routine
9576        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
9577        // grammar) accepts mixed case — crates.io's keyword rule is
9578        // "case-insensitive" at the index layer but admits mixed case
9579        // at the entry layer (the canonical Helm chart `keywords:`
9580        // shape is lowercase by convention, but the grammar admits
9581        // uppercase). Case-sensitivity at the duplicate-set layer
9582        // remains structural — two distinct strings are two distinct
9583        // entries.
9584        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
9585        c.validate_etiquetas().unwrap();
9586    }
9587
9588    #[test]
9589    fn validate_etiquetas_diagnostic_carries_offending_tag() {
9590        // Diagnostic-shape pin (peer with
9591        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
9592        // the error's Display surfaces the offending tag verbatim, so a
9593        // `feira lint` run can render the diagnostic without re-parsing
9594        // and the author can grep their caixa.lisp for the offending
9595        // value.
9596        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
9597        let rendered = c.validate_etiquetas().unwrap_err().to_string();
9598        assert!(
9599            rendered.contains(":etiquetas"),
9600            "diagnostic must name the offending slot: {rendered}",
9601        );
9602        assert!(
9603            rendered.contains("demo"),
9604            "diagnostic must quote the offending tag: {rendered}",
9605        );
9606    }
9607
9608    #[test]
9609    fn validate_etiquetas_rejects_leading_whitespace_entry() {
9610        // Canonical paste-from-aligned-doc footgun. Without the shape
9611        // gate `" mesh"` silently passed validate and landed as a
9612        // YAML plain-style scalar with leading whitespace in the
9613        // rendered Chart.yaml `keywords:` array — every YAML 1.2
9614        // dumper trims leading whitespace from plain-style scalars,
9615        // so the authored space round-tripped inconsistently back
9616        // through `caixa.lisp`. Mirrors the peer
9617        // `validate_autores_rejects_leading_whitespace_entry`.
9618        let c = caixa_with_etiquetas(vec![" mesh"]);
9619        let err = c.validate_etiquetas().unwrap_err();
9620        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9621            panic!("expected EtiquetaInvalid, got {err:?}");
9622        };
9623        assert_eq!(etiqueta, " mesh");
9624        assert!(reason.contains("whitespace"), "got: {reason}");
9625    }
9626
9627    #[test]
9628    fn validate_etiquetas_rejects_embedded_newline_entry() {
9629        // Canonical paste-from-multiline-doc footgun — the author
9630        // pasted a multi-tag block into one `:etiquetas` entry
9631        // instead of splitting into one entry per tag. Without the
9632        // shape gate `"mesh\nhttp"` silently passed validate and
9633        // landed as a YAML-illegal multi-line scalar in the rendered
9634        // Chart.yaml `keywords:` array.
9635        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
9636        let err = c.validate_etiquetas().unwrap_err();
9637        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9638            panic!("expected EtiquetaInvalid, got {err:?}");
9639        };
9640        assert_eq!(etiqueta, "mesh\nhttp");
9641        assert!(reason.contains("newline"), "got: {reason}");
9642    }
9643
9644    #[test]
9645    fn validate_etiquetas_rejects_embedded_comma_entry() {
9646        // Canonical CSV-list-separator-confusion footgun: the author
9647        // confused the CSV-style separator convention with the
9648        // `:etiquetas` list grammar. Without the shape gate
9649        // `"mesh,http,grpc"` silently passed validate and landed as a
9650        // single malformed search tag in the rendered Chart.yaml
9651        // `keywords:` array — Artifact Hub's keyword index would
9652        // either silently drop the tag or index it as
9653        // `mesh,http,grpc` instead of three separate tags.
9654        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
9655        let err = c.validate_etiquetas().unwrap_err();
9656        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9657            panic!("expected EtiquetaInvalid, got {err:?}");
9658        };
9659        assert_eq!(etiqueta, "mesh,http,grpc");
9660        assert!(reason.contains('`'), "got: {reason}");
9661        assert!(reason.contains(','), "got: {reason}");
9662    }
9663
9664    #[test]
9665    fn validate_etiquetas_rejects_embedded_slash_entry() {
9666        // Canonical path-separator-confusion footgun: the author
9667        // confused namespace-path notation with the keyword grammar.
9668        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
9669        let err = c.validate_etiquetas().unwrap_err();
9670        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9671            panic!("expected EtiquetaInvalid, got {err:?}");
9672        };
9673        assert_eq!(etiqueta, "caixa/servico");
9674        assert!(reason.contains('/'), "got: {reason}");
9675    }
9676
9677    #[test]
9678    fn validate_etiquetas_rejects_leading_digit_entry() {
9679        // Canonical paste-from-numbered-list footgun: the author
9680        // copied `1. mesh` from a numbered doc and the `1` leaked
9681        // into the tag.
9682        let c = caixa_with_etiquetas(vec!["1mesh"]);
9683        let err = c.validate_etiquetas().unwrap_err();
9684        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9685            panic!("expected EtiquetaInvalid, got {err:?}");
9686        };
9687        assert_eq!(etiqueta, "1mesh");
9688        assert!(reason.contains("digit"), "got: {reason}");
9689    }
9690
9691    #[test]
9692    fn validate_etiquetas_rejects_leading_hyphen_entry() {
9693        // Canonical kebab-leak footgun.
9694        let c = caixa_with_etiquetas(vec!["-foo"]);
9695        let err = c.validate_etiquetas().unwrap_err();
9696        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9697            panic!("expected EtiquetaInvalid, got {err:?}");
9698        };
9699        assert_eq!(etiqueta, "-foo");
9700        assert!(reason.contains('-'), "got: {reason}");
9701    }
9702
9703    #[test]
9704    fn validate_etiquetas_rejects_non_ascii_entry() {
9705        // Canonical paste-from-Unicode-doc footgun. Every legitimate
9706        // search tag is strict ASCII; raw non-ASCII silently
9707        // round-trips inconsistently across NFC/NFD normalization on
9708        // APFS / case-folding filesystems and breaks the Artifact Hub
9709        // keyword search index lookup.
9710        let c = caixa_with_etiquetas(vec!["café"]);
9711        let err = c.validate_etiquetas().unwrap_err();
9712        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9713            panic!("expected EtiquetaInvalid, got {err:?}");
9714        };
9715        assert_eq!(etiqueta, "café");
9716        assert!(reason.contains("non-ASCII"), "got: {reason}");
9717    }
9718
9719    #[test]
9720    fn validate_etiquetas_rejects_period_entry() {
9721        // Canonical namespace-confusion / version-suffix footgun
9722        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
9723        // excludes `.` from the continuation set even though the
9724        // sibling `:caracteristicas` axis (Cargo's feature-name
9725        // grammar) admits it. Tighter than the sibling axis, peer
9726        // with Cargo's own crates.io keyword shape.
9727        let c = caixa_with_etiquetas(vec!["http.1"]);
9728        let err = c.validate_etiquetas().unwrap_err();
9729        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
9730            panic!("expected EtiquetaInvalid, got {err:?}");
9731        };
9732        assert_eq!(etiqueta, "http.1");
9733        assert!(reason.contains('.'), "got: {reason}");
9734    }
9735
9736    #[test]
9737    fn validate_etiquetas_empty_takes_precedence_over_shape() {
9738        // Per-entry empty-first cascade pin: an entry that is both
9739        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
9740        // narrower "this entry has no value" structural defect
9741        // dominates the broader shape-predicate diagnostic). The
9742        // empty arm fires before the shape predicate is consulted,
9743        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
9744        // cascade established on the sibling universal-axis Vec<String>
9745        // surface.
9746        let c = caixa_with_etiquetas(vec![""]);
9747        let err = c.validate_etiquetas().unwrap_err();
9748        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
9749    }
9750
9751    #[test]
9752    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
9753        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
9754        // entry that is malformed surfaces `EtiquetaInvalid` even when
9755        // a later entry would have collided on duplicate. The
9756        // per-entry shape arm fires inside the same loop iteration as
9757        // the empty arm, before the seen-set insert at end-of-iteration
9758        // — structural per-entry defects dominate the cross-entry
9759        // uniqueness diagnostic. Mirrors the peer
9760        // `validate_autores_shape_takes_precedence_over_duplicate`.
9761        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
9762        let err = c.validate_etiquetas().unwrap_err();
9763        assert!(
9764            matches!(err, ManifestError::EtiquetaInvalid { .. }),
9765            "got {err:?}",
9766        );
9767    }
9768
9769    #[test]
9770    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
9771        // Diagnostic-shape pin on the new shape arm (peer with
9772        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
9773        // the rendered Display surfaces both the offending slot name
9774        // and the offending value verbatim, so a `feira lint` run
9775        // points the author at the exact `:etiquetas` entry to fix.
9776        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
9777        let rendered = c.validate_etiquetas().unwrap_err().to_string();
9778        assert!(
9779            rendered.contains(":etiquetas"),
9780            "diagnostic must name the offending slot: {rendered}",
9781        );
9782        assert!(
9783            rendered.contains("mesh\\nhttp"),
9784            "diagnostic must quote the offending value (debug-escaped): {rendered}",
9785        );
9786    }
9787
9788    #[test]
9789    fn validate_etiquetas_rejects_at_21_byte_boundary() {
9790        // The 20-byte cap pin — boundary-exceeding case rejected,
9791        // boundary-accepting case passes. Mirrors the peer
9792        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
9793        // side pin, surfaced at the per-axis caller so the cap
9794        // propagates through validate end-to-end. Constructed as a
9795        // single all-`a` token so only the cap arm fires.
9796        let max_ok = "a".repeat(20);
9797        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
9798        c.validate_etiquetas().unwrap();
9799        let too_long = "a".repeat(21);
9800        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
9801        let err = c.validate_etiquetas().unwrap_err();
9802        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
9803            panic!("expected EtiquetaInvalid, got {err:?}");
9804        };
9805        assert!(reason.contains("20"), "got: {reason}");
9806        assert!(reason.contains("21"), "got: {reason}");
9807    }
9808
9809    #[test]
9810    fn validate_etiquetas_accepts_canonical_shaped_forms() {
9811        // Positive control sweep: every canonical-shaped tag from the
9812        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
9813        // example fixtures plus the substrate-fixed tags caixa-helm
9814        // unions in at chart render. Drift between this list and the
9815        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
9816        // sweep surfaces here — one source of truth for the rule.
9817        let c = caixa_with_etiquetas(vec![
9818            "example",
9819            "aplicacao",
9820            "mesh",
9821            "ecommerce",
9822            "demo",
9823            "infrastructure",
9824            "aws",
9825            "akeyless",
9826            "pangea-native",
9827            "hello-world",
9828            "wasm",
9829            "rust",
9830            "tatara-lisp",
9831            "caixa-servico",
9832            "lareira",
9833        ]);
9834        c.validate_etiquetas().unwrap();
9835    }
9836
9837    // ── validate_autores — universal-axis maintainer shape ────────────
9838
9839    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
9840        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9841        c.autores = autores.into_iter().map(String::from).collect();
9842        c
9843    }
9844
9845    #[test]
9846    fn validate_autores_accepts_empty_list() {
9847        // The empty-list identity: `Caixa::template` emits `:autores ()`,
9848        // so the gate is non-disruptive against every existing manifest.
9849        let c = caixa_with_autores(vec![]);
9850        c.validate_autores().unwrap();
9851    }
9852
9853    #[test]
9854    fn validate_autores_accepts_canonical_forms() {
9855        // Positive control sweep: every canonical-shaped non-empty
9856        // distinct maintainer list passes — the hello-rio / checkout-
9857        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
9858        // multi-author shape downstream packaging surfaces emit.
9859        let c = caixa_with_autores(vec!["pleme-io"]);
9860        c.validate_autores().unwrap();
9861        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
9862        c.validate_autores().unwrap();
9863    }
9864
9865    #[test]
9866    fn validate_autores_rejects_empty_entry() {
9867        // Canonical paste-from-blank-doc footgun. Without the gate the
9868        // empty entry rendered as `maintainers: [{name: "", email: null}]`
9869        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
9870        // to.
9871        let c = caixa_with_autores(vec![""]);
9872        let err = c.validate_autores().unwrap_err();
9873        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9874    }
9875
9876    #[test]
9877    fn validate_autores_rejects_duplicate_entry() {
9878        // Canonical copy-paste-the-wrong-author footgun. Unlike the
9879        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
9880        // dedups the rendered `keywords:` array), the `maintainers:`
9881        // rendering has *no* dedup — duplicates stack verbatim. The
9882        // duplicate-arm names the offending author verbatim.
9883        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9884        let err = c.validate_autores().unwrap_err();
9885        let ManifestError::AutorDuplicate { autor } = err else {
9886            panic!("expected AutorDuplicate, got {err:?}");
9887        };
9888        assert_eq!(autor, "pleme-io");
9889    }
9890
9891    #[test]
9892    fn validate_autores_empty_takes_precedence_over_duplicate() {
9893        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
9894        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
9895        // "this entry has no value" defect dominates the cross-entry
9896        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
9897        // cascades on `:etiquetas` (`EtiquetaEmpty` before
9898        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
9899        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
9900        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
9901        // `MembroDuplicate`).
9902        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
9903        let err = c.validate_autores().unwrap_err();
9904        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
9905    }
9906
9907    #[test]
9908    fn validate_autores_duplicate_reports_first_collision() {
9909        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
9910        // duplicate (the lexicographically-earliest offending position
9911        // — the second `"a"` at index 2 collides with the first `"a"`
9912        // at index 0), not the later `"b"` collision at index 3,
9913        // peer with every other first-collision diagnostic posture on
9914        // this surface.
9915        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
9916        let err = c.validate_autores().unwrap_err();
9917        let ManifestError::AutorDuplicate { autor } = err else {
9918            panic!("expected AutorDuplicate, got {err:?}");
9919        };
9920        assert_eq!(autor, "a");
9921    }
9922
9923    #[test]
9924    fn validate_autores_case_sensitive() {
9925        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
9926        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
9927        // / `:children :caixa` exact-string-match discipline.
9928        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
9929        c.validate_autores().unwrap();
9930    }
9931
9932    #[test]
9933    fn validate_autores_diagnostic_carries_offending_author() {
9934        // Diagnostic-shape pin (peer with
9935        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
9936        // error's Display surfaces the offending author verbatim, so a
9937        // `feira lint` run can render the diagnostic without re-parsing
9938        // and the author can grep their caixa.lisp for the offending
9939        // value.
9940        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
9941        let rendered = c.validate_autores().unwrap_err().to_string();
9942        assert!(
9943            rendered.contains(":autores"),
9944            "diagnostic must name the offending slot: {rendered}",
9945        );
9946        assert!(
9947            rendered.contains("pleme-io"),
9948            "diagnostic must quote the offending author: {rendered}",
9949        );
9950    }
9951
9952    #[test]
9953    fn validate_autores_rejects_leading_whitespace_entry() {
9954        // Canonical paste-from-aligned-doc footgun. Without the shape
9955        // gate `" pleme-io"` silently passed validate and landed as a
9956        // YAML plain-style scalar with leading whitespace in the
9957        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
9958        // dumper trims leading whitespace from plain-style scalars, so
9959        // the authored space round-tripped inconsistently back through
9960        // `caixa.lisp`. Mirrors the peer
9961        // `validate_descricao_rejects_leading_whitespace`.
9962        let c = caixa_with_autores(vec![" pleme-io"]);
9963        let err = c.validate_autores().unwrap_err();
9964        let ManifestError::AutorInvalid { autor, reason } = err else {
9965            panic!("expected AutorInvalid, got {err:?}");
9966        };
9967        assert_eq!(autor, " pleme-io");
9968        assert!(reason.contains("whitespace"), "got: {reason}");
9969    }
9970
9971    #[test]
9972    fn validate_autores_rejects_trailing_whitespace_entry() {
9973        // Canonical paste-from-doc footgun.
9974        let c = caixa_with_autores(vec!["pleme-io "]);
9975        let err = c.validate_autores().unwrap_err();
9976        let ManifestError::AutorInvalid { autor, reason } = err else {
9977            panic!("expected AutorInvalid, got {err:?}");
9978        };
9979        assert_eq!(autor, "pleme-io ");
9980        assert!(reason.contains("whitespace"), "got: {reason}");
9981    }
9982
9983    #[test]
9984    fn validate_autores_rejects_embedded_newline_entry() {
9985        // Canonical paste-from-multiline-doc footgun — the author
9986        // pasted a multi-line block of author records into one
9987        // `:autores` entry instead of splitting into one entry per
9988        // author. Without the shape gate `"alice\nbob"` silently
9989        // passed validate and landed as a YAML-illegal multi-line
9990        // scalar in the rendered Chart.yaml `maintainers:` array.
9991        let c = caixa_with_autores(vec!["alice\nbob"]);
9992        let err = c.validate_autores().unwrap_err();
9993        let ManifestError::AutorInvalid { autor, reason } = err else {
9994            panic!("expected AutorInvalid, got {err:?}");
9995        };
9996        assert_eq!(autor, "alice\nbob");
9997        assert!(reason.contains("newline"), "got: {reason}");
9998    }
9999
10000    #[test]
10001    fn validate_autores_rejects_embedded_carriage_return_entry() {
10002        // Canonical paste-from-Windows-CRLF-doc footgun.
10003        let c = caixa_with_autores(vec!["alice\rbob"]);
10004        let err = c.validate_autores().unwrap_err();
10005        let ManifestError::AutorInvalid { autor, reason } = err else {
10006            panic!("expected AutorInvalid, got {err:?}");
10007        };
10008        assert_eq!(autor, "alice\rbob");
10009        assert!(reason.contains("carriage return"), "got: {reason}");
10010    }
10011
10012    #[test]
10013    fn validate_autores_rejects_embedded_tab_entry() {
10014        // Canonical tab-from-aligned-doc footgun.
10015        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
10016        let err = c.validate_autores().unwrap_err();
10017        let ManifestError::AutorInvalid { autor, reason } = err else {
10018            panic!("expected AutorInvalid, got {err:?}");
10019        };
10020        assert_eq!(autor, "Pleme\tContributors");
10021        assert!(reason.contains("tab"), "got: {reason}");
10022    }
10023
10024    #[test]
10025    fn validate_autores_rejects_embedded_control_bytes_entry() {
10026        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
10027        // surface the same control-byte arm.
10028        for entry in [
10029            "alice\x00bob",
10030            "alice\x07bob",
10031            "alice\x1bbob",
10032            "alice\x7fbob",
10033        ] {
10034            let c = caixa_with_autores(vec![entry]);
10035            let err = c.validate_autores().unwrap_err();
10036            let ManifestError::AutorInvalid { autor, reason } = err else {
10037                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
10038            };
10039            assert_eq!(autor, entry);
10040            assert!(
10041                reason.contains("control character"),
10042                "{entry:?} reason: {reason}",
10043            );
10044        }
10045    }
10046
10047    #[test]
10048    fn validate_autores_accepts_unicode_entry() {
10049        // Unicode positive control: realistic maintainer names carry
10050        // Unicode (`François`, `日本語`, `naïve`). The predicate must
10051        // round-trip Unicode losslessly, peer with the
10052        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
10053        // sweep.
10054        let c = caixa_with_autores(vec![
10055            "François Dupont",
10056            "日本語の名前",
10057            "naïve <naive@example.com>",
10058        ]);
10059        c.validate_autores().unwrap();
10060    }
10061
10062    #[test]
10063    fn validate_autores_empty_takes_precedence_over_shape() {
10064        // Per-entry empty-first cascade pin: an entry that is both
10065        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
10066        // "this entry has no value" structural defect dominates the
10067        // broader shape-predicate diagnostic). The empty arm fires
10068        // before the shape predicate is consulted, mirroring the peer
10069        // `validate_repositorio_empty_takes_precedence_over_shape`
10070        // cascade on the universal `Option<String>` siblings — and now
10071        // established on the Vec<String> per-entry surface.
10072        let c = caixa_with_autores(vec![""]);
10073        let err = c.validate_autores().unwrap_err();
10074        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
10075    }
10076
10077    #[test]
10078    fn validate_autores_shape_takes_precedence_over_duplicate() {
10079        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
10080        // entry that is malformed surfaces `AutorInvalid` even when a
10081        // later entry would have collided on duplicate. The per-entry
10082        // shape arm fires inside the same loop iteration as the empty
10083        // arm, before the seen-set insert at end-of-iteration —
10084        // structural per-entry defects dominate the cross-entry
10085        // uniqueness diagnostic.
10086        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
10087        let err = c.validate_autores().unwrap_err();
10088        assert!(
10089            matches!(err, ManifestError::AutorInvalid { .. }),
10090            "got {err:?}",
10091        );
10092    }
10093
10094    #[test]
10095    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
10096        // Diagnostic-shape pin on the new shape arm (peer with
10097        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
10098        // the rendered Display surfaces both the offending slot name
10099        // and the offending value verbatim, so a `feira lint` run
10100        // points the author at the exact `:autores` entry to fix.
10101        let c = caixa_with_autores(vec!["alice\nbob"]);
10102        let rendered = c.validate_autores().unwrap_err().to_string();
10103        assert!(
10104            rendered.contains(":autores"),
10105            "diagnostic must name the offending slot: {rendered}",
10106        );
10107        assert!(
10108            rendered.contains("alice\\nbob"),
10109            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10110        );
10111    }
10112
10113    #[test]
10114    fn validate_autores_rejects_at_129_byte_boundary() {
10115        // The 128-byte cap pin — boundary-exceeding case rejected,
10116        // boundary-accepting case passes. Mirrors the peer
10117        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
10118        // substrate-side pin, surfaced at the per-axis caller so the
10119        // cap propagates through validate end-to-end. Constructed as
10120        // a single all-`a` token so only the cap arm fires.
10121        let max_ok = "a".repeat(128);
10122        let c = caixa_with_autores(vec![max_ok.as_str()]);
10123        c.validate_autores().unwrap();
10124        let too_long = "a".repeat(129);
10125        let c = caixa_with_autores(vec![too_long.as_str()]);
10126        let err = c.validate_autores().unwrap_err();
10127        let ManifestError::AutorInvalid { reason, .. } = err else {
10128            panic!("expected AutorInvalid, got {err:?}");
10129        };
10130        assert!(reason.contains("128"), "got: {reason}");
10131        assert!(reason.contains("129"), "got: {reason}");
10132    }
10133
10134    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
10135
10136    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
10137        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10138        c.repositorio = repositorio.map(String::from);
10139        c
10140    }
10141
10142    #[test]
10143    fn validate_repositorio_accepts_none() {
10144        // The omit-the-slot identity: `:repositorio` is optional. The
10145        // gate is a no-op when the author didn't declare a value —
10146        // every caixa without a `:repositorio` line trivially passes,
10147        // and the substrate-side renderers fall back to their
10148        // documented placeholder (`caixa-helm`'s `home: None`,
10149        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
10150        // URL). Mirrors the peer `validate_restart_window_accepts_none`
10151        // posture on the other `Option<String>` Caixa slot.
10152        let c = caixa_with_repositorio(None);
10153        c.validate_repositorio().unwrap();
10154    }
10155
10156    #[test]
10157    fn validate_repositorio_accepts_canonical_forms() {
10158        // Positive control sweep across every documented `:repositorio`
10159        // authoring shape — the same union the shared
10160        // `crate::render::is_git_repo_url` predicate accepts and the
10161        // peer `:deps :fonte :repo` axis already routes through.
10162        // Covers the `github:` shorthand (the canonical pleme-io
10163        // convention used in the `:repositorio` field of every
10164        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
10165        // `examples/`), the `https://…` URL the README quickstart uses,
10166        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
10167        // `file://` URL schemes the shared predicate documents.
10168        for repo in [
10169            "github:pleme-io/hello-rio",
10170            "github:pleme-io/checkout",
10171            "https://github.com/pleme-io/hello-rio",
10172            "ssh://git@github.com/pleme-io/hello-rio.git",
10173            "git://github.com/pleme-io/hello-rio.git",
10174            "git@github.com:pleme-io/hello-rio.git",
10175            "file:///srv/pleme/hello-rio",
10176        ] {
10177            let c = caixa_with_repositorio(Some(repo));
10178            c.validate_repositorio()
10179                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
10180        }
10181    }
10182
10183    #[test]
10184    fn validate_repositorio_rejects_empty_some() {
10185        // Canonical paste-from-blank-doc footgun. The narrower
10186        // [`ManifestError::RepositorioEmpty`] arm fires before the
10187        // shape predicate is consulted, mirroring the empty-first
10188        // cascade every peer per-axis identity gate uses
10189        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
10190        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
10191        // the empty `Some("")` silently passed the renderer's
10192        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
10193        // on `None`) and landed as `home: ""` in `Chart.yaml` /
10194        // `url: ""` in the FluxCD `GitRepository`.
10195        let c = caixa_with_repositorio(Some(""));
10196        let err = c.validate_repositorio().unwrap_err();
10197        assert!(
10198            matches!(err, ManifestError::RepositorioEmpty),
10199            "got {err:?}",
10200        );
10201    }
10202
10203    #[test]
10204    fn validate_repositorio_rejects_whitespace() {
10205        // Paste-from-doc whitespace footgun. The shared
10206        // `is_git_repo_url` predicate refuses any whitespace byte; a
10207        // trailing space in a `:repositorio` value silently broke
10208        // `git clone '<value> '` at clone time. The diagnostic names
10209        // the offending value verbatim.
10210        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
10211        let err = c.validate_repositorio().unwrap_err();
10212        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
10213            panic!("expected RepositorioInvalid, got {err:?}");
10214        };
10215        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
10216    }
10217
10218    #[test]
10219    fn validate_repositorio_rejects_control_char() {
10220        // Paste-from-multiline-doc CRLF footgun — control characters
10221        // at the URL boundary are a class of subprocess-arg injection
10222        // and break git's URL parser at every porcelain entry point.
10223        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
10224        let err = c.validate_repositorio().unwrap_err();
10225        assert!(
10226            matches!(err, ManifestError::RepositorioInvalid { .. }),
10227            "got {err:?}",
10228        );
10229    }
10230
10231    #[test]
10232    fn validate_repositorio_rejects_leading_dash() {
10233        // Canonical CLI-argument-injection footgun: `git clone <repo>`
10234        // interprets a leading `-` as a CLI flag, so a
10235        // `-upload-pack=…` value escapes the subprocess argument
10236        // boundary. The shared predicate refuses every leading-`-`
10237        // shape at validate time.
10238        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
10239        let err = c.validate_repositorio().unwrap_err();
10240        assert!(
10241            matches!(err, ManifestError::RepositorioInvalid { .. }),
10242            "got {err:?}",
10243        );
10244    }
10245
10246    #[test]
10247    fn validate_repositorio_rejects_missing_colon_separator() {
10248        // The bare `org/repo` ambiguity footgun — `git clone` reads
10249        // a no-`:` form as a relative filesystem path rather than the
10250        // GitHub-shorthand expansion the author probably intended.
10251        // The shared predicate refuses every shape without a `:`
10252        // separator.
10253        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
10254        let err = c.validate_repositorio().unwrap_err();
10255        assert!(
10256            matches!(err, ManifestError::RepositorioInvalid { .. }),
10257            "got {err:?}",
10258        );
10259    }
10260
10261    #[test]
10262    fn validate_repositorio_rejects_fragment_anchor() {
10263        // Paste-from-browser-address-bar footgun on the
10264        // `:repositorio` axis — an author copies a GitHub permalink
10265        // to a README section / line-permalink and forgets to trim
10266        // the `#fragment` tail. The shared `is_git_repo_url`
10267        // predicate refuses the byte at the URL-grammar layer
10268        // (libcurl strips the fragment before opening the
10269        // transport, so the byte rides verbatim into the rendered
10270        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
10271        // fields but is silently dropped on the wire — two
10272        // manifest variants whose values differ only in their
10273        // fragment anchor lock to two distinct rendered artifacts
10274        // for the byte-identical clone, defeating the THEORY.md
10275        // §V.2 render-determinism contract on the `:repositorio`
10276        // axis the peer `:fonte :repo` axis already closes).
10277        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
10278        let err = c.validate_repositorio().unwrap_err();
10279        let ManifestError::RepositorioInvalid {
10280            repositorio,
10281            reason,
10282        } = err
10283        else {
10284            panic!("expected RepositorioInvalid, got {err:?}");
10285        };
10286        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
10287        assert!(
10288            reason.contains("must not contain `#`"),
10289            "reason must surface the fragment-`#` arm, got {reason:?}"
10290        );
10291    }
10292
10293    #[test]
10294    fn validate_repositorio_rejects_query_string() {
10295        // Paste-from-browser-address-bar footgun on the
10296        // `:repositorio` axis (peer with the a68f818 fragment-`#`
10297        // arm on the same axis). An author copies a GitHub tab
10298        // deep-link out of the address bar and forgets to trim
10299        // the `?tab=…` query tail. The shared `is_git_repo_url`
10300        // predicate refuses the byte at the URL-grammar layer
10301        // (GitHub / GitLab / Bitbucket silently ignore the
10302        // `?query` tail and serve the same repo regardless, so
10303        // the byte rides verbatim into the rendered `Chart.yaml`
10304        // `home:` and FluxCD `GitRepository` `url:` fields but
10305        // is silently masked at the wire — two manifest variants
10306        // whose values differ only in their query tail lock to
10307        // two distinct rendered artifacts for the byte-identical
10308        // clone, defeating the THEORY.md §V.2 render-determinism
10309        // contract on the `:repositorio` axis the peer `:fonte
10310        // :repo` axis already closes).
10311        let c = caixa_with_repositorio(Some(
10312            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
10313        ));
10314        let err = c.validate_repositorio().unwrap_err();
10315        let ManifestError::RepositorioInvalid {
10316            repositorio,
10317            reason,
10318        } = err
10319        else {
10320            panic!("expected RepositorioInvalid, got {err:?}");
10321        };
10322        assert_eq!(
10323            repositorio,
10324            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
10325        );
10326        assert!(
10327            reason.contains("must not contain `?`"),
10328            "reason must surface the query-`?` arm, got {reason:?}"
10329        );
10330    }
10331
10332    #[test]
10333    fn validate_repositorio_rejects_embedded_backslash() {
10334        // Windows-file-path-confusion footgun on the `:repositorio`
10335        // axis (peer with the prior fragment-`#` / query-`?` arms on
10336        // the same axis, and peer with the new dep-level `:fonte :repo`
10337        // backslash arm on the URL-grammar trajectory). An author
10338        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
10339        // hello-rio` into the `:repositorio` slot, expecting the
10340        // `lareira-<nome>` chart's `home:` field and the FluxCD
10341        // `GitRepository` `url:` field to render the canonical local
10342        // file-URI. The shared `is_git_repo_url` predicate refuses
10343        // the byte at the URL-grammar layer (libcurl silently
10344        // translates `\` → `/` on some platforms and refuses it on
10345        // others, so the byte rides verbatim into the rendered
10346        // artifacts but is silently rewritten or rejected at the wire
10347        // — two manifest variants whose values differ only in
10348        // backslash-vs-forward-slash lock to two distinct rendered
10349        // artifacts for the byte-identical clone, defeating the
10350        // THEORY.md §V.2 render-determinism contract on the
10351        // `:repositorio` axis the peer `:fonte :repo` axis already
10352        // closes).
10353        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
10354        let err = c.validate_repositorio().unwrap_err();
10355        let ManifestError::RepositorioInvalid {
10356            repositorio,
10357            reason,
10358        } = err
10359        else {
10360            panic!("expected RepositorioInvalid, got {err:?}");
10361        };
10362        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
10363        assert!(
10364            reason.contains("must not contain `\\`"),
10365            "reason must surface the backslash-`\\` arm, got {reason:?}"
10366        );
10367    }
10368
10369    #[test]
10370    fn validate_repositorio_rejects_uri_template_placeholder() {
10371        // URI Template (RFC 6570) placeholder footgun on the
10372        // `:repositorio` axis (peer with the prior fragment-`#` /
10373        // query-`?` / backslash-`\` arms on the same axis, and peer
10374        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
10375        // URL-grammar trajectory). An author pastes a quick-start
10376        // README snippet / OpenAPI `servers:` URL / Helm chart
10377        // `home:` template carrying unresolved `{org}` / `{repo}`
10378        // placeholders into the `:repositorio` slot, expecting the
10379        // substrate to resolve the placeholder downstream. The
10380        // shared `is_git_repo_url` predicate refuses the byte at the
10381        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
10382        // `%7B` / `%7D` on the wire, so the byte round-trips
10383        // inconsistently between the rendered `Chart.yaml home:` /
10384        // FluxCD `GitRepository url:` and the resolver's `git clone`
10385        // invocation, defeating the THEORY.md §V.2 render-
10386        // determinism contract on the `:repositorio` axis the peer
10387        // `:fonte :repo` axis already closes; every git porcelain
10388        // entry-point additionally fetches a nonexistent literal-
10389        // `{placeholder}`-named path far from the source caixa.lisp).
10390        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
10391        let err = c.validate_repositorio().unwrap_err();
10392        let ManifestError::RepositorioInvalid {
10393            repositorio,
10394            reason,
10395        } = err
10396        else {
10397            panic!("expected RepositorioInvalid, got {err:?}");
10398        };
10399        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
10400        assert!(
10401            reason.contains("must not contain `{`"),
10402            "reason must surface the open-brace `{{` arm, got {reason:?}"
10403        );
10404        assert!(
10405            reason.contains("URI Template") || reason.contains("RFC 6570"),
10406            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
10407        );
10408    }
10409
10410    #[test]
10411    fn validate_repositorio_empty_takes_precedence_over_shape() {
10412        // Empty-first cascade pin: the empty `Some("")` surfaces the
10413        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
10414        // `RepositorioInvalid`, mirroring the peer
10415        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
10416        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
10417        // `is_git_repo_url` predicate also rejects the empty input
10418        // (defensively, with its own `"must not be empty"` reason),
10419        // but the manifest-layer empty arm runs first to surface the
10420        // narrower diagnostic verbatim.
10421        let c = caixa_with_repositorio(Some(""));
10422        let err = c.validate_repositorio().unwrap_err();
10423        assert!(
10424            matches!(err, ManifestError::RepositorioEmpty),
10425            "got {err:?}",
10426        );
10427    }
10428
10429    #[test]
10430    fn validate_repositorio_diagnostic_carries_offending_value() {
10431        // Diagnostic-shape pin (peer with
10432        // `validate_autores_diagnostic_carries_offending_author`): the
10433        // error's Display surfaces the offending value + slot name
10434        // verbatim, so a `feira lint` run can render the diagnostic
10435        // without re-parsing and the author can grep their caixa.lisp
10436        // for the offending `:repositorio` value.
10437        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
10438        let rendered = c.validate_repositorio().unwrap_err().to_string();
10439        assert!(
10440            rendered.contains(":repositorio"),
10441            "diagnostic must name the offending slot: {rendered}",
10442        );
10443        assert!(
10444            rendered.contains("pleme-io/hello-rio"),
10445            "diagnostic must quote the offending value: {rendered}",
10446        );
10447    }
10448
10449    // ── validate_descricao — universal-axis Chart.yaml description shape ──
10450
10451    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
10452        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10453        c.descricao = descricao.map(String::from);
10454        c
10455    }
10456
10457    #[test]
10458    fn validate_descricao_accepts_none() {
10459        // The omit-the-slot identity: `:descricao` is optional. The
10460        // gate is a no-op when the author didn't declare a value —
10461        // every caixa without a `:descricao` line trivially passes,
10462        // and the substrate-side renderers fall back to their
10463        // documented `caixa.nome`-derived placeholder. Mirrors the
10464        // peer `validate_repositorio_accepts_none` posture on the
10465        // sibling `Option<String>` Caixa slot.
10466        let c = caixa_with_descricao(None);
10467        c.validate_descricao().unwrap();
10468    }
10469
10470    #[test]
10471    fn validate_descricao_accepts_canonical_summary() {
10472        // Positive control: the canonical pleme-io descricao shape —
10473        // a short free-form prose summary — passes the gate. Covers
10474        // the fixture shapes the `caixa-helm` / `caixa-flux` /
10475        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
10476        // wasip2 caixa Servico."`, `"Checkout flow."`).
10477        for desc in [
10478            "Canonical Rust→wasm32-wasip2 caixa Servico.",
10479            "Checkout flow.",
10480            "AWS provider caixa for tatara-lisp",
10481            "FIXME — describe this caixa",
10482            "x",
10483        ] {
10484            let c = caixa_with_descricao(Some(desc));
10485            c.validate_descricao()
10486                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
10487        }
10488    }
10489
10490    #[test]
10491    fn validate_descricao_rejects_empty_some() {
10492        // Canonical paste-from-blank-doc footgun. Without this gate
10493        // the empty `Some("")` silently passed the renderer's
10494        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
10495        // on `None`) and landed as `description: ""` in `Chart.yaml`
10496        // and a blank `README.md` header. Mirrors the peer
10497        // [`ManifestError::RepositorioEmpty`] empty-arm on the
10498        // sibling `Option<String>` Caixa slot.
10499        let c = caixa_with_descricao(Some(""));
10500        let err = c.validate_descricao().unwrap_err();
10501        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
10502    }
10503
10504    #[test]
10505    fn validate_descricao_rejects_leading_whitespace() {
10506        // Paste-from-aligned-doc footgun: a leading ASCII space the
10507        // bare empty-arm gate accepted, the shape predicate now
10508        // refuses. The diagnostic carries the offending value
10509        // verbatim (with the leading space preserved) so the author
10510        // can grep their caixa.lisp for the exact `:descricao` line
10511        // and fix the round-trip-inconsistent leading whitespace.
10512        // Mirrors the peer
10513        // `validate_licenca_rejects_leading_whitespace` arm on the
10514        // sibling `:licenca` axis.
10515        let c = caixa_with_descricao(Some(" Checkout flow."));
10516        let err = c.validate_descricao().unwrap_err();
10517        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
10518            panic!("expected DescricaoInvalid, got {err:?}");
10519        };
10520        assert_eq!(descricao, " Checkout flow.");
10521        assert!(reason.contains("whitespace"), "got: {reason:?}");
10522    }
10523
10524    #[test]
10525    fn validate_descricao_rejects_trailing_whitespace() {
10526        // Paste-from-doc footgun: a trailing ASCII space the bare
10527        // empty-arm gate accepted, the shape predicate now refuses.
10528        let c = caixa_with_descricao(Some("Checkout flow. "));
10529        let err = c.validate_descricao().unwrap_err();
10530        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
10531            panic!("expected DescricaoInvalid, got {err:?}");
10532        };
10533        assert_eq!(descricao, "Checkout flow. ");
10534        assert!(reason.contains("whitespace"), "got: {reason:?}");
10535    }
10536
10537    #[test]
10538    fn validate_descricao_rejects_embedded_newline() {
10539        // Paste-from-multiline-doc footgun: an embedded LF the bare
10540        // empty-arm gate accepted, the shape predicate now refuses.
10541        // Without this gate the embedded newline silently landed in
10542        // the rendered Chart.yaml as a multi-line YAML block scalar,
10543        // and every chart-aware UI (`helm list`, `helm search`,
10544        // Artifact Hub) renders the description in a single-line
10545        // column so the embedded newline is silently dropped at
10546        // every downstream consumer.
10547        let c = caixa_with_descricao(Some("Checkout\nflow."));
10548        let err = c.validate_descricao().unwrap_err();
10549        assert!(
10550            matches!(err, ManifestError::DescricaoInvalid { .. }),
10551            "got {err:?}",
10552        );
10553        assert!(err.to_string().contains("newline"), "got {err}");
10554    }
10555
10556    #[test]
10557    fn validate_descricao_rejects_embedded_carriage_return() {
10558        // Paste-from-Windows-CRLF-doc footgun.
10559        let c = caixa_with_descricao(Some("Checkout\rflow."));
10560        let err = c.validate_descricao().unwrap_err();
10561        assert!(
10562            matches!(err, ManifestError::DescricaoInvalid { .. }),
10563            "got {err:?}",
10564        );
10565        assert!(err.to_string().contains("carriage return"), "got {err}");
10566    }
10567
10568    #[test]
10569    fn validate_descricao_rejects_embedded_tab() {
10570        // Tab-from-aligned-doc footgun.
10571        let c = caixa_with_descricao(Some("Checkout\tflow."));
10572        let err = c.validate_descricao().unwrap_err();
10573        assert!(
10574            matches!(err, ManifestError::DescricaoInvalid { .. }),
10575            "got {err:?}",
10576        );
10577        assert!(err.to_string().contains("tab"), "got {err}");
10578    }
10579
10580    #[test]
10581    fn validate_descricao_rejects_embedded_control_bytes() {
10582        // Paste-from-binary-blob footgun: every other control byte
10583        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
10584        // the peer SPDX-expression control-byte arm.
10585        for s in [
10586            "Checkout\x00flow.",
10587            "Checkout\x07flow.",
10588            "Checkout\x1bflow.",
10589            "Checkout\x7fflow.",
10590        ] {
10591            let c = caixa_with_descricao(Some(s));
10592            let err = c.validate_descricao().unwrap_err();
10593            assert!(
10594                matches!(err, ManifestError::DescricaoInvalid { .. }),
10595                "{s:?} got {err:?}",
10596            );
10597            assert!(
10598                err.to_string().contains("control character"),
10599                "{s:?} got {err}",
10600            );
10601        }
10602    }
10603
10604    #[test]
10605    fn validate_descricao_accepts_unicode_prose() {
10606        // Positive control: Unicode prose is accepted — the
10607        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
10608        // and `Caixa::template`'s `"FIXME — describe this caixa"`
10609        // scaffold every `feira init` emits must continue to pass.
10610        for s in [
10611            "Canonical Rust→wasm32-wasip2 caixa Servico.",
10612            "FIXME — describe this caixa",
10613            "Caixa pour le projet tâche",
10614            "日本語の説明",
10615        ] {
10616            let c = caixa_with_descricao(Some(s));
10617            c.validate_descricao()
10618                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
10619        }
10620    }
10621
10622    #[test]
10623    fn validate_descricao_empty_takes_precedence_over_shape() {
10624        // Cascade pin: a `Some("")` surfaces the narrower
10625        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
10626        // shape-predicate arm. Mirrors the peer
10627        // `validate_licenca_empty_takes_precedence_over_shape` pin
10628        // on the sibling `:licenca` axis.
10629        let c = caixa_with_descricao(Some(""));
10630        let err = c.validate_descricao().unwrap_err();
10631        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
10632    }
10633
10634    #[test]
10635    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
10636        // Diagnostic-shape pin: the error's Display surfaces both
10637        // the `:descricao` slot name and the offending value
10638        // verbatim, so a `feira lint` run can render the diagnostic
10639        // without re-parsing and the author can grep their caixa.lisp
10640        // for the offending `:descricao` line. Mirrors the peer
10641        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
10642        // pin (ee2e888) on the sibling `:licenca` axis.
10643        // The `{descricao:?}` Debug format escapes embedded control
10644        // bytes; the quoted offending value surfaces as
10645        // `"Checkout\nflow."` (literal backslash-n) in the rendered
10646        // diagnostic. The author can grep their caixa.lisp for the
10647        // literal `Checkout` summary prefix.
10648        let c = caixa_with_descricao(Some("Checkout\nflow."));
10649        let rendered = c.validate_descricao().unwrap_err().to_string();
10650        assert!(
10651            rendered.contains(":descricao"),
10652            "diagnostic must name the offending slot: {rendered}",
10653        );
10654        assert!(
10655            rendered.contains("Checkout\\nflow."),
10656            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10657        );
10658    }
10659
10660    #[test]
10661    fn validate_descricao_template_passes() {
10662        // Round-trip pin: the bare `Caixa::template` shape carries
10663        // `:descricao "FIXME — describe this caixa"` (a non-empty
10664        // sentinel), so the template-derived Caixa passes the gate by
10665        // construction. A future template-shape change that omits or
10666        // empties `:descricao` would surface here as a regression.
10667        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10668        c.validate_descricao().unwrap();
10669    }
10670
10671    #[test]
10672    fn validate_descricao_diagnostic_names_offending_slot() {
10673        // Diagnostic-shape pin (peer with
10674        // `validate_repositorio_diagnostic_carries_offending_value`):
10675        // the error's Display surfaces the `:descricao` slot name
10676        // verbatim, so a `feira lint` run can render the diagnostic
10677        // without re-parsing and the author can grep their caixa.lisp
10678        // for the offending `:descricao` line.
10679        let c = caixa_with_descricao(Some(""));
10680        let rendered = c.validate_descricao().unwrap_err().to_string();
10681        assert!(
10682            rendered.contains(":descricao"),
10683            "diagnostic must name the offending slot: {rendered}",
10684        );
10685    }
10686
10687    // ── validate_licenca — universal-axis chart README license shape ──
10688
10689    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
10690        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10691        c.licenca = licenca.map(String::from);
10692        c
10693    }
10694
10695    #[test]
10696    fn validate_licenca_accepts_none() {
10697        // The omit-the-slot identity: `:licenca` is optional. The
10698        // gate is a no-op when the author didn't declare a value —
10699        // every caixa without a `:licenca` line trivially passes,
10700        // and the substrate-side `caixa-helm` renderer falls back to
10701        // the documented `"MIT"` placeholder. Mirrors the peer
10702        // `validate_descricao_accepts_none` posture on the sibling
10703        // `Option<String>` Caixa slot.
10704        let c = caixa_with_licenca(None);
10705        c.validate_licenca().unwrap();
10706    }
10707
10708    #[test]
10709    fn validate_licenca_accepts_canonical_expressions() {
10710        // Positive control: every canonical SPDX expression shape
10711        // pleme-io carries in its existing fixtures + the canonical
10712        // SPDX dual-license / with-exception / `+`-suffix / grouped /
10713        // user-defined-reference shapes all pass the gate. Covers
10714        // the single-license, `OR`-compound, `AND`-compound,
10715        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
10716        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
10717        // production the SPDX 2.1 expression grammar admits that
10718        // sits within the alphabet floor the
10719        // `is_spdx_expression_shape` predicate enforces.
10720        for lic in [
10721            "MIT",
10722            "Apache-2.0",
10723            "Apache-2.0 OR MIT",
10724            "Apache-2.0 AND MIT",
10725            "BSD-3-Clause",
10726            "MPL-2.0",
10727            "GPL-3.0-or-later",
10728            "GPL-2.0+",
10729            "Apache-2.0 WITH LLVM-exception",
10730            "(MIT OR Apache-2.0) AND BSD-3-Clause",
10731            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
10732            "LicenseRef-MyLicense",
10733            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
10734            "x",
10735        ] {
10736            let c = caixa_with_licenca(Some(lic));
10737            c.validate_licenca()
10738                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
10739        }
10740    }
10741
10742    #[test]
10743    fn validate_licenca_rejects_trailing_whitespace() {
10744        // Paste-from-doc whitespace footgun. A trailing space in the
10745        // `:licenca` value would silently break a downstream SPDX
10746        // parser that splits on exact `AND` / `OR` / `WITH` keyword
10747        // boundaries. The shape predicate refuses every trailing
10748        // whitespace byte by construction. Peer with
10749        // `validate_repositorio_rejects_whitespace` and
10750        // `validate_edicao_rejects_trailing_whitespace`.
10751        let c = caixa_with_licenca(Some("MIT "));
10752        let err = c.validate_licenca().unwrap_err();
10753        let ManifestError::LicencaInvalid { licenca, .. } = err else {
10754            panic!("expected LicencaInvalid, got {err:?}");
10755        };
10756        assert_eq!(licenca, "MIT ");
10757    }
10758
10759    #[test]
10760    fn validate_licenca_rejects_leading_whitespace() {
10761        // Symmetric paste-from-doc whitespace footgun on the leading
10762        // boundary — the gate refuses every shape that starts with a
10763        // space byte by construction. Peer with
10764        // `validate_edicao_rejects_leading_whitespace`.
10765        let c = caixa_with_licenca(Some(" MIT"));
10766        let err = c.validate_licenca().unwrap_err();
10767        assert!(
10768            matches!(err, ManifestError::LicencaInvalid { .. }),
10769            "got {err:?}",
10770        );
10771    }
10772
10773    #[test]
10774    fn validate_licenca_rejects_control_char() {
10775        // Paste-from-multiline-doc CRLF footgun — control characters
10776        // at the value boundary land as a malformed line in the
10777        // rendered chart `README.md` `## License` section. Peer with
10778        // `validate_repositorio_rejects_control_char` and
10779        // `validate_edicao_rejects_control_char`.
10780        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
10781            let c = caixa_with_licenca(Some(lic));
10782            let err = c.validate_licenca().unwrap_err();
10783            assert!(
10784                matches!(err, ManifestError::LicencaInvalid { .. }),
10785                "expected LicencaInvalid on {lic:?}, got {err:?}",
10786            );
10787        }
10788    }
10789
10790    #[test]
10791    fn validate_licenca_rejects_tab() {
10792        // Tab-from-aligned-doc footgun — SPDX expressions use a
10793        // single ASCII space between tokens; a tab breaks every
10794        // downstream SPDX parser that splits on exact `" "`
10795        // boundaries.
10796        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
10797        let err = c.validate_licenca().unwrap_err();
10798        assert!(
10799            matches!(err, ManifestError::LicencaInvalid { .. }),
10800            "got {err:?}",
10801        );
10802    }
10803
10804    #[test]
10805    fn validate_licenca_rejects_non_ascii() {
10806        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
10807        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
10808        // ".")` production. The shape predicate refuses every
10809        // non-ASCII byte by construction; peer with
10810        // `validate_edicao_rejects_non_ascii_lookalike`.
10811        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
10812            let c = caixa_with_licenca(Some(lic));
10813            let err = c.validate_licenca().unwrap_err();
10814            assert!(
10815                matches!(err, ManifestError::LicencaInvalid { .. }),
10816                "expected LicencaInvalid on {lic:?}, got {err:?}",
10817            );
10818        }
10819    }
10820
10821    #[test]
10822    fn validate_licenca_rejects_underscore() {
10823        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
10824        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
10825        // snake-case identifier conventions that don't apply to the
10826        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
10827        // "-" / "."`). The shape predicate refuses every underscore
10828        // byte by construction.
10829        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
10830            let c = caixa_with_licenca(Some(lic));
10831            let err = c.validate_licenca().unwrap_err();
10832            assert!(
10833                matches!(err, ManifestError::LicencaInvalid { .. }),
10834                "expected LicencaInvalid on {lic:?}, got {err:?}",
10835            );
10836        }
10837    }
10838
10839    #[test]
10840    fn validate_licenca_rejects_comma_separator() {
10841        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
10842        // SPDX expressions compose multiple licenses via `AND` / `OR`
10843        // keywords, not the comma separator. The shape predicate
10844        // refuses every comma byte by construction.
10845        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
10846            let c = caixa_with_licenca(Some(lic));
10847            let err = c.validate_licenca().unwrap_err();
10848            assert!(
10849                matches!(err, ManifestError::LicencaInvalid { .. }),
10850                "expected LicencaInvalid on {lic:?}, got {err:?}",
10851            );
10852        }
10853    }
10854
10855    #[test]
10856    fn validate_licenca_rejects_slash_dual_license() {
10857        // Slash-dual-license colloquial idiom footgun — the
10858        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
10859        // `package.license` field but non-SPDX; the SPDX equivalent
10860        // is `MIT OR Apache-2.0`. The shape predicate refuses every
10861        // forward-slash byte by construction.
10862        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
10863            let c = caixa_with_licenca(Some(lic));
10864            let err = c.validate_licenca().unwrap_err();
10865            assert!(
10866                matches!(err, ManifestError::LicencaInvalid { .. }),
10867                "expected LicencaInvalid on {lic:?}, got {err:?}",
10868            );
10869        }
10870    }
10871
10872    #[test]
10873    fn validate_licenca_rejects_semicolon_separator() {
10874        // Semicolon-list-separator confusion footgun — adjacent to
10875        // the comma-separator idiom, every list-separator-belongs-
10876        // to-list-grammar confusion lands here.
10877        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
10878        let err = c.validate_licenca().unwrap_err();
10879        assert!(
10880            matches!(err, ManifestError::LicencaInvalid { .. }),
10881            "got {err:?}",
10882        );
10883    }
10884
10885    #[test]
10886    fn validate_licenca_empty_takes_precedence_over_shape() {
10887        // Empty-first cascade pin: the empty `Some("")` surfaces the
10888        // narrower `LicencaEmpty` not the shape-predicate-wrapped
10889        // `LicencaInvalid`, mirroring the peer
10890        // `validate_edicao_empty_takes_precedence_over_shape` and
10891        // `validate_repositorio_empty_takes_precedence_over_shape`
10892        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
10893        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
10894        // The shape predicate also refuses the empty input
10895        // (defensively — `"must not be empty"`), but the manifest-
10896        // layer empty arm runs first to surface the narrower
10897        // diagnostic verbatim.
10898        let c = caixa_with_licenca(Some(""));
10899        let err = c.validate_licenca().unwrap_err();
10900        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10901    }
10902
10903    #[test]
10904    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
10905        // Diagnostic-shape pin on the shape-predicate arm (peer with
10906        // `validate_edicao_invalid_diagnostic_carries_offending_value`
10907        // and `validate_repositorio_diagnostic_carries_offending_value`):
10908        // the error's Display surfaces the offending value + slot
10909        // name verbatim, so a `feira lint` run can render the
10910        // diagnostic without re-parsing and the author can grep
10911        // their caixa.lisp for the offending `:licenca` value.
10912        let c = caixa_with_licenca(Some("Apache_2.0"));
10913        let rendered = c.validate_licenca().unwrap_err().to_string();
10914        assert!(
10915            rendered.contains(":licenca"),
10916            "diagnostic must name the offending slot: {rendered}",
10917        );
10918        assert!(
10919            rendered.contains("Apache_2.0"),
10920            "diagnostic must quote the offending value: {rendered}",
10921        );
10922    }
10923
10924    #[test]
10925    fn validate_licenca_rejects_empty_some() {
10926        // Canonical paste-from-blank-doc footgun. Without this gate
10927        // the empty `Some("")` silently passed the renderer's
10928        // `Option::unwrap_or_else(|| "MIT".into())` (which only
10929        // fires on `None`) and landed as a bare trailing period in
10930        // the rendered chart `README.md` `## License` section.
10931        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
10932        // arm on the sibling `Option<String>` Caixa slot.
10933        let c = caixa_with_licenca(Some(""));
10934        let err = c.validate_licenca().unwrap_err();
10935        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
10936    }
10937
10938    #[test]
10939    fn validate_licenca_template_passes() {
10940        // Round-trip pin: the bare `Caixa::template` shape (whether
10941        // it carries `:licenca` or omits it) passes the gate by
10942        // construction. A future template-shape change that
10943        // introduced `(:licenca "")` would surface here as a
10944        // regression. Mirrors the peer
10945        // `validate_descricao_template_passes` pin.
10946        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10947        c.validate_licenca().unwrap();
10948    }
10949
10950    #[test]
10951    fn validate_licenca_diagnostic_names_offending_slot() {
10952        // Diagnostic-shape pin (peer with
10953        // `validate_descricao_diagnostic_names_offending_slot`):
10954        // the error's Display surfaces the `:licenca` slot name
10955        // verbatim, so a `feira lint` run can render the diagnostic
10956        // without re-parsing and the author can grep their caixa.lisp
10957        // for the offending `:licenca` line.
10958        let c = caixa_with_licenca(Some(""));
10959        let rendered = c.validate_licenca().unwrap_err().to_string();
10960        assert!(
10961            rendered.contains(":licenca"),
10962            "diagnostic must name the offending slot: {rendered}",
10963        );
10964    }
10965
10966    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
10967
10968    #[test]
10969    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
10970        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
10971        // pin: [`Caixa::licenca`] must return the `:licenca` typed
10972        // byte-string verbatim as an `Option<&str>`, byte-equal to the
10973        // raw `self.licenca.as_deref()` access across every
10974        // representative value in the accept-set — `None` (the "omit
10975        // the slot to defer to the caixa-helm renderer's `MIT`
10976        // fallback" arm every existing fixture without a `:licenca`
10977        // line carries), `Some("")` (a past-the-guard sentinel that
10978        // pins the accessor doesn't perform a silent
10979        // `Some("") → None` collapse on the empty arm — validate
10980        // rejects `Some("")` through `LicencaEmpty` but the accessor
10981        // must ship the raw slot verbatim so a validate-time gate
10982        // regression surfaces at the caixa-helm emit boundary rather
10983        // than being silently absorbed into the fallback), `Some("MIT")`
10984        // (the canonical single-license shape every `feira init`
10985        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
10986        // canonical `OR`-compound shape the peer
10987        // `validate_licenca_accepts_canonical_expressions` positive
10988        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
10989        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
10990        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
10991        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
10992        // guard sentinels — validate rejects each through
10993        // `LicencaInvalid` but the accessor must ship the raw slot
10994        // verbatim).
10995        //
10996        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
10997        // accessor pin on the substrate primitive — opens the "outer
10998        // [`Caixa`] `Option<&str>` scalar" projection pattern the
10999        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
11000        // future lifts fold on. Sibling in shape to the peer per-`:placement`
11001        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11002        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11003        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11004        // axes, extended onto the outer top-level [`Caixa`] universal-
11005        // axis surface. Pins against a future silent detour that
11006        // returned an owned `Option<String>` (which would type-check
11007        // but silently allocate on every accessor call, breaking the
11008        // zero-cost projection every peer sibling accessor carries), a
11009        // `Some("") → None` collapse (which would silently absorb the
11010        // `LicencaEmpty` refusal case at the accessor boundary and the
11011        // caixa-helm emit path would silently fall back to `"MIT"` on
11012        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
11013        // `None → Some("MIT")` collapse (which would silently reify
11014        // the caixa-helm renderer's `"MIT"` fallback at the accessor
11015        // boundary and every downstream consumer keying off the
11016        // `Option::is_none()` discriminator would lose the "author
11017        // omitted the slot" signal).
11018        for licenca in [
11019            None,
11020            Some(""),
11021            Some("MIT"),
11022            Some("Apache-2.0 OR MIT"),
11023            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
11024            Some("MIT "),
11025            Some(" MIT"),
11026            Some("MIT\n"),
11027            Some("Apache_2.0"),
11028            Some("MIT,Apache-2.0"),
11029        ] {
11030            let c = caixa_with_licenca(licenca);
11031            assert_eq!(
11032                c.licenca(),
11033                licenca,
11034                "Caixa::licenca must return :licenca verbatim (got {:?}, \
11035                 expected {licenca:?})",
11036                c.licenca(),
11037            );
11038            assert_eq!(
11039                c.licenca(),
11040                c.licenca.as_deref(),
11041                "Caixa::licenca must byte-equal the raw \
11042                 `self.licenca.as_deref()` field access across every \
11043                 value in the Option<&str> accept-set",
11044            );
11045        }
11046    }
11047
11048    #[test]
11049    fn validate_licenca_empty_arm_routes_through_accessor() {
11050        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
11051        // must key off [`Caixa::licenca`], not the raw
11052        // `self.licenca.as_deref()` field access. Structurally: a
11053        // `Caixa { licenca: Some(""), .. }` must surface the
11054        // `LicencaEmpty` refusal exactly, and a
11055        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
11056        // single-license form) must pass validate. The pair jointly
11057        // pins the accessor + validate-gate composition: any future
11058        // silent detour that had the accessor return `None` on the
11059        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
11060        // silently absorb the `LicencaEmpty` refusal at the accessor
11061        // boundary and the validate gate would accept a struct-literal
11062        // `Caixa { licenca: Some(""), .. }` — the composition pin
11063        // catches that at caixa-core build time.
11064        //
11065        // Peer of the per-`:politicas :circuit-breaker`
11066        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11067        // accessor-composition pin
11068        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11069        // on the sibling per-M3-mesh-slot required-`u32` axis — same
11070        // "the validate / shape-gate predicate must route through the
11071        // substrate-primitive typed dispatch" discipline extended onto
11072        // the outer top-level [`Caixa`] universal-axis
11073        // `Option<&str>`-composition surface.
11074        let c = caixa_with_licenca(Some(""));
11075        assert!(
11076            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
11077            "validate_licenca must reject licenca == Some(\"\") with \
11078             LicencaEmpty — the accessor and the validate gate must \
11079             route through the same substrate-primitive typed dispatch \
11080             on the :licenca empty arm",
11081        );
11082        let c = caixa_with_licenca(Some("MIT"));
11083        assert!(
11084            c.validate_licenca().is_ok(),
11085            "validate_licenca must accept licenca == Some(\"MIT\") \
11086             (the canonical single-license SPDX shape)",
11087        );
11088    }
11089
11090    #[test]
11091    fn licenca_projects_option_str_by_borrow() {
11092        // The by-borrow pin: [`Caixa::licenca`] returns
11093        // `Option<&str>` by borrow — the `&str` borrows the underlying
11094        // `String` storage of the `Option<String>` slot and the
11095        // accessor must not allocate a fresh `String` on every call.
11096        // Peer of the per-`:placement`
11097        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11098        // borrow pin on the peer per-M3-mesh-slot
11099        // `Option<&str>`-return axis, extended onto the outer top-
11100        // level [`Caixa`] universal-axis `Option<&str>` shape — the
11101        // accessor's returned `&str` must borrow from `&self` (the
11102        // returned reference's lifetime is tied to `&self`), and
11103        // calling the accessor twice on the same [`Caixa`] must yield
11104        // the same `Option<&str>` verbatim (idempotent, no side
11105        // effects on `&self`).
11106        //
11107        // Pins against a future silent detour that returned an owned
11108        // `Option<String>` (which would type-check but silently
11109        // allocate on every call, breaking the zero-cost projection
11110        // every peer sibling accessor carries), or a one-arm-only
11111        // accessor that returned a saturating value on some sentinel
11112        // input (breaking the pass-through invariant the sibling
11113        // required-scalar accessors carry).
11114        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
11115            let c = caixa_with_licenca(licenca);
11116            let first = c.licenca();
11117            let second = c.licenca();
11118            assert_eq!(
11119                first, second,
11120                "Caixa::licenca must be idempotent — two successive \
11121                 calls on the same &self must return the same \
11122                 Option<&str>",
11123            );
11124            assert_eq!(
11125                first, licenca,
11126                "Caixa::licenca must return :licenca verbatim by \
11127                 borrow — got {first:?}, expected {licenca:?}",
11128            );
11129        }
11130    }
11131
11132    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
11133
11134    #[test]
11135    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
11136        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
11137        // pin: [`Caixa::repositorio`] must return the `:repositorio`
11138        // typed byte-string verbatim as an `Option<&str>`, byte-equal
11139        // to the raw `self.repositorio.as_deref()` access across every
11140        // representative value in the accept-set — `None` (the "omit
11141        // the slot to defer to the per-renderer placeholder" arm every
11142        // existing fixture without a `:repositorio` line carries),
11143        // `Some("")` (a past-the-guard sentinel that pins the accessor
11144        // doesn't perform a silent `Some("") → None` collapse on the
11145        // empty arm — validate rejects `Some("")` through
11146        // `RepositorioEmpty` but the accessor must ship the raw slot
11147        // verbatim so a validate-time gate regression surfaces at the
11148        // caixa-helm / caixa-flux emit boundary rather than being
11149        // silently absorbed into the per-renderer fallback),
11150        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
11151        // shorthand every existing manifest fixture across
11152        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
11153        // `Some("https://github.com/pleme-io/checkout")` (the canonical
11154        // `https://` URL the README quickstart uses),
11155        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
11156        // `Some("git://github.com/pleme-io/checkout.git")` /
11157        // `Some("git@github.com:pleme-io/checkout.git")` /
11158        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
11159        // github scheme the shared `is_git_repo_url` predicate
11160        // documents), and five past-the-guard sentinels for the
11161        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
11162        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
11163        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
11164        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
11165        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
11166        // sentinels pin the accessor doesn't silently absorb the
11167        // refusal cases into a fallback).
11168        //
11169        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
11170        // accessor pin on the substrate primitive — sibling of the peer
11171        // [`Caixa::licenca`] (6d5bc28) pin
11172        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
11173        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
11174        // projection pin pattern this pin folds on. Sibling in shape to
11175        // the peer per-`:placement`
11176        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11177        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11178        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11179        // axes, extended onto the outer top-level [`Caixa`] universal-
11180        // axis surface. Pins against a future silent detour that
11181        // returned an owned `Option<String>` (which would type-check
11182        // but silently allocate on every accessor call, breaking the
11183        // zero-cost projection every peer sibling accessor carries), a
11184        // `Some("") → None` collapse (which would silently absorb the
11185        // `RepositorioEmpty` refusal case at the accessor boundary and
11186        // the caixa-helm `Chart.yaml` `home:` fold would silently
11187        // render a `home: null` / omitted field on a struct-literal
11188        // `Caixa { repositorio: Some(""), .. }`), or a
11189        // `None → Some(<default>)` collapse (which would silently reify
11190        // the per-renderer fallback at the accessor boundary and every
11191        // downstream consumer keying off the `Option::is_none()`
11192        // discriminator would lose the "author omitted the slot"
11193        // signal).
11194        for repositorio in [
11195            None,
11196            Some(""),
11197            Some("github:pleme-io/hello-rio"),
11198            Some("https://github.com/pleme-io/checkout"),
11199            Some("ssh://git@github.com/pleme-io/checkout.git"),
11200            Some("git://github.com/pleme-io/checkout.git"),
11201            Some("git@github.com:pleme-io/checkout.git"),
11202            Some("file:///opt/mirrors/pleme-io/checkout"),
11203            Some("pleme-io/checkout"),
11204            Some("-upload-pack=evil"),
11205            Some("github:pleme-io/checkout?ref=main"),
11206            Some("github:pleme-io/checkout#main"),
11207            Some("github:pleme-io/{tpl}"),
11208        ] {
11209            let c = caixa_with_repositorio(repositorio);
11210            assert_eq!(
11211                c.repositorio(),
11212                repositorio,
11213                "Caixa::repositorio must return :repositorio verbatim \
11214                 (got {:?}, expected {repositorio:?})",
11215                c.repositorio(),
11216            );
11217            assert_eq!(
11218                c.repositorio(),
11219                c.repositorio.as_deref(),
11220                "Caixa::repositorio must byte-equal the raw \
11221                 `self.repositorio.as_deref()` field access across every \
11222                 value in the Option<&str> accept-set",
11223            );
11224        }
11225    }
11226
11227    #[test]
11228    fn validate_repositorio_empty_arm_routes_through_accessor() {
11229        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
11230        // gate must key off [`Caixa::repositorio`], not the raw
11231        // `self.repositorio.as_deref()` field access. Structurally: a
11232        // `Caixa { repositorio: Some(""), .. }` must surface the
11233        // `RepositorioEmpty` refusal exactly, and a
11234        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
11235        // (the canonical `github:` shorthand form) must pass validate.
11236        // The pair jointly pins the accessor + validate-gate
11237        // composition: any future silent detour that had the accessor
11238        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
11239        // collapse) would silently absorb the `RepositorioEmpty` refusal
11240        // at the accessor boundary and the validate gate would accept a
11241        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
11242        // composition pin catches that at caixa-core build time.
11243        //
11244        // Peer of the [`Caixa::licenca`] (6d5bc28)
11245        // `validate_licenca_empty_arm_routes_through_accessor`
11246        // composition pin on the sibling outer top-level [`Caixa`]
11247        // `Option<&str>` universal-axis surface — same "the validate /
11248        // shape-gate predicate must route through the substrate-
11249        // primitive typed dispatch" discipline extended onto the second
11250        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
11251        // composition surface.
11252        let c = caixa_with_repositorio(Some(""));
11253        assert!(
11254            matches!(
11255                c.validate_repositorio(),
11256                Err(ManifestError::RepositorioEmpty),
11257            ),
11258            "validate_repositorio must reject repositorio == Some(\"\") \
11259             with RepositorioEmpty — the accessor and the validate gate \
11260             must route through the same substrate-primitive typed \
11261             dispatch on the :repositorio empty arm",
11262        );
11263        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
11264        assert!(
11265            c.validate_repositorio().is_ok(),
11266            "validate_repositorio must accept repositorio == \
11267             Some(\"github:pleme-io/hello-rio\") (the canonical \
11268             `github:` shorthand git-repo-URL shape)",
11269        );
11270    }
11271
11272    #[test]
11273    fn repositorio_projects_option_str_by_borrow() {
11274        // The by-borrow pin: [`Caixa::repositorio`] returns
11275        // `Option<&str>` by borrow — the `&str` borrows the underlying
11276        // `String` storage of the `Option<String>` slot and the
11277        // accessor must not allocate a fresh `String` on every call.
11278        // Peer of the per-`:placement`
11279        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
11280        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
11281        // `Option<&str>`-return axes, extended onto the second outer
11282        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
11283        // the accessor's returned `&str` must borrow from `&self` (the
11284        // returned reference's lifetime is tied to `&self`), and
11285        // calling the accessor twice on the same [`Caixa`] must yield
11286        // the same `Option<&str>` verbatim (idempotent, no side effects
11287        // on `&self`).
11288        //
11289        // Pins against a future silent detour that returned an owned
11290        // `Option<String>` (which would type-check but silently
11291        // allocate on every call, breaking the zero-cost projection
11292        // every peer sibling accessor carries), or a one-arm-only
11293        // accessor that returned a saturating value on some sentinel
11294        // input (breaking the pass-through invariant the sibling
11295        // required-scalar accessors carry).
11296        for repositorio in [
11297            None,
11298            Some(""),
11299            Some("github:pleme-io/hello-rio"),
11300            Some("https://github.com/pleme-io/checkout"),
11301        ] {
11302            let c = caixa_with_repositorio(repositorio);
11303            let first = c.repositorio();
11304            let second = c.repositorio();
11305            assert_eq!(
11306                first, second,
11307                "Caixa::repositorio must be idempotent — two successive \
11308                 calls on the same &self must return the same \
11309                 Option<&str>",
11310            );
11311            assert_eq!(
11312                first, repositorio,
11313                "Caixa::repositorio must return :repositorio verbatim by \
11314                 borrow — got {first:?}, expected {repositorio:?}",
11315            );
11316        }
11317    }
11318
11319    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
11320
11321    #[test]
11322    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
11323        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
11324        // return the author-declared `:repositorio` byte-string verbatim
11325        // on the `Some` arm — no scheme rewrite, no trailing-slash
11326        // canonicalization, no `github:` → `https://github.com/`
11327        // desugaring. The resolved-URL composer is the projection of
11328        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
11329        // the `String`-return arity every substrate-side field-fill
11330        // consumer keys off; on the `Some` arm the projection is
11331        // `str::to_owned` verbatim, so every accept-set value the
11332        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
11333        // across_permutations` pin covers (`https://…`, `github:…`,
11334        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
11335        // guard sentinel `pleme-io/…`) must survive the accessor
11336        // byte-equal. Pins against a future silent detour that rewrote
11337        // the `github:` shorthand to the `https://github.com/` full URL
11338        // at the accessor boundary (which would silently split the
11339        // resolved-URL surface from the raw [`Caixa::repositorio`]
11340        // accessor's documented pass-through invariant), or a trailing-
11341        // slash normalization (which would silently break the
11342        // FluxCD `GitRepository` `spec.url` byte-exact match every
11343        // downstream consumer keys the source-controller reconcile off).
11344        for repositorio in [
11345            "github:pleme-io/hello-rio",
11346            "https://github.com/pleme-io/checkout",
11347            "ssh://git@github.com/pleme-io/checkout.git",
11348            "git://github.com/pleme-io/checkout.git",
11349            "git@github.com:pleme-io/checkout.git",
11350            "file:///opt/mirrors/pleme-io/checkout",
11351        ] {
11352            let c = caixa_with_repositorio(Some(repositorio));
11353            assert_eq!(
11354                c.canonical_git_url(),
11355                repositorio,
11356                "Caixa::canonical_git_url on the Some arm must return \
11357                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
11358                c.canonical_git_url(),
11359            );
11360        }
11361    }
11362
11363    #[test]
11364    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
11365        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
11366        // `None` arm must emit the substrate's canonical pleme-org github
11367        // URL derived from `caixa.nome()` — `https://github.com/<org>/
11368        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
11369        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
11370        // is the exact byte-image of the prior inline
11371        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
11372        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
11373        // re-derived open-coded. Pins against a future silent detour
11374        // that migrated the `<org>` segment to a different constant (a
11375        // fork rebranding that split off a new
11376        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
11377        // to migrate onto), a scheme change (`https://` → `git://` or
11378        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
11379        // override (which would break the substrate-wide single-source-
11380        // of-truth guarantee this method encodes).
11381        let c = caixa_with_repositorio(None);
11382        let expected = format!(
11383            "https://github.com/{org}/{nome}",
11384            org = crate::DEFAULT_PLEME_GIT_ORG,
11385            nome = c.nome(),
11386        );
11387        assert_eq!(
11388            c.canonical_git_url(),
11389            expected,
11390            "Caixa::canonical_git_url on the None arm must fold through \
11391             the substrate's canonical pleme-org github URL fallback \
11392             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
11393             {:?}, expected {expected:?}",
11394            c.canonical_git_url(),
11395        );
11396    }
11397
11398    #[test]
11399    fn canonical_git_url_byte_matches_manual_composition() {
11400        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
11401        // byte-identically to the manual open-coded
11402        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
11403        //  format!("https://github.com/{org}/{nome}", ...))` composition
11404        // every prior substrate-side caller re-derived. Guards the
11405        // paired-site convergence just applied at caixa-flux's
11406        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
11407        // now routes through this accessor): a future implementation of
11408        // this method that reordered the format arguments, swapped the
11409        // `<org>` constant for a different one, or interposed a
11410        // canonicalization pass on the `Some` arm surfaces here as a
11411        // caixa-core build-time test failure rather than as a downstream
11412        // FluxCD `GitRepository` reconcile mismatch far from this
11413        // method's source.
11414        for repositorio in [
11415            None,
11416            Some("github:pleme-io/hello-rio"),
11417            Some("https://github.com/pleme-io/checkout"),
11418            Some("ssh://git@github.com/pleme-io/checkout.git"),
11419        ] {
11420            let c = caixa_with_repositorio(repositorio);
11421            let manual = c.repositorio().map_or_else(
11422                || {
11423                    format!(
11424                        "https://github.com/{org}/{nome}",
11425                        org = crate::DEFAULT_PLEME_GIT_ORG,
11426                        nome = c.nome(),
11427                    )
11428                },
11429                str::to_owned,
11430            );
11431            assert_eq!(
11432                c.canonical_git_url(),
11433                manual,
11434                "Caixa::canonical_git_url must byte-equal the manual \
11435                 open-coded `repositorio().map(str::to_owned)\
11436                 .unwrap_or_else(|| format!(...))` composition across \
11437                 every representative :repositorio input — got {:?}, \
11438                 expected {manual:?}",
11439                c.canonical_git_url(),
11440            );
11441        }
11442    }
11443
11444    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
11445
11446    #[test]
11447    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
11448        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
11449        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
11450        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
11451        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
11452        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
11453        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
11454        // the `0.0.0` boundary case. Every accept-set value the peer
11455        // validate gate lets through must survive the resolved-tag
11456        // projection byte-equal.
11457        for versao in [
11458            "0.1.0",
11459            "0.0.0",
11460            "1.0.0",
11461            "1.2.3-rc.1",
11462            "1.2.3+build.42",
11463            "1.2.3-rc.1+build.42",
11464        ] {
11465            let c = caixa_with_versao(versao);
11466            let expected = format!(
11467                "{prefix}{versao}",
11468                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11469            );
11470            assert_eq!(
11471                c.publish_tag(),
11472                expected,
11473                "Caixa::publish_tag must compose \
11474                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
11475                 :versao ({versao:?}) verbatim — got {got:?}, \
11476                 expected {expected:?}",
11477                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11478                got = c.publish_tag(),
11479            );
11480        }
11481    }
11482
11483    #[test]
11484    fn publish_tag_starts_with_default_publish_tag_prefix() {
11485        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
11486        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
11487        // byte-string on every input, guarding a hypothetical future
11488        // implementation that migrated the prefix segment to an inline
11489        // literal (`"v"`) that would silently drift from any rebrand of
11490        // the lifted constant. Peer to the sibling caixa-flux
11491        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
11492        // test which pins the same prefix invariant at the reader-side
11493        // `GitRefSpec::Tag` emit site.
11494        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
11495            let c = caixa_with_versao(versao);
11496            let tag = c.publish_tag();
11497            assert!(
11498                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
11499                "Caixa::publish_tag emission {tag:?} must start with \
11500                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
11501                 ({prefix:?})",
11502                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11503            );
11504        }
11505    }
11506
11507    #[test]
11508    fn publish_tag_byte_matches_manual_composition() {
11509        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
11510        // identically to the manual open-coded
11511        // `format!("{prefix}{versao}", prefix =
11512        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
11513        //  caixa.versao())` composition every prior substrate-side
11514        // caller re-derived. Guards the paired-site convergence just
11515        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
11516        // `git_ref` composer (which now routes through this accessor):
11517        // a future implementation of this method that reordered the
11518        // format arguments, swapped the `<prefix>` constant for a
11519        // different one, or interposed a canonicalization pass on the
11520        // `:versao` axis surfaces here as a caixa-core build-time test
11521        // failure rather than as a downstream FluxCD `GitRepository`
11522        // reconcile mismatch far from this method's source.
11523        for versao in [
11524            "0.1.0",
11525            "0.0.0",
11526            "1.2.3-rc.1",
11527            "1.2.3+build.42",
11528            "1.2.3-rc.1+build.42",
11529        ] {
11530            let c = caixa_with_versao(versao);
11531            let manual = format!(
11532                "{prefix}{versao}",
11533                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11534                versao = c.versao(),
11535            );
11536            assert_eq!(
11537                c.publish_tag(),
11538                manual,
11539                "Caixa::publish_tag must byte-equal the manual \
11540                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
11541                 composition across every representative :versao input \
11542                 — got {got:?}, expected {manual:?}",
11543                got = c.publish_tag(),
11544            );
11545        }
11546    }
11547
11548    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
11549
11550    #[test]
11551    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
11552        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
11553        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
11554        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
11555        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
11556        // set sweep documents — single-word, hyphen-joined, version-
11557        // suffixed, single-char, two-char, digit-start, retry-suffixed.
11558        // Every accept-set value the peer validate gate lets through must
11559        // survive the resolved-chart-name projection byte-equal.
11560        for nome in [
11561            "checkout",
11562            "cart-v2",
11563            "a",
11564            "db",
11565            "3rd-party-shim",
11566            "payment-retry",
11567            "0",
11568        ] {
11569            let c = caixa_with_nome(nome);
11570            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
11571            assert_eq!(
11572                c.lareira_chart_name(),
11573                expected,
11574                "Caixa::lareira_chart_name must compose \
11575                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
11576                 :nome ({nome:?}) verbatim — got {got:?}, \
11577                 expected {expected:?}",
11578                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
11579                got = c.lareira_chart_name(),
11580            );
11581        }
11582    }
11583
11584    #[test]
11585    fn lareira_chart_name_starts_with_lifted_prefix() {
11586        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
11587        // must begin with the canonical
11588        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
11589        // input, guarding a hypothetical future implementation that
11590        // migrated the prefix segment to an inline literal (`"lareira-"`)
11591        // that would silently drift from any rebrand of the lifted
11592        // constant. Peer to the sibling
11593        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
11594        // the co-resident resolved-publish-tag composer's prefix axis.
11595        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
11596            let c = caixa_with_nome(nome);
11597            let chart = c.lareira_chart_name();
11598            assert!(
11599                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
11600                "Caixa::lareira_chart_name emission {chart:?} must start \
11601                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
11602                 ({prefix:?})",
11603                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
11604            );
11605        }
11606    }
11607
11608    #[test]
11609    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
11610        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
11611        // byte-identically to the manual open-coded
11612        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
11613        // composition every prior substrate-side caller re-derived.
11614        // Guards the paired-site convergence just applied at caixa-helm's
11615        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
11616        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
11617        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
11618        // composer (all of which now route through this accessor): a
11619        // future implementation of this method that reordered the
11620        // composition arguments, swapped the `<prefix>` constant for a
11621        // different one, or interposed a canonicalization pass on the
11622        // `:nome` axis surfaces here as a caixa-core build-time test
11623        // failure rather than as a downstream Helm chart-render / FluxCD
11624        // reconcile / tatara Process-CR mismatch far from this method's
11625        // source.
11626        for nome in [
11627            "checkout",
11628            "cart-v2",
11629            "a",
11630            "db",
11631            "3rd-party-shim",
11632            "payment-retry",
11633        ] {
11634            let c = caixa_with_nome(nome);
11635            let manual = crate::lareira_chart_name(c.nome());
11636            assert_eq!(
11637                c.lareira_chart_name(),
11638                manual,
11639                "Caixa::lareira_chart_name must byte-equal the manual \
11640                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
11641                 composition across every representative :nome input — \
11642                 got {got:?}, expected {manual:?}",
11643                got = c.lareira_chart_name(),
11644            );
11645        }
11646    }
11647
11648    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
11649
11650    #[test]
11651    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
11652        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
11653        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
11654        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
11655        // across the full paired `(registry, :nome)` accept-set — every
11656        // representative registry the substrate-side emitters carry
11657        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
11658        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
11659        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
11660        // inline_format` render-side pin exercises; `registry.example.
11661        // com`, an off-org shape; `localhost:5000`, the local-dev shape
11662        // every `feira chart` iteration path lands under) × every DNS-
11663        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
11664        // forms` positive-set sweep documents (single-word, hyphen-
11665        // joined, single-char, two-char, digit-start, retry-suffixed).
11666        // Every accept-set pair the peer validate gates let through must
11667        // survive the resolved-OCI-ref projection byte-equal.
11668        for registry in [
11669            "ghcr.io/pleme-io/charts",
11670            "ghcr.io/pleme-io",
11671            "registry.example.com",
11672            "localhost:5000",
11673        ] {
11674            for nome in [
11675                "checkout",
11676                "cart-v2",
11677                "a",
11678                "db",
11679                "3rd-party-shim",
11680                "payment-retry",
11681                "0",
11682            ] {
11683                let c = caixa_with_nome(nome);
11684                let expected = format!(
11685                    "{scheme}{registry}/{chart}",
11686                    scheme = crate::OCI_SCHEME_PREFIX,
11687                    chart = crate::lareira_chart_name(nome),
11688                );
11689                assert_eq!(
11690                    c.oci_chart_ref(registry),
11691                    expected,
11692                    "Caixa::oci_chart_ref must compose \
11693                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
11694                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
11695                     expected {expected:?}",
11696                    scheme = crate::OCI_SCHEME_PREFIX,
11697                    got = c.oci_chart_ref(registry),
11698                );
11699            }
11700        }
11701    }
11702
11703    #[test]
11704    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
11705        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
11706        // emission must begin with the canonical
11707        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
11708        // a hypothetical future implementation that migrated the scheme
11709        // segment to an inline literal (`"oci://"`) that would silently
11710        // drift from any rebrand of the lifted constant. Peer to the
11711        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
11712        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
11713        // co-resident resolved-publish-tag / resolved-chart-name
11714        // composers' prefix axes.
11715        for registry in [
11716            "ghcr.io/pleme-io/charts",
11717            "ghcr.io/pleme-io",
11718            "localhost:5000",
11719        ] {
11720            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
11721                let c = caixa_with_nome(nome);
11722                let ref_ = c.oci_chart_ref(registry);
11723                assert!(
11724                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
11725                    "Caixa::oci_chart_ref emission {ref_:?} must start \
11726                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
11727                     — registry ({registry:?}), :nome ({nome:?})",
11728                    scheme = crate::OCI_SCHEME_PREFIX,
11729                );
11730            }
11731        }
11732    }
11733
11734    #[test]
11735    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
11736        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
11737        // identically to the manual open-coded
11738        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
11739        // composition every prior substrate-side caller re-derived.
11740        // Guards the paired-site convergence just applied at caixa-
11741        // tatara's [`derive_chart_ref`] helper (which now routes through
11742        // this accessor): a future implementation of this method that
11743        // reordered the composition arguments, swapped the `<scheme>`
11744        // constant for a different one, migrated the `<chart>` segment
11745        // off the paired [`crate::lareira_chart_name`] composer, or
11746        // interposed a canonicalization pass on either input axis
11747        // surfaces here as a caixa-core build-time test failure rather
11748        // than as a downstream `helm install` / FluxCD OCI-source
11749        // reconcile / tatara `Process`-CR mismatch far from this
11750        // method's source. Sibling to the peer
11751        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
11752        // / [`publish_tag_byte_matches_manual_composition`] /
11753        // [`canonical_git_url_byte_matches_manual_composition`] byte-
11754        // parity pins that carry the same discipline on the co-resident
11755        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
11756        // composers.
11757        for registry in [
11758            "ghcr.io/pleme-io/charts",
11759            "ghcr.io/pleme-io",
11760            "registry.example.com",
11761            "localhost:5000",
11762        ] {
11763            for nome in [
11764                "checkout",
11765                "cart-v2",
11766                "a",
11767                "db",
11768                "3rd-party-shim",
11769                "payment-retry",
11770            ] {
11771                let c = caixa_with_nome(nome);
11772                let manual = crate::oci_chart_ref(registry, c.nome());
11773                assert_eq!(
11774                    c.oci_chart_ref(registry),
11775                    manual,
11776                    "Caixa::oci_chart_ref must byte-equal the manual \
11777                     open-coded `caixa_core::oci_chart_ref(registry, \
11778                     caixa.nome())` composition across every representative \
11779                     (registry, :nome) pair — registry ({registry:?}), \
11780                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
11781                    got = c.oci_chart_ref(registry),
11782                );
11783            }
11784        }
11785    }
11786
11787    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
11788
11789    #[test]
11790    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
11791        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
11792        // pin: [`Caixa::descricao`] must return the `:descricao` typed
11793        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11794        // raw `self.descricao.as_deref()` access across every
11795        // representative value in the accept-set — `None` (the "omit
11796        // the slot to defer to the per-renderer `caixa.nome`-derived
11797        // fallback" arm every existing fixture without a `:descricao`
11798        // line carries), `Some("")` (a past-the-guard sentinel that
11799        // pins the accessor doesn't perform a silent `Some("") → None`
11800        // collapse on the empty arm — validate rejects `Some("")`
11801        // through `DescricaoEmpty` but the accessor must ship the raw
11802        // slot verbatim so a validate-time gate regression surfaces at
11803        // the caixa-helm / caixa-feira emit boundary rather than being
11804        // silently absorbed into the per-renderer `caixa.nome`-derived
11805        // fallback), `Some("Checkout flow.")` (the canonical one-line
11806        // prose descriptor the peer
11807        // `validate_descricao_accepts_canonical_value` positive sweep
11808        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
11809        // Servico.")` (the multi-byte Unicode continuation-byte shape
11810        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
11811        // multi-glyph Unicode shape the peer
11812        // `is_chart_description_shape` predicate accepts), and five
11813        // past-the-guard sentinels for the `DescricaoInvalid` refusal
11814        // cases (`Some(" Checkout flow.")` leading-whitespace,
11815        // `Some("Checkout flow. ")` trailing-whitespace,
11816        // `Some("Checkout\nflow.")` embedded-LF,
11817        // `Some("Checkout\tflow.")` embedded-TAB, and
11818        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
11819        // the accessor doesn't silently absorb the refusal cases into
11820        // a fallback).
11821        //
11822        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
11823        // accessor pin on the substrate primitive — sibling of the peer
11824        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
11825        // (cc7332d) pins that opened the "outer [`Caixa`]
11826        // `Option<&str>` scalar" projection pin pattern this pin folds
11827        // on. Sibling in shape to the peer per-`:placement`
11828        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11829        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11830        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11831        // axes, extended onto the outer top-level [`Caixa`] universal-
11832        // axis surface. Pins against a future silent detour that
11833        // returned an owned `Option<String>` (which would type-check
11834        // but silently allocate on every accessor call, breaking the
11835        // zero-cost projection every peer sibling accessor carries), a
11836        // `Some("") → None` collapse (which would silently absorb the
11837        // `DescricaoEmpty` refusal case at the accessor boundary and
11838        // the caixa-helm `Chart.yaml` `description:` fold would
11839        // silently render a `caixa.nome`-derived fallback on a
11840        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
11841        // `None → Some(<default>)` collapse (which would silently
11842        // reify the per-renderer `caixa.nome`-derived fallback at the
11843        // accessor boundary and every downstream consumer keying off
11844        // the `Option::is_none()` discriminator would lose the "author
11845        // omitted the slot" signal).
11846        for descricao in [
11847            None,
11848            Some(""),
11849            Some("Checkout flow."),
11850            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
11851            Some("→ — · ✓"),
11852            Some(" Checkout flow."),
11853            Some("Checkout flow. "),
11854            Some("Checkout\nflow."),
11855            Some("Checkout\tflow."),
11856            Some("Checkout\x00flow."),
11857        ] {
11858            let c = caixa_with_descricao(descricao);
11859            assert_eq!(
11860                c.descricao(),
11861                descricao,
11862                "Caixa::descricao must return :descricao verbatim (got \
11863                 {:?}, expected {descricao:?})",
11864                c.descricao(),
11865            );
11866            assert_eq!(
11867                c.descricao(),
11868                c.descricao.as_deref(),
11869                "Caixa::descricao must byte-equal the raw \
11870                 `self.descricao.as_deref()` field access across every \
11871                 value in the Option<&str> accept-set",
11872            );
11873        }
11874    }
11875
11876    #[test]
11877    fn validate_descricao_empty_arm_routes_through_accessor() {
11878        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
11879        // gate must key off [`Caixa::descricao`], not the raw
11880        // `self.descricao.as_deref()` field access. Structurally: a
11881        // `Caixa { descricao: Some(""), .. }` must surface the
11882        // `DescricaoEmpty` refusal exactly, and a
11883        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
11884        // canonical one-line-prose form) must pass validate. The pair
11885        // jointly pins the accessor + validate-gate composition: any
11886        // future silent detour that had the accessor return `None` on
11887        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
11888        // silently absorb the `DescricaoEmpty` refusal at the accessor
11889        // boundary and the validate gate would accept a struct-literal
11890        // `Caixa { descricao: Some(""), .. }` — the composition pin
11891        // catches that at caixa-core build time.
11892        //
11893        // Peer of the [`Caixa::licenca`] (6d5bc28)
11894        // `validate_licenca_empty_arm_routes_through_accessor` and
11895        // [`Caixa::repositorio`] (cc7332d)
11896        // `validate_repositorio_empty_arm_routes_through_accessor`
11897        // composition pins on the sibling outer top-level [`Caixa`]
11898        // `Option<&str>` universal-axis surface — same "the validate /
11899        // shape-gate predicate must route through the substrate-
11900        // primitive typed dispatch" discipline extended onto the third
11901        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
11902        // composition surface.
11903        let c = caixa_with_descricao(Some(""));
11904        assert!(
11905            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
11906            "validate_descricao must reject descricao == Some(\"\") \
11907             with DescricaoEmpty — the accessor and the validate gate \
11908             must route through the same substrate-primitive typed \
11909             dispatch on the :descricao empty arm",
11910        );
11911        let c = caixa_with_descricao(Some("Checkout flow."));
11912        assert!(
11913            c.validate_descricao().is_ok(),
11914            "validate_descricao must accept descricao == \
11915             Some(\"Checkout flow.\") (the canonical one-line-prose \
11916             chart-description shape)",
11917        );
11918    }
11919
11920    #[test]
11921    fn descricao_projects_option_str_by_borrow() {
11922        // The by-borrow pin: [`Caixa::descricao`] returns
11923        // `Option<&str>` by borrow — the `&str` borrows the underlying
11924        // `String` storage of the `Option<String>` slot and the
11925        // accessor must not allocate a fresh `String` on every call.
11926        // Peer of the [`Caixa::licenca`] (6d5bc28) and
11927        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
11928        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
11929        // the per-`:placement`
11930        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11931        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
11932        // return axis, extended onto the third outer top-level
11933        // [`Caixa`] universal-axis `Option<&str>` shape — the
11934        // accessor's returned `&str` must borrow from `&self` (the
11935        // returned reference's lifetime is tied to `&self`), and
11936        // calling the accessor twice on the same [`Caixa`] must yield
11937        // the same `Option<&str>` verbatim (idempotent, no side
11938        // effects on `&self`).
11939        //
11940        // Pins against a future silent detour that returned an owned
11941        // `Option<String>` (which would type-check but silently
11942        // allocate on every call, breaking the zero-cost projection
11943        // every peer sibling accessor carries), or a one-arm-only
11944        // accessor that returned a saturating value on some sentinel
11945        // input (breaking the pass-through invariant the sibling
11946        // required-scalar accessors carry).
11947        for descricao in [
11948            None,
11949            Some(""),
11950            Some("Checkout flow."),
11951            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
11952        ] {
11953            let c = caixa_with_descricao(descricao);
11954            let first = c.descricao();
11955            let second = c.descricao();
11956            assert_eq!(
11957                first, second,
11958                "Caixa::descricao must be idempotent — two successive \
11959                 calls on the same &self must return the same \
11960                 Option<&str>",
11961            );
11962            assert_eq!(
11963                first, descricao,
11964                "Caixa::descricao must return :descricao verbatim by \
11965                 borrow — got {first:?}, expected {descricao:?}",
11966            );
11967        }
11968    }
11969
11970    // ── validate_edicao — universal-axis language-edition shape ──
11971
11972    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
11973        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11974        c.edicao = edicao.map(String::from);
11975        c
11976    }
11977
11978    #[test]
11979    fn validate_edicao_accepts_none() {
11980        // The omit-the-slot identity: `:edicao` is optional. The
11981        // gate is a no-op when the author didn't declare a value —
11982        // every caixa without an `:edicao` line trivially passes,
11983        // and the substrate-side build pipeline falls back to the
11984        // documented default edition. Mirrors the peer
11985        // `validate_licenca_accepts_none` posture on the sibling
11986        // `Option<String>` Caixa slot.
11987        let c = caixa_with_edicao(None);
11988        c.validate_edicao().unwrap();
11989    }
11990
11991    #[test]
11992    fn validate_edicao_accepts_canonical_value() {
11993        // Positive control: the canonical `"2026"` edition every
11994        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
11995        // `caixa-mesh`) carries by construction passes the gate.
11996        // Future-introduced sibling editions (`"2027"`, `"2030"`,
11997        // `"2049"`) that match the same 4-digit ASCII decimal year
11998        // shape must also trivially pass — the structural shape
11999        // predicate accepts every well-formed year regardless of
12000        // whether the substrate yet understands the specific value
12001        // (a future known-edition allowlist tightens that).
12002        for ed in ["2026", "2027", "2030", "2049"] {
12003            let c = caixa_with_edicao(Some(ed));
12004            c.validate_edicao()
12005                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
12006        }
12007    }
12008
12009    #[test]
12010    fn validate_edicao_rejects_empty_some() {
12011        // Canonical paste-from-blank-doc footgun. Without this gate
12012        // the empty `Some("")` silently lands as `(:edicao "")` in
12013        // the rendered caixa.lisp and a future renderer-side
12014        // consumer's `Option::unwrap_or_else` (which only fires on
12015        // `None`) skips its fallback. Mirrors the peer
12016        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
12017        // `Option<String>` Caixa slot.
12018        let c = caixa_with_edicao(Some(""));
12019        let err = c.validate_edicao().unwrap_err();
12020        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
12021    }
12022
12023    #[test]
12024    fn validate_edicao_rejects_free_form_non_year() {
12025        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
12026        // `"nightly"` shapes carry no operational meaning on the
12027        // substrate's build-time edition selector. Until this gate
12028        // landed the bare empty-arm check let every such value
12029        // through and broke far from the source caixa.lisp. Peer
12030        // with the shape-predicate cascade
12031        // `validate_repositorio_rejects_missing_colon_separator`
12032        // establishes past its own empty arm.
12033        for ed in ["x", "latest", "nightly", "stable"] {
12034            let c = caixa_with_edicao(Some(ed));
12035            let err = c.validate_edicao().unwrap_err();
12036            assert!(
12037                matches!(err, ManifestError::EdicaoInvalid { .. }),
12038                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12039            );
12040        }
12041    }
12042
12043    #[test]
12044    fn validate_edicao_rejects_trailing_whitespace() {
12045        // Paste-from-doc whitespace footgun. A trailing space in
12046        // the `:edicao` value would silently break the substrate's
12047        // build-time edition match-table lookup at the rendered
12048        // artifact's edition-selector consumer. The shape predicate
12049        // refuses every whitespace byte by construction (any byte
12050        // outside `0-9` fails `is_ascii_digit`). Peer with
12051        // `validate_repositorio_rejects_whitespace`.
12052        let c = caixa_with_edicao(Some("2026 "));
12053        let err = c.validate_edicao().unwrap_err();
12054        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
12055            panic!("expected EdicaoInvalid, got {err:?}");
12056        };
12057        assert_eq!(edicao, "2026 ");
12058    }
12059
12060    #[test]
12061    fn validate_edicao_rejects_leading_whitespace() {
12062        // Symmetric paste-from-doc whitespace footgun on the leading
12063        // boundary — the gate refuses every shape with a non-digit
12064        // byte by construction.
12065        let c = caixa_with_edicao(Some(" 2026"));
12066        let err = c.validate_edicao().unwrap_err();
12067        assert!(
12068            matches!(err, ManifestError::EdicaoInvalid { .. }),
12069            "got {err:?}",
12070        );
12071    }
12072
12073    #[test]
12074    fn validate_edicao_rejects_control_char() {
12075        // Paste-from-multiline-doc CRLF footgun — control characters
12076        // at the value boundary break the substrate's build-time
12077        // edition-selector parser. Peer with
12078        // `validate_repositorio_rejects_control_char`.
12079        let c = caixa_with_edicao(Some("2026\n"));
12080        let err = c.validate_edicao().unwrap_err();
12081        assert!(
12082            matches!(err, ManifestError::EdicaoInvalid { .. }),
12083            "got {err:?}",
12084        );
12085    }
12086
12087    #[test]
12088    fn validate_edicao_rejects_non_ascii_lookalike() {
12089        // Fullwidth-keyboard look-alike footgun — `"2026"` is
12090        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
12091        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
12092        // edition selector wants an ASCII year, and the gate
12093        // refuses every non-ASCII shape by construction (length in
12094        // bytes is 12 ≠ 4, *and* every byte falls outside
12095        // `is_ascii_digit`'s `0-9` range).
12096        let c = caixa_with_edicao(Some("2026"));
12097        let err = c.validate_edicao().unwrap_err();
12098        assert!(
12099            matches!(err, ManifestError::EdicaoInvalid { .. }),
12100            "got {err:?}",
12101        );
12102    }
12103
12104    #[test]
12105    fn validate_edicao_rejects_version_tag_prefix() {
12106        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
12107        // / `"r2026"` are familiar shapes from git-tag / Rust
12108        // edition / release-tag conventions that don't apply to
12109        // the year-shaped edition axis. The shape predicate refuses
12110        // every leading non-digit prefix.
12111        for ed in ["v2026", "e2026", "r2026"] {
12112            let c = caixa_with_edicao(Some(ed));
12113            let err = c.validate_edicao().unwrap_err();
12114            assert!(
12115                matches!(err, ManifestError::EdicaoInvalid { .. }),
12116                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12117            );
12118        }
12119    }
12120
12121    #[test]
12122    fn validate_edicao_rejects_decimal_shape() {
12123        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
12124        // `"2026.0"` are familiar shapes from semver / float
12125        // conventions that don't apply to the year-shaped edition
12126        // axis. The shape predicate refuses every non-digit byte
12127        // (`.` falls outside `is_ascii_digit`).
12128        for ed in ["2026.1", "2026.0", "2026.0.1"] {
12129            let c = caixa_with_edicao(Some(ed));
12130            let err = c.validate_edicao().unwrap_err();
12131            assert!(
12132                matches!(err, ManifestError::EdicaoInvalid { .. }),
12133                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12134            );
12135        }
12136    }
12137
12138    #[test]
12139    fn validate_edicao_rejects_wrong_length_numeric() {
12140        // Wrong-length numeric footgun — `"26"` (truncated) /
12141        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
12142        // (zero-padded too wide) all parse as integers but don't
12143        // name a 4-digit year. The shape predicate refuses every
12144        // value whose length isn't exactly 4 bytes.
12145        for ed in ["26", "202", "20260", "00026", "9"] {
12146            let c = caixa_with_edicao(Some(ed));
12147            let err = c.validate_edicao().unwrap_err();
12148            assert!(
12149                matches!(err, ManifestError::EdicaoInvalid { .. }),
12150                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12151            );
12152        }
12153    }
12154
12155    #[test]
12156    fn validate_edicao_empty_takes_precedence_over_shape() {
12157        // Empty-first cascade pin: the empty `Some("")` surfaces
12158        // the narrower `EdicaoEmpty` not the shape-predicate-
12159        // wrapped `EdicaoInvalid`, mirroring the peer
12160        // `validate_repositorio_empty_takes_precedence_over_shape`
12161        // (`RepositorioEmpty` → `RepositorioInvalid`),
12162        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
12163        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
12164        // cascades. The shape predicate also refuses the empty
12165        // input (defensively — `s.len() != 4`), but the
12166        // manifest-layer empty arm runs first to surface the
12167        // narrower diagnostic verbatim.
12168        let c = caixa_with_edicao(Some(""));
12169        let err = c.validate_edicao().unwrap_err();
12170        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
12171    }
12172
12173    #[test]
12174    fn validate_edicao_template_passes() {
12175        // Round-trip pin: the bare `Caixa::template` shape (which
12176        // carries `:edicao "2026"` verbatim) passes the gate by
12177        // construction. A future template-shape change that
12178        // introduced `(:edicao "")` or a non-year value would
12179        // surface here as a regression. Mirrors the peer
12180        // `validate_licenca_template_passes` pin.
12181        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12182        c.validate_edicao().unwrap();
12183    }
12184
12185    #[test]
12186    fn validate_edicao_diagnostic_names_offending_slot() {
12187        // Diagnostic-shape pin (peer with
12188        // `validate_licenca_diagnostic_names_offending_slot`): the
12189        // error's Display surfaces the `:edicao` slot name verbatim,
12190        // so a `feira lint` run can render the diagnostic without
12191        // re-parsing and the author can grep their caixa.lisp for
12192        // the offending `:edicao` line.
12193        let c = caixa_with_edicao(Some(""));
12194        let rendered = c.validate_edicao().unwrap_err().to_string();
12195        assert!(
12196            rendered.contains(":edicao"),
12197            "diagnostic must name the offending slot: {rendered}",
12198        );
12199    }
12200
12201    #[test]
12202    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
12203        // Diagnostic-shape pin on the shape-predicate arm (peer
12204        // with `validate_repositorio_diagnostic_carries_offending_value`):
12205        // the error's Display surfaces the offending value + slot
12206        // name verbatim, so a `feira lint` run can render the
12207        // diagnostic without re-parsing and the author can grep
12208        // their caixa.lisp for the offending `:edicao` value.
12209        let c = caixa_with_edicao(Some("v2026"));
12210        let rendered = c.validate_edicao().unwrap_err().to_string();
12211        assert!(
12212            rendered.contains(":edicao"),
12213            "diagnostic must name the offending slot: {rendered}",
12214        );
12215        assert!(
12216            rendered.contains("v2026"),
12217            "diagnostic must quote the offending value: {rendered}",
12218        );
12219    }
12220
12221    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
12222
12223    #[test]
12224    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
12225        // The canonical per-`Caixa` `:edicao` language-edition scalar
12226        // pin: [`Caixa::edicao`] must return the `:edicao` typed
12227        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12228        // raw `self.edicao.as_deref()` access across every representative
12229        // value in the accept-set — `None` (the "omit the slot to defer
12230        // to the substrate's default edition" arm every existing
12231        // [`caixa-resolver`] fixture without an `:edicao` line carries),
12232        // `Some("")` (a past-the-guard sentinel that pins the accessor
12233        // doesn't perform a silent `Some("") → None` collapse on the
12234        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
12235        // but the accessor must ship the raw slot verbatim so a
12236        // validate-time gate regression surfaces at any future edition-
12237        // aware consumer's boundary rather than being silently absorbed
12238        // into the substrate's default edition), `Some("2026")` (the
12239        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
12240        // template scaffolds via [`Caixa::template`] and every
12241        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
12242        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
12243        // carries by construction), `Some("2018")` / `Some("2021")` /
12244        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
12245        // peer with Cargo's `[package] edition` grammar every future-
12246        // introduced sibling to `"2026"` will follow), and eight
12247        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
12248        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
12249        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
12250        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
12251        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
12252        // length-numeric, `Some("latest")` free-form-non-year — the
12253        // sentinels pin the accessor doesn't silently absorb the
12254        // refusal cases into a substrate-default-edition fallback).
12255        //
12256        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
12257        // return scalar accessor pin on the substrate primitive —
12258        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
12259        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
12260        // (3f16e2f) pins that opened the "outer [`Caixa`]
12261        // `Option<&str>` scalar" projection pin pattern this pin folds
12262        // on. Sibling in shape to the peer per-`:placement`
12263        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12264        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12265        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12266        // axes, extended onto the outer top-level [`Caixa`] universal-
12267        // axis surface's last unlifted `Option<String>` slot. Pins
12268        // against a future silent detour that returned an owned
12269        // `Option<String>` (which would type-check but silently
12270        // allocate on every accessor call, breaking the zero-cost
12271        // projection every peer sibling accessor carries), a
12272        // `Some("") → None` collapse (which would silently absorb the
12273        // `EdicaoEmpty` refusal case at the accessor boundary and any
12274        // future edition-aware consumer would silently fall back to
12275        // the substrate's default edition on a struct-literal
12276        // `Caixa { edicao: Some(""), .. }`), or a
12277        // `None → Some("2026")` collapse (which would silently reify
12278        // the substrate's default edition at the accessor boundary
12279        // and every downstream consumer keying off the
12280        // `Option::is_none()` discriminator would lose the "author
12281        // omitted the slot" signal).
12282        for edicao in [
12283            None,
12284            Some(""),
12285            Some("2026"),
12286            Some("2018"),
12287            Some("2021"),
12288            Some("2024"),
12289            Some("2026 "),
12290            Some(" 2026"),
12291            Some("2026\n"),
12292            Some("2026"),
12293            Some("v2026"),
12294            Some("2026.1"),
12295            Some("26"),
12296            Some("latest"),
12297        ] {
12298            let c = caixa_with_edicao(edicao);
12299            assert_eq!(
12300                c.edicao(),
12301                edicao,
12302                "Caixa::edicao must return :edicao verbatim (got {:?}, \
12303                 expected {edicao:?})",
12304                c.edicao(),
12305            );
12306            assert_eq!(
12307                c.edicao(),
12308                c.edicao.as_deref(),
12309                "Caixa::edicao must byte-equal the raw \
12310                 `self.edicao.as_deref()` field access across every \
12311                 value in the Option<&str> accept-set",
12312            );
12313        }
12314    }
12315
12316    #[test]
12317    fn validate_edicao_empty_arm_routes_through_accessor() {
12318        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
12319        // must key off [`Caixa::edicao`], not the raw
12320        // `self.edicao.as_deref()` field access. Structurally: a
12321        // `Caixa { edicao: Some(""), .. }` must surface the
12322        // `EdicaoEmpty` refusal exactly, and a
12323        // `Caixa { edicao: Some("2026"), .. }` (the canonical
12324        // 4-digit-ASCII-decimal-year form) must pass validate. The
12325        // pair jointly pins the accessor + validate-gate composition:
12326        // any future silent detour that had the accessor return `None`
12327        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
12328        // would silently absorb the `EdicaoEmpty` refusal at the
12329        // accessor boundary and the validate gate would accept a
12330        // struct-literal `Caixa { edicao: Some(""), .. }` — the
12331        // composition pin catches that at caixa-core build time.
12332        //
12333        // Peer of the [`Caixa::licenca`] (6d5bc28)
12334        // `validate_licenca_empty_arm_routes_through_accessor`,
12335        // [`Caixa::repositorio`] (cc7332d)
12336        // `validate_repositorio_empty_arm_routes_through_accessor`,
12337        // and [`Caixa::descricao`] (3f16e2f)
12338        // `validate_descricao_empty_arm_routes_through_accessor`
12339        // composition pins on the sibling outer top-level [`Caixa`]
12340        // `Option<&str>` universal-axis surface — same "the validate /
12341        // shape-gate predicate must route through the substrate-
12342        // primitive typed dispatch" discipline extended onto the
12343        // fourth and final outer top-level [`Caixa`] universal-axis
12344        // `Option<&str>`-composition surface, closing the accessor-
12345        // composition family.
12346        let c = caixa_with_edicao(Some(""));
12347        assert!(
12348            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
12349            "validate_edicao must reject edicao == Some(\"\") with \
12350             EdicaoEmpty — the accessor and the validate gate must \
12351             route through the same substrate-primitive typed dispatch \
12352             on the :edicao empty arm",
12353        );
12354        let c = caixa_with_edicao(Some("2026"));
12355        assert!(
12356            c.validate_edicao().is_ok(),
12357            "validate_edicao must accept edicao == Some(\"2026\") \
12358             (the canonical 4-digit-ASCII-decimal-year shape)",
12359        );
12360    }
12361
12362    #[test]
12363    fn edicao_projects_option_str_by_borrow() {
12364        // The by-borrow pin: [`Caixa::edicao`] returns
12365        // `Option<&str>` by borrow — the `&str` borrows the underlying
12366        // `String` storage of the `Option<String>` slot and the
12367        // accessor must not allocate a fresh `String` on every call.
12368        // Peer of the [`Caixa::licenca`] (6d5bc28),
12369        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
12370        // (3f16e2f) by-borrow pins on the peer outer top-level
12371        // [`Caixa`] `Option<&str>`-return axes, and of the
12372        // per-`:placement`
12373        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12374        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
12375        // return axis, extended onto the fourth and final outer top-
12376        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12377        // accessor's returned `&str` must borrow from `&self` (the
12378        // returned reference's lifetime is tied to `&self`), and
12379        // calling the accessor twice on the same [`Caixa`] must yield
12380        // the same `Option<&str>` verbatim (idempotent, no side
12381        // effects on `&self`).
12382        //
12383        // Pins against a future silent detour that returned an owned
12384        // `Option<String>` (which would type-check but silently
12385        // allocate on every call, breaking the zero-cost projection
12386        // every peer sibling accessor carries), or a one-arm-only
12387        // accessor that returned a saturating value on some sentinel
12388        // input (breaking the pass-through invariant the sibling
12389        // required-scalar accessors carry).
12390        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
12391            let c = caixa_with_edicao(edicao);
12392            let first = c.edicao();
12393            let second = c.edicao();
12394            assert_eq!(
12395                first, second,
12396                "Caixa::edicao must be idempotent — two successive \
12397                 calls on the same &self must return the same \
12398                 Option<&str>",
12399            );
12400            assert_eq!(
12401                first, edicao,
12402                "Caixa::edicao must return :edicao verbatim by \
12403                 borrow — got {first:?}, expected {edicao:?}",
12404            );
12405        }
12406    }
12407
12408    #[test]
12409    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
12410        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
12411        // label caixa-identity scalar pin: [`Caixa::nome`] must return
12412        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
12413        // the raw field access across every representative value in
12414        // the accept-set — the canonical `"demo"` template baseline
12415        // (the same `feira init`-scaffolded default the sibling
12416        // `validate_nome_accepts_canonical_template` positive-control
12417        // gate pins), plus every sibling per-typed-slot atom accessor's
12418        // canonical positive-arm byte-string (`"catalog"` per
12419        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
12420        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
12421        // `caixa-helm`/`caixa-flux` cross-crate integration-test
12422        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
12423        // canonical example), plus every past-the-guard sentinel for
12424        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
12425        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
12426        // the bare DNS-1123 63-byte cap but overflows the joint
12427        // `lareira-<nome>` chart-name budget the sibling
12428        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
12429        //
12430        // The past-the-guard sentinels pin the accessor doesn't
12431        // silently absorb the refusal cases into a template-derived
12432        // fallback (a future `.nome().is_empty().then(|| "demo")`
12433        // collapse would silently absorb the `NomeEmpty` refusal at
12434        // the accessor boundary and the validate gate would accept a
12435        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
12436        // catches that at caixa-core build time).
12437        //
12438        // First outer top-level [`Caixa`] `&str`-return required-
12439        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
12440        // required-scalar" projection pattern the sibling per-`Caixa`
12441        // `:versao` future lift folds on. Sibling in shape to the peer
12442        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
12443        // required-`String`-carry accessor pin on the sibling per-
12444        // sub-struct required-axis, extended onto the outer top-level
12445        // [`Caixa`] universal-axis required-`String`-carry axis.
12446        for nome in [
12447            "demo",
12448            "catalog",
12449            "cart",
12450            "hello-rio",
12451            "checkout",
12452            "",
12453            "Bad_Name",
12454            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
12455        ] {
12456            let c = caixa_with_nome(nome);
12457            assert_eq!(
12458                c.nome(),
12459                nome,
12460                "Caixa::nome must return :nome verbatim (got {}, \
12461                 expected {nome})",
12462                c.nome(),
12463            );
12464            assert_eq!(
12465                c.nome(),
12466                c.nome.as_str(),
12467                "Caixa::nome must byte-equal the raw .nome field \
12468                 access across every value in the String accept-set",
12469            );
12470        }
12471    }
12472
12473    #[test]
12474    fn validate_nome_empty_arm_routes_through_accessor() {
12475        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
12476        // key off [`Caixa::nome`], not the raw `.nome` field access.
12477        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
12478        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
12479        // template baseline (the peer positive-arm the sibling
12480        // `validate_nome_accepts_canonical_template` gate carves out)
12481        // must pass validate. The pair jointly pins the accessor +
12482        // validate-gate composition: any future silent detour that
12483        // had the accessor return a fresh `"demo"` on the empty arm
12484        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
12485        // would silently absorb the `NomeEmpty` refusal at the
12486        // accessor boundary and the validate gate would accept a
12487        // struct-literal `Caixa { nome: "".into(), .. }` — the
12488        // composition pin catches that at caixa-core build time.
12489        //
12490        // Peer of the sibling per-`Caixa`
12491        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
12492        // / `validate_repositorio_empty_arm_routes_through_accessor`
12493        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
12494        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
12495        // (2641cbd) composition pins on the sibling outer top-level
12496        // [`Caixa`] `Option<&str>` axes — same "the validate /
12497        // shape-gate predicate must route through the substrate-
12498        // primitive typed dispatch" discipline extended onto the peer
12499        // outer top-level [`Caixa`] required-`&str` composition axis.
12500        let c = caixa_with_nome("");
12501        assert!(
12502            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
12503            "validate_nome must reject nome == \"\" with NomeEmpty — \
12504             the accessor and the validate gate must route through the \
12505             same substrate-primitive typed dispatch on the :nome \
12506             empty-arm",
12507        );
12508        let c = caixa_with_nome("demo");
12509        assert!(
12510            c.validate_nome().is_ok(),
12511            "validate_nome must accept nome == \"demo\" (the canonical \
12512             DNS-1123-label template baseline)",
12513        );
12514    }
12515
12516    #[test]
12517    fn nome_projects_str_by_borrow() {
12518        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
12519        // — the `&str` borrows the underlying `String` storage of the
12520        // required `nome` slot and the accessor must not allocate a
12521        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
12522        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
12523        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
12524        // by-borrow pins on the peer outer top-level [`Caixa`]
12525        // `Option<&str>`-return axes, extended onto the first outer
12526        // top-level [`Caixa`] required-`&str`-return axis — the
12527        // accessor's returned `&str` must borrow from `&self` (the
12528        // returned reference's lifetime is tied to `&self`), and
12529        // calling the accessor twice on the same [`Caixa`] must yield
12530        // the same `&str` verbatim (idempotent, no side effects on
12531        // `&self`).
12532        //
12533        // Pins against a future silent detour that returned an owned
12534        // `String` (which would type-check but silently allocate on
12535        // every call, breaking the zero-cost projection every peer
12536        // sibling accessor carries), an accidental
12537        // `.nome.to_lowercase()` detour that returned a fresh
12538        // allocation through an already-DNS-1123-lowercase-only
12539        // string (breaking a future `const fn` regression), or a
12540        // one-arm-only accessor that returned a canonicalized value
12541        // on some sentinel input (breaking the pass-through invariant
12542        // the sibling required-scalar accessors carry).
12543        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
12544            let c = caixa_with_nome(nome);
12545            let first = c.nome();
12546            let second = c.nome();
12547            assert_eq!(
12548                first, second,
12549                "Caixa::nome must be idempotent — two successive calls \
12550                 on the same &self must return the same &str",
12551            );
12552            assert_eq!(
12553                first, nome,
12554                "Caixa::nome must return :nome verbatim by borrow — \
12555                 got {first}, expected {nome}",
12556            );
12557        }
12558    }
12559
12560    #[test]
12561    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
12562        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
12563        // pinned-version scalar pin: [`Caixa::versao`] must return the
12564        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
12565        // raw `.versao` field access across every representative value
12566        // in the accept-set — the canonical `"0.1.0"` template baseline
12567        // (the same `feira init`-scaffolded default the sibling
12568        // `validate_versao_accepts_canonical_template` positive-control
12569        // gate pins), plus every canonical SemVer-2 shape the sibling
12570        // `validate_versao_accepts_canonical_forms` positive-arm sweep
12571        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
12572        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
12573        // `"10.20.30"`), plus every past-the-guard sentinel for the
12574        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
12575        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
12576        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
12577        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
12578        // `"latest"` the docker-tag-shape footgun — the sentinels pin
12579        // the accessor doesn't silently absorb the refusal cases into a
12580        // template-derived fallback like `"0.1.0"`).
12581        //
12582        // The past-the-guard sentinels pin the accessor doesn't silently
12583        // absorb the refusal cases into a template-derived fallback (a
12584        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
12585        // silently absorb the `VersaoEmpty` refusal at the accessor
12586        // boundary and the validate gate would accept a struct-literal
12587        // `Caixa { versao: "".into(), .. }` — the pin catches that at
12588        // caixa-core build time).
12589        //
12590        // Second outer top-level [`Caixa`] `&str`-return required-scalar
12591        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
12592        // scalar" projection pattern the sibling per-`Caixa`
12593        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
12594        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
12595        // (4127bb6) / per-`:children`
12596        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
12597        // / per-`:upgrade-from`
12598        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
12599        // struct `:versao`-shaped `&str`-return accessor pins on the
12600        // sibling per-typed-slot version-carrier axes, extended onto the
12601        // second outer top-level [`Caixa`] universal-axis required-
12602        // `String`-carry axis so the two universal-axis identity-
12603        // carrying scalars every `defcaixa` form supplies (`:nome` +
12604        // `:versao`) share the same "one typed dispatch per axis" pin
12605        // discipline.
12606        for versao in [
12607            "0.1.0",
12608            "0.0.0",
12609            "1.0.0",
12610            "0.2.0-rc.1",
12611            "1.0.0-alpha.0",
12612            "1.0.0+build.42",
12613            "1.0.0-rc.1+build.42",
12614            "10.20.30",
12615            "",
12616            "v0.1.0",
12617            "0.1",
12618            "^0.1",
12619            "0.1.0.0",
12620            "latest",
12621        ] {
12622            let c = caixa_with_versao(versao);
12623            assert_eq!(
12624                c.versao(),
12625                versao,
12626                "Caixa::versao must return :versao verbatim (got {}, \
12627                 expected {versao})",
12628                c.versao(),
12629            );
12630            assert_eq!(
12631                c.versao(),
12632                c.versao.as_str(),
12633                "Caixa::versao must byte-equal the raw .versao field \
12634                 access across every value in the String accept-set",
12635            );
12636        }
12637    }
12638
12639    #[test]
12640    fn validate_versao_empty_arm_routes_through_accessor() {
12641        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
12642        // must key off [`Caixa::versao`], not the raw `.versao` field
12643        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
12644        // surface the `VersaoEmpty` refusal exactly, and the canonical
12645        // `"0.1.0"` template baseline (the peer positive-arm the sibling
12646        // `validate_versao_accepts_canonical_template` gate carves out)
12647        // must pass validate. The pair jointly pins the accessor +
12648        // validate-gate composition: any future silent detour that had
12649        // the accessor return a fresh `"0.1.0"` on the empty arm
12650        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
12651        // would silently absorb the `VersaoEmpty` refusal at the
12652        // accessor boundary and the validate gate would accept a
12653        // struct-literal `Caixa { versao: "".into(), .. }` — the
12654        // composition pin catches that at caixa-core build time.
12655        //
12656        // Peer of the sibling per-`Caixa`
12657        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
12658        // composition pin on the sibling outer top-level [`Caixa`]
12659        // required-`&str` universal-axis surface — same "the validate /
12660        // shape-gate predicate must route through the substrate-
12661        // primitive typed dispatch" discipline extended onto the peer
12662        // outer top-level [`Caixa`] required-`&str` universal-axis
12663        // pinned-version composition axis, closing the second
12664        // coordinate of the "one canonical typed dispatch per per-Caixa
12665        // required-`&str` universal-axis" discipline.
12666        let c = caixa_with_versao("");
12667        assert!(
12668            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
12669            "validate_versao must reject versao == \"\" with VersaoEmpty — \
12670             the accessor and the validate gate must route through the \
12671             same substrate-primitive typed dispatch on the :versao \
12672             empty-arm",
12673        );
12674        let c = caixa_with_versao("0.1.0");
12675        assert!(
12676            c.validate_versao().is_ok(),
12677            "validate_versao must accept versao == \"0.1.0\" (the \
12678             canonical SemVer-2 template baseline)",
12679        );
12680    }
12681
12682    #[test]
12683    fn versao_projects_str_by_borrow() {
12684        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
12685        // — the `&str` borrows the underlying `String` storage of the
12686        // required `versao` slot and the accessor must not allocate a
12687        // fresh `String` on every call. Peer of the [`Caixa::nome`]
12688        // (e6b7d97) by-borrow pin on the sibling outer top-level
12689        // [`Caixa`] required-`&str`-return axis, extended onto the
12690        // second outer top-level [`Caixa`] required-`&str`-return
12691        // universal-axis pinned-version surface — the accessor's
12692        // returned `&str` must borrow from `&self` (the returned
12693        // reference's lifetime is tied to `&self`), and calling the
12694        // accessor twice on the same [`Caixa`] must yield the same
12695        // `&str` verbatim (idempotent, no side effects on `&self`).
12696        //
12697        // Pins against a future silent detour that returned an owned
12698        // `String` (which would type-check but silently allocate on
12699        // every call, breaking the zero-cost projection every peer
12700        // sibling accessor carries), an accidental
12701        // `semver::Version::parse(&self.versao).unwrap().to_string()`
12702        // detour that returned a canonicalized fresh allocation through
12703        // an already-canonical byte-string (breaking a future `const fn`
12704        // regression and silently absorbing the `VersaoInvalid` refusal
12705        // at the accessor boundary), or a one-arm-only accessor that
12706        // returned a canonicalized value on some sentinel input
12707        // (breaking the pass-through invariant the sibling required-
12708        // scalar accessors carry).
12709        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
12710            let c = caixa_with_versao(versao);
12711            let first = c.versao();
12712            let second = c.versao();
12713            assert_eq!(
12714                first, second,
12715                "Caixa::versao must be idempotent — two successive \
12716                 calls on the same &self must return the same &str",
12717            );
12718            assert_eq!(
12719                first, versao,
12720                "Caixa::versao must return :versao verbatim by borrow \
12721                 — got {first}, expected {versao}",
12722            );
12723        }
12724    }
12725
12726    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
12727        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12728        c.kind = kind;
12729        c
12730    }
12731
12732    #[test]
12733    fn kind_returns_kind_variant_verbatim_across_permutations() {
12734        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
12735        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
12736        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
12737        // the raw `.kind` field access across every variant in the
12738        // closed accept-set (`Biblioteca` — the library kind that
12739        // exports lisp forms; `Binario` — the nix-built executable kind
12740        // under `exe/`; `Servico` — the wasm-component daemon kind
12741        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
12742        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
12743        // composition kind).
12744        //
12745        // Pins against a future silent detour that re-derived the kind
12746        // from a peer axis (an accidental fallback to
12747        // `if !servicos.is_empty() { Servico } else if
12748        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
12749        // collapse that read the code-surface / mesh-slot columns into
12750        // the kind discriminator), a variant remap the operator
12751        // authors on one consumer without the other, or a stale-derive
12752        // detour that substituted [`CaixaKind::Biblioteca`] as the
12753        // default when the field held any other variant (which would
12754        // silently collapse the distinction between "author explicitly
12755        // declared `:kind Servico`" and "author declared any other
12756        // kind" every downstream renderer-dispatch site depends on).
12757        //
12758        // First outer top-level [`Caixa`] `Copy`-return required-enum-
12759        // discriminant accessor pin — opens the "outer [`Caixa`]
12760        // `Copy`-return required-discriminant" projection pattern.
12761        // Sibling in shape to the peer per-`:supervisor`
12762        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
12763        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
12764        // (921fe1b), and per-`:children`
12765        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
12766        // `Copy`-return closed-set-enum discriminant accessor pins on
12767        // the sibling nested-spec typed-slot discriminator axes,
12768        // extended here to the outer top-level [`Caixa`] universal-
12769        // axis surface.
12770        for kind in [
12771            CaixaKind::Biblioteca,
12772            CaixaKind::Binario,
12773            CaixaKind::Servico,
12774            CaixaKind::Supervisor,
12775            CaixaKind::Aplicacao,
12776        ] {
12777            let c = caixa_with_kind(kind);
12778            assert_eq!(
12779                c.kind(),
12780                kind,
12781                "Caixa::kind must return :kind verbatim (got {:?}, \
12782                 expected {kind:?})",
12783                c.kind(),
12784            );
12785            assert_eq!(
12786                c.kind(),
12787                c.kind,
12788                "Caixa::kind accessor and .kind field access must \
12789                 byte-equal — the accessor is the substrate-primitive \
12790                 typed dispatch every downstream kind-gate consumer \
12791                 must route through",
12792            );
12793        }
12794    }
12795
12796    #[test]
12797    fn require_kind_reads_through_lifted_kind_accessor() {
12798        // Two-consumer coherence pin: the [`crate::render::require_kind`]
12799        // entry-gate predicate (the canonical two-line
12800        // `require_kind(caixa, Servico)?` prelude every per-Servico /
12801        // per-Aplicacao renderer runs at its entry-point) and the
12802        // sibling [`crate::render::KindMismatch`] error carrier's
12803        // `actual:` field (which names the offending caixa's variant
12804        // in the diagnostic) must both key off the lifted accessor, so
12805        // any future rebrand on the typed slot's reader shape lands at
12806        // exactly one place. Pins the two-site coherence by exercising
12807        // every off-diagonal `(actual, expected)` pair across the
12808        // closed accept-set — the `KindMismatch { actual, expected }`
12809        // surfaced on the mismatch arm must byte-equal the pair the
12810        // accessor returns for each side.
12811        //
12812        // Peer of the sibling per-`:placement`
12813        // `validate_placement_reads_through_lifted_estrategia_accessor`
12814        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
12815        // `Copy`-return discriminant axis — same "the entry-gate
12816        // predicate and the error carrier's `actual:` field must route
12817        // through the substrate-primitive typed dispatch" discipline
12818        // extended onto the outer top-level [`Caixa`] universal-axis
12819        // discriminant surface.
12820        for expected in [
12821            CaixaKind::Biblioteca,
12822            CaixaKind::Binario,
12823            CaixaKind::Servico,
12824            CaixaKind::Supervisor,
12825            CaixaKind::Aplicacao,
12826        ] {
12827            for actual in [
12828                CaixaKind::Biblioteca,
12829                CaixaKind::Binario,
12830                CaixaKind::Servico,
12831                CaixaKind::Supervisor,
12832                CaixaKind::Aplicacao,
12833            ] {
12834                let c = caixa_with_kind(actual);
12835                let result = crate::render::require_kind(&c, expected);
12836                if expected == actual {
12837                    assert!(
12838                        result.is_ok(),
12839                        "require_kind must accept when actual == expected \
12840                         (actual={actual:?}, expected={expected:?})",
12841                    );
12842                } else {
12843                    let err = result.expect_err("require_kind must reject when actual != expected");
12844                    assert_eq!(
12845                        err.actual,
12846                        c.kind(),
12847                        "KindMismatch.actual must byte-equal Caixa::kind() \
12848                         — the error carrier's `actual:` field reads \
12849                         through the lifted accessor",
12850                    );
12851                    assert_eq!(
12852                        err.expected, expected,
12853                        "KindMismatch.expected must byte-equal the \
12854                         expected variant passed to require_kind",
12855                    );
12856                }
12857            }
12858        }
12859    }
12860
12861    #[test]
12862    fn aplicacao_view_kind_gate_routes_through_accessor() {
12863        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
12864        // must key off [`Caixa::kind`], not the raw `.kind` field
12865        // access. Structurally: a `Caixa { kind: X, .. }` for any
12866        // non-`Aplicacao` variant must fold to `None` on the
12867        // `aplicacao_view` composer (the "kind mismatch → no typed
12868        // view" contract every downstream Aplicacao consumer keys off
12869        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
12870        // `Some(_)`. The pair jointly pins the accessor + view-gate
12871        // composition: any future silent detour that had the accessor
12872        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
12873        // input would silently absorb the kind-mismatch case at the
12874        // accessor boundary and every per-Aplicacao renderer would
12875        // silently render a non-Aplicacao caixa's mesh slots — the
12876        // composition pin catches that at caixa-core build time.
12877        //
12878        // Peer of the sibling per-`Caixa`
12879        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
12880        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
12881        // composition pins on the sibling outer top-level [`Caixa`]
12882        // required-`&str` universal-axis surfaces — same "the
12883        // composer / validate gate must route through the substrate-
12884        // primitive typed dispatch" discipline extended onto the
12885        // outer top-level [`Caixa`] `Copy`-return required-
12886        // discriminant composition axis.
12887        for kind in [
12888            CaixaKind::Biblioteca,
12889            CaixaKind::Binario,
12890            CaixaKind::Servico,
12891            CaixaKind::Supervisor,
12892        ] {
12893            let c = caixa_with_kind(kind);
12894            assert!(
12895                c.aplicacao_view().is_none(),
12896                "aplicacao_view must return None on non-Aplicacao \
12897                 kind {kind:?} — the composer's kind-gate must route \
12898                 through Caixa::kind()",
12899            );
12900        }
12901        let c = caixa_with_kind(CaixaKind::Aplicacao);
12902        assert!(
12903            c.aplicacao_view().is_some(),
12904            "aplicacao_view must return Some on kind Aplicacao — \
12905             the composer's kind-gate must accept the matching arm \
12906             through Caixa::kind()",
12907        );
12908    }
12909
12910    #[test]
12911    fn supervisor_view_kind_gate_routes_through_accessor() {
12912        // Composition pin (mirror of the sibling
12913        // `aplicacao_view_kind_gate_routes_through_accessor` on the
12914        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
12915        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
12916        // field access. A `Caixa { kind: X, .. }` for any non-
12917        // `Supervisor` variant must fold to `None` on the
12918        // `supervisor_view` composer, and a `Caixa { kind:
12919        // Supervisor, .. }` must fold to `Some(_)`. Same peer
12920        // composition pin discipline on the second `_view` composer
12921        // axis.
12922        for kind in [
12923            CaixaKind::Biblioteca,
12924            CaixaKind::Binario,
12925            CaixaKind::Servico,
12926            CaixaKind::Aplicacao,
12927        ] {
12928            let c = caixa_with_kind(kind);
12929            assert!(
12930                c.supervisor_view().is_none(),
12931                "supervisor_view must return None on non-Supervisor \
12932                 kind {kind:?} — the composer's kind-gate must route \
12933                 through Caixa::kind()",
12934            );
12935        }
12936        let mut c = caixa_with_kind(CaixaKind::Supervisor);
12937        // A Supervisor caixa needs a strategy + at least one child to
12938        // fold to a Some(_) that also validates; the composer itself
12939        // requires only the kind arm, so bare kind flip is enough to
12940        // pin the `Some(_)` return, but we populate the minimum
12941        // supervisor shape so a future strengthening of the composer
12942        // to reject an empty spec doesn't false-positive this pin.
12943        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
12944        c.children = vec![crate::supervisor::ChildSpec {
12945            caixa: "child".into(),
12946            versao: "^0.1".into(),
12947            restart: crate::supervisor::RestartPolicy::Permanent,
12948        }];
12949        assert!(
12950            c.supervisor_view().is_some(),
12951            "supervisor_view must return Some on kind Supervisor — \
12952             the composer's kind-gate must accept the matching arm \
12953             through Caixa::kind()",
12954        );
12955    }
12956
12957    #[test]
12958    fn kind_projects_by_copy() {
12959        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
12960        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
12961        // `&self` (the returned value is owned, `Copy`-projected from
12962        // the underlying [`CaixaKind`] storage; two calls on the same
12963        // [`Caixa`] must yield byte-equal values). Peer of the peer
12964        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
12965        // `SupervisorSpec::estrategia` / per-`:children`
12966        // `ChildSpec::restart` `Copy`-return discriminant accessor
12967        // pins on the sibling nested-spec typed-slot discriminator
12968        // axes, extended onto the first outer top-level [`Caixa`]
12969        // required-`Copy`-return axis — pins against a future silent
12970        // detour that returned `&CaixaKind` (which would type-check
12971        // but silently constrain every consumer's callsite to a
12972        // borrow-shaped dispatch, breaking the zero-cost `Copy`
12973        // projection every peer sibling accessor carries).
12974        for kind in [
12975            CaixaKind::Biblioteca,
12976            CaixaKind::Binario,
12977            CaixaKind::Servico,
12978            CaixaKind::Supervisor,
12979            CaixaKind::Aplicacao,
12980        ] {
12981            let c = caixa_with_kind(kind);
12982            let first: CaixaKind = c.kind();
12983            let second: CaixaKind = c.kind();
12984            assert_eq!(
12985                first, second,
12986                "Caixa::kind must be idempotent — two successive \
12987                 calls on the same &self must return the same \
12988                 CaixaKind variant",
12989            );
12990            assert_eq!(
12991                first, kind,
12992                "Caixa::kind must return :kind verbatim by Copy — \
12993                 got {first:?}, expected {kind:?}",
12994            );
12995        }
12996    }
12997
12998    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
12999
13000    #[test]
13001    fn autores_returns_autores_slice_verbatim_across_permutations() {
13002        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
13003        // name-list slice pin: [`Caixa::autores`] must return the
13004        // `:autores` typed [`Vec<String>`] list verbatim as a
13005        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
13006        // access across every representative value in the accept-set —
13007        // `[]` (the "no maintainers declared" arm every existing
13008        // fixture without an `:autores` line carries), `[""]` (a past-
13009        // the-guard sentinel that pins the accessor doesn't perform a
13010        // silent `[""] → []` collapse on the empty-entry arm — validate
13011        // rejects `[""]` through `AutorEmpty` but the accessor must
13012        // ship the raw slot verbatim so a validate-time gate regression
13013        // surfaces at the caixa-helm emit boundary rather than being
13014        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
13015        // canonical single-maintainer form every `feira init` template
13016        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
13017        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
13018        // (the canonical RFC-5322 `<name> <email>` form the
13019        // `is_chart_maintainer_name_shape` predicate accepts), and
13020        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
13021        // sentinel — validate rejects through `AutorDuplicate` but the
13022        // accessor must ship the raw slot verbatim).
13023        //
13024        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
13025        // pin on the substrate primitive — opens the "outer [`Caixa`]
13026        // `&[T]` slice" projection pattern the sibling per-`Caixa`
13027        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
13028        // / `:servicos` / `:upgrade-from` / `:children` future lifts
13029        // fold on. Sibling in shape to the peer per-`:supervisor`
13030        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
13031        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
13032        // (a6e18d7), per-`:membros`
13033        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
13034        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
13035        // (0dcc926), and per-`:upgrade-from :instructions`
13036        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
13037        // `&[T]`-return slice accessor pins on the sibling per-M2 /
13038        // per-M3 typed-slot list axes, extended onto the outer top-
13039        // level [`Caixa`] universal-axis surface. Pins against a future
13040        // silent detour that returned an owned `Vec<String>` (which
13041        // would type-check but silently clone on every accessor call,
13042        // breaking the zero-cost projection every peer sibling slice
13043        // accessor carries), a `[""] → []` collapse (which would
13044        // silently absorb the `AutorEmpty` refusal case at the accessor
13045        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
13046        // would silently absorb the `AutorDuplicate` refusal case at
13047        // the accessor boundary and the caixa-helm `maintainers:` fold
13048        // would silently render a dedupped list on a struct-literal
13049        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
13050        for autores in [
13051            vec![],
13052            vec![""],
13053            vec!["pleme-io"],
13054            vec!["alice", "bob"],
13055            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
13056            vec!["pleme-io", "pleme-io"],
13057        ] {
13058            let c = caixa_with_autores(autores.clone());
13059            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
13060            assert_eq!(
13061                c.autores(),
13062                expected.as_slice(),
13063                "Caixa::autores must return :autores verbatim (got {:?}, \
13064                 expected {expected:?})",
13065                c.autores(),
13066            );
13067            assert_eq!(
13068                c.autores(),
13069                c.autores.as_slice(),
13070                "Caixa::autores must byte-equal the raw \
13071                 `self.autores.as_slice()` field access across every \
13072                 value in the Vec<String> accept-set",
13073            );
13074        }
13075    }
13076
13077    #[test]
13078    fn validate_autores_empty_entry_arm_routes_through_accessor() {
13079        // Composition pin: [`Caixa::validate_autores`]'s per-entry
13080        // empty-arm gate must key off [`Caixa::autores`], not the raw
13081        // `&self.autores` field-borrow walk. Structurally: a
13082        // `Caixa { autores: vec!["".into()], .. }` must surface the
13083        // `AutorEmpty` refusal exactly, and a
13084        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
13085        // canonical single-maintainer form) must pass validate. The
13086        // pair jointly pins the accessor + validate-gate composition:
13087        // any future silent detour that had the accessor return an
13088        // empty slice on the `[""]` arm (a
13089        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
13090        // would silently absorb the `AutorEmpty` refusal at the
13091        // accessor boundary and the validate gate would accept a
13092        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
13093        // the composition pin catches that at caixa-core build time.
13094        //
13095        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
13096        // accessor-composition pin
13097        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
13098        // sibling `Option<&str>`-composition axis and the
13099        // per-`:politicas :circuit-breaker`
13100        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
13101        // accessor-composition pin
13102        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
13103        // on the sibling required-`u32`-composition axis — same "the
13104        // validate / shape-gate predicate must route through the
13105        // substrate-primitive typed dispatch" discipline extended onto
13106        // the outer top-level [`Caixa`] universal-axis `&[T]`-
13107        // composition surface.
13108        let c = caixa_with_autores(vec![""]);
13109        assert!(
13110            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
13111            "validate_autores must reject autores == vec![\"\"] with \
13112             AutorEmpty — the accessor and the validate gate must \
13113             route through the same substrate-primitive typed dispatch \
13114             on the :autores per-entry empty arm",
13115        );
13116        let c = caixa_with_autores(vec!["pleme-io"]);
13117        assert!(
13118            c.validate_autores().is_ok(),
13119            "validate_autores must accept autores == vec![\"pleme-io\"] \
13120             (the canonical single-maintainer shape every `feira init` \
13121             template scaffolds)",
13122        );
13123    }
13124
13125    #[test]
13126    fn autores_projects_slice_by_borrow() {
13127        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
13128        // borrow — the returned slice borrows the underlying
13129        // `Vec<String>` storage of the `:autores` slot and the
13130        // accessor must not clone the backing `Vec` on every call.
13131        // Peer of the per-`:membros`
13132        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
13133        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
13134        // (0dcc926) / per-`:placement`
13135        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
13136        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
13137        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
13138        // typed-slot `&[T]`-return axes, extended onto the outer top-
13139        // level [`Caixa`] universal-axis `&[String]` shape — the
13140        // accessor's returned slice must borrow from `&self` (the
13141        // returned reference's lifetime is tied to `&self`), and
13142        // calling the accessor twice on the same [`Caixa`] must yield
13143        // slices that are pointer-equal (the underlying byte-buffer is
13144        // the storage `Vec`'s allocation, not a fresh copy) as well as
13145        // value-equal (idempotent, no side effects on `&self`).
13146        //
13147        // Pins against a future silent detour that returned an owned
13148        // `Vec<String>` (which would type-check but silently clone on
13149        // every call, breaking the zero-cost projection every peer
13150        // sibling slice accessor carries), a `&Vec<String>` return
13151        // (which would leak the backing `Vec`'s grow/push/reserve
13152        // surface no downstream consumer reaches for), or a one-arm-
13153        // only accessor that returned a saturating value on some
13154        // sentinel input (breaking the pass-through invariant the
13155        // sibling slice accessors carry).
13156        for autores in [
13157            vec![],
13158            vec!["pleme-io"],
13159            vec!["alice", "bob"],
13160            vec!["pleme-io", "pleme-io"],
13161        ] {
13162            let c = caixa_with_autores(autores.clone());
13163            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
13164            let first = c.autores();
13165            let second = c.autores();
13166            assert_eq!(
13167                first, second,
13168                "Caixa::autores must be idempotent — two successive \
13169                 calls on the same &self must return the same \
13170                 &[String]",
13171            );
13172            assert_eq!(
13173                first.as_ptr(),
13174                second.as_ptr(),
13175                "Caixa::autores must borrow the underlying Vec<String> \
13176                 storage — two successive calls must return slices \
13177                 with the same backing pointer (a fresh Vec<String> \
13178                 clone would change the pointer on every call)",
13179            );
13180            assert_eq!(
13181                first,
13182                expected.as_slice(),
13183                "Caixa::autores must return :autores verbatim by \
13184                 borrow — got {first:?}, expected {expected:?}",
13185            );
13186        }
13187    }
13188
13189    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
13190
13191    #[test]
13192    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
13193        // The canonical per-`Caixa` `:etiquetas` universal-axis
13194        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
13195        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
13196        // as a `&[String]`, byte-equal to the raw
13197        // `self.etiquetas.as_slice()` access across every representative
13198        // value in the accept-set — `[]` (the "no tags declared" arm
13199        // every existing fixture without an `:etiquetas` line carries),
13200        // `[""]` (a past-the-guard sentinel that pins the accessor
13201        // doesn't perform a silent `[""] → []` collapse on the empty-
13202        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
13203        // but the accessor must ship the raw slot verbatim so a
13204        // validate-time gate regression surfaces at the caixa-helm emit
13205        // boundary rather than being silently absorbed into a keyword-
13206        // drop), `["demo"]` (the canonical single-tag form every
13207        // `feira init` template scaffolds), `["example", "aplicacao",
13208        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
13209        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
13210        // (a past-the-guard duplicate sentinel — validate rejects
13211        // through `EtiquetaDuplicate` but the accessor must ship the
13212        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
13213        // at chart-render time isn't silently promoted into the
13214        // accessor boundary and struct-literal
13215        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
13216        // fixtures continue to expose the duplicate at the accessor).
13217        //
13218        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
13219        // pin on the substrate primitive — folds on the "outer
13220        // [`Caixa`] `&[T]` slice" projection pattern
13221        // `autores_returns_autores_slice_verbatim_across_permutations`
13222        // (b5d813f) opened, sibling in shape and idiom. Pins against a
13223        // future silent detour that returned an owned `Vec<String>`
13224        // (which would type-check but silently clone on every accessor
13225        // call, breaking the zero-cost projection every peer sibling
13226        // slice accessor carries), a `[""] → []` collapse (which would
13227        // silently absorb the `EtiquetaEmpty` refusal case at the
13228        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
13229        // (which would silently absorb the `EtiquetaDuplicate` refusal
13230        // case at the accessor boundary — the caixa-helm chart-render
13231        // `BTreeSet::collect` dedup is downstream of the accessor and
13232        // must not be silently promoted into it).
13233        for etiquetas in [
13234            vec![],
13235            vec![""],
13236            vec!["demo"],
13237            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
13238            vec!["demo", "demo"],
13239        ] {
13240            let c = caixa_with_etiquetas(etiquetas.clone());
13241            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
13242            assert_eq!(
13243                c.etiquetas(),
13244                expected.as_slice(),
13245                "Caixa::etiquetas must return :etiquetas verbatim (got \
13246                 {:?}, expected {expected:?})",
13247                c.etiquetas(),
13248            );
13249            assert_eq!(
13250                c.etiquetas(),
13251                c.etiquetas.as_slice(),
13252                "Caixa::etiquetas must byte-equal the raw \
13253                 `self.etiquetas.as_slice()` field access across every \
13254                 value in the Vec<String> accept-set",
13255            );
13256        }
13257    }
13258
13259    #[test]
13260    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
13261        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
13262        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
13263        // `&self.etiquetas` field-borrow walk. Structurally: a
13264        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
13265        // `EtiquetaEmpty` refusal exactly, and a
13266        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
13267        // single-tag form) must pass validate. The pair jointly pins
13268        // the accessor + validate-gate composition: any future silent
13269        // detour that had the accessor return an empty slice on the
13270        // `[""]` arm (a
13271        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
13272        // silently absorb the `EtiquetaEmpty` refusal at the accessor
13273        // boundary and the validate gate would accept a struct-literal
13274        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
13275        // pin catches that at caixa-core build time.
13276        //
13277        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
13278        // through_accessor` (b5d813f) accessor-composition pin on the
13279        // sibling `&[T]`-composition axis — same "the validate / shape-
13280        // gate predicate must route through the substrate-primitive
13281        // typed dispatch" discipline extended onto the sibling outer
13282        // top-level [`Caixa`] `&[T]`-composition surface.
13283        let c = caixa_with_etiquetas(vec![""]);
13284        assert!(
13285            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
13286            "validate_etiquetas must reject etiquetas == vec![\"\"] \
13287             with EtiquetaEmpty — the accessor and the validate gate \
13288             must route through the same substrate-primitive typed \
13289             dispatch on the :etiquetas per-entry empty arm",
13290        );
13291        let c = caixa_with_etiquetas(vec!["demo"]);
13292        assert!(
13293            c.validate_etiquetas().is_ok(),
13294            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
13295             (the canonical single-tag shape every `feira init` \
13296             template scaffolds)",
13297        );
13298    }
13299
13300    #[test]
13301    fn etiquetas_projects_slice_by_borrow() {
13302        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
13303        // by borrow — the returned slice borrows the underlying
13304        // `Vec<String>` storage of the `:etiquetas` slot and the
13305        // accessor must not clone the backing `Vec` on every call.
13306        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13307        // (b5d813f) by-borrow pin on the sibling outer top-level
13308        // [`Caixa`] `&[String]`-return axis — the accessor's returned
13309        // slice must borrow from `&self` (the returned reference's
13310        // lifetime is tied to `&self`), and calling the accessor twice
13311        // on the same [`Caixa`] must yield slices that are pointer-
13312        // equal (the underlying byte-buffer is the storage `Vec`'s
13313        // allocation, not a fresh copy) as well as value-equal
13314        // (idempotent, no side effects on `&self`).
13315        //
13316        // Pins against a future silent detour that returned an owned
13317        // `Vec<String>` (which would type-check but silently clone on
13318        // every call, breaking the zero-cost projection every peer
13319        // sibling slice accessor carries), a `&Vec<String>` return
13320        // (which would leak the backing `Vec`'s grow/push/reserve
13321        // surface no downstream consumer reaches for), or a one-arm-
13322        // only accessor that returned a saturating value on some
13323        // sentinel input (breaking the pass-through invariant the
13324        // sibling slice accessors carry).
13325        for etiquetas in [
13326            vec![],
13327            vec!["demo"],
13328            vec!["example", "aplicacao", "mesh"],
13329            vec!["demo", "demo"],
13330        ] {
13331            let c = caixa_with_etiquetas(etiquetas.clone());
13332            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
13333            let first = c.etiquetas();
13334            let second = c.etiquetas();
13335            assert_eq!(
13336                first, second,
13337                "Caixa::etiquetas must be idempotent — two successive \
13338                 calls on the same &self must return the same \
13339                 &[String]",
13340            );
13341            assert_eq!(
13342                first.as_ptr(),
13343                second.as_ptr(),
13344                "Caixa::etiquetas must borrow the underlying \
13345                 Vec<String> storage — two successive calls must \
13346                 return slices with the same backing pointer (a fresh \
13347                 Vec<String> clone would change the pointer on every \
13348                 call)",
13349            );
13350            assert_eq!(
13351                first,
13352                expected.as_slice(),
13353                "Caixa::etiquetas must return :etiquetas verbatim by \
13354                 borrow — got {first:?}, expected {expected:?}",
13355            );
13356        }
13357    }
13358
13359    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
13360
13361    #[test]
13362    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
13363        // The canonical per-`Caixa` `:bibliotecas` universal-axis
13364        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
13365        // must return the `:bibliotecas` typed [`Vec<String>`] list
13366        // verbatim as a `&[String]`, byte-equal to the raw
13367        // `self.bibliotecas.as_slice()` access across every
13368        // representative value in the accept-set — `[]` (the "no
13369        // libraries declared" arm every `:kind` other than `Biblioteca`
13370        // + every `Biblioteca` relying on the canonical
13371        // `lib/<nome>.lisp` implicit-default path carries; the
13372        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
13373        // fires exactly on this empty-slot + `Biblioteca`-kind
13374        // combination), `[""]` (a past-the-guard sentinel that pins
13375        // the accessor doesn't perform a silent `[""] → []` collapse
13376        // on the empty-entry arm — validate rejects `[""]` through
13377        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
13378        // must ship the raw slot verbatim so a validate-time gate
13379        // regression surfaces at the `feira build` phase-1 parse
13380        // boundary rather than being silently absorbed into a
13381        // library-drop), `["lib/demo.lisp"]` (the canonical single-
13382        // entry form `Caixa::template` scaffolds and every `feira init`
13383        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
13384        // (the canonical multi-library form the
13385        // `validate_code_paths_accepts_explicit_relative_paths_on_
13386        // every_slot` fixture emits), and `["lib/foo.lisp",
13387        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
13388        // validate rejects through `CodePathDuplicate { slot:
13389        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
13390        // but the accessor must ship the raw slot verbatim so the
13391        // `feira build` `for entry in caixa.bibliotecas()` parse walk
13392        // sees the duplicate at the accessor boundary and struct-
13393        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
13394        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
13395        // the duplicate at the accessor).
13396        //
13397        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
13398        // pin on the substrate primitive — folds on the "outer
13399        // [`Caixa`] `&[T]` slice" projection pattern
13400        // `autores_returns_autores_slice_verbatim_across_permutations`
13401        // (b5d813f) opened and
13402        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13403        // (78c7d3c) folded on, sibling in shape and idiom. Pins
13404        // against a future silent detour that returned an owned
13405        // `Vec<String>` (which would type-check but silently clone on
13406        // every accessor call, breaking the zero-cost projection
13407        // every peer sibling slice accessor carries), a `[""] → []`
13408        // collapse (which would silently absorb the `CodePathEmpty`
13409        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
13410        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
13411        // would silently absorb the `CodePathDuplicate` refusal case
13412        // at the accessor boundary — the per-slot set-not-multiset
13413        // gate is downstream of the accessor and must not be silently
13414        // promoted into it).
13415        for bibliotecas in [
13416            vec![],
13417            vec![""],
13418            vec!["lib/demo.lisp"],
13419            vec!["lib/demo.lisp", "lib/helpers.lisp"],
13420            vec!["lib/foo.lisp", "lib/foo.lisp"],
13421        ] {
13422            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
13423            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
13424            assert_eq!(
13425                c.bibliotecas(),
13426                expected.as_slice(),
13427                "Caixa::bibliotecas must return :bibliotecas verbatim \
13428                 (got {:?}, expected {expected:?})",
13429                c.bibliotecas(),
13430            );
13431            assert_eq!(
13432                c.bibliotecas(),
13433                c.bibliotecas.as_slice(),
13434                "Caixa::bibliotecas must byte-equal the raw \
13435                 `self.bibliotecas.as_slice()` field access across \
13436                 every value in the Vec<String> accept-set",
13437            );
13438        }
13439    }
13440
13441    #[test]
13442    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
13443        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13444        // empty-arm gate on the `:bibliotecas` slot must key off
13445        // [`Caixa::bibliotecas`], not a divergent raw
13446        // `&self.bibliotecas` field-borrow walk. Structurally: a
13447        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
13448        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
13449        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
13450        // into()], .. }` (the canonical single-library form
13451        // `Caixa::template` scaffolds) must pass validate. The pair
13452        // jointly pins the accessor + validate-gate composition: any
13453        // future silent detour that had the accessor return an empty
13454        // slice on the `[""]` arm (a `.iter().filter(|s|
13455        // !s.is_empty()).collect()` collapse) would silently absorb
13456        // the `CodePathEmpty` refusal at the accessor boundary and
13457        // the validate gate would accept a struct-literal
13458        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
13459        // composition pin catches that at caixa-core build time.
13460        //
13461        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
13462        // through_accessor` (b5d813f) and
13463        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13464        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13465        // composition axes — same "the validate / shape-gate
13466        // predicate must route through the substrate-primitive typed
13467        // dispatch" discipline extended onto the sibling outer top-
13468        // level [`Caixa`] `&[T]`-composition surface. Nominally the
13469        // in-tree `validate_code_paths` production body still keys
13470        // off the internal `[(":bibliotecas", &self.bibliotecas,
13471        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13472        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13473        // (the tuple's homogeneous slice-typed shape blocks a per-
13474        // element accessor swap in isolation — a future companion
13475        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
13476        // `&[T]` slice-accessor axis closes that tuple onto the
13477        // triple of typed dispatches as a unit); the composition pin
13478        // catches any future accessor-side silent filter drop against
13479        // that eventual tuple-closure regardless of whether the
13480        // `:bibliotecas` slot is threaded through the accessor or the
13481        // raw field access at the tuple's construction site.
13482        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
13483        assert!(
13484            matches!(
13485                c.validate_code_paths(),
13486                Err(ManifestError::CodePathEmpty {
13487                    slot: ":bibliotecas"
13488                })
13489            ),
13490            "validate_code_paths must reject bibliotecas == vec![\"\"] \
13491             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
13492             accessor and the validate gate must route through the \
13493             same substrate-primitive typed dispatch on the \
13494             :bibliotecas per-entry empty arm",
13495        );
13496        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
13497        assert!(
13498            c.validate_code_paths().is_ok(),
13499            "validate_code_paths must accept bibliotecas == \
13500             vec![\"lib/demo.lisp\"] (the canonical single-library \
13501             shape every `feira init` template scaffolds)",
13502        );
13503    }
13504
13505    #[test]
13506    fn bibliotecas_projects_slice_by_borrow() {
13507        // The by-borrow pin: [`Caixa::bibliotecas`] returns
13508        // `&[String]` by borrow — the returned slice borrows the
13509        // underlying `Vec<String>` storage of the `:bibliotecas` slot
13510        // and the accessor must not clone the backing `Vec` on every
13511        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13512        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
13513        // by-borrow pins on the sibling outer top-level [`Caixa`]
13514        // `&[String]`-return axes — the accessor's returned slice
13515        // must borrow from `&self` (the returned reference's lifetime
13516        // is tied to `&self`), and calling the accessor twice on the
13517        // same [`Caixa`] must yield slices that are pointer-equal
13518        // (the underlying byte-buffer is the storage `Vec`'s
13519        // allocation, not a fresh copy) as well as value-equal
13520        // (idempotent, no side effects on `&self`).
13521        //
13522        // Pins against a future silent detour that returned an owned
13523        // `Vec<String>` (which would type-check but silently clone on
13524        // every call, breaking the zero-cost projection every peer
13525        // sibling slice accessor carries), a `&Vec<String>` return
13526        // (which would leak the backing `Vec`'s grow/push/reserve
13527        // surface no downstream consumer reaches for), or a one-arm-
13528        // only accessor that returned a saturating value on some
13529        // sentinel input (breaking the pass-through invariant the
13530        // sibling slice accessors carry).
13531        for bibliotecas in [
13532            vec![],
13533            vec!["lib/demo.lisp"],
13534            vec!["lib/demo.lisp", "lib/helpers.lisp"],
13535            vec!["lib/foo.lisp", "lib/foo.lisp"],
13536        ] {
13537            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
13538            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
13539            let first = c.bibliotecas();
13540            let second = c.bibliotecas();
13541            assert_eq!(
13542                first, second,
13543                "Caixa::bibliotecas must be idempotent — two \
13544                 successive calls on the same &self must return the \
13545                 same &[String]",
13546            );
13547            assert_eq!(
13548                first.as_ptr(),
13549                second.as_ptr(),
13550                "Caixa::bibliotecas must borrow the underlying \
13551                 Vec<String> storage — two successive calls must \
13552                 return slices with the same backing pointer (a \
13553                 fresh Vec<String> clone would change the pointer on \
13554                 every call)",
13555            );
13556            assert_eq!(
13557                first,
13558                expected.as_slice(),
13559                "Caixa::bibliotecas must return :bibliotecas verbatim \
13560                 by borrow — got {first:?}, expected {expected:?}",
13561            );
13562        }
13563    }
13564
13565    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
13566
13567    #[test]
13568    fn exe_returns_exe_slice_verbatim_across_permutations() {
13569        // The canonical per-`Caixa` `:exe` universal-axis
13570        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
13571        // must return the `:exe` typed [`Vec<String>`] list verbatim as
13572        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
13573        // access across every representative value in the accept-set —
13574        // `[]` (the "no executable declared" arm every `:kind` other
13575        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
13576        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
13577        // + `Binario`-kind combination), `[""]` (a past-the-guard
13578        // sentinel that pins the accessor doesn't perform a silent
13579        // `[""] → []` collapse on the empty-entry arm — validate rejects
13580        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
13581        // accessor must ship the raw slot verbatim so a validate-time
13582        // gate regression surfaces at the layout / `feira nix` boundary
13583        // rather than being silently absorbed into an executable-drop),
13584        // `["exe/cli"]` (the canonical single-entry Binario form every
13585        // in-tree `caixa_with_code_paths` positive control uses),
13586        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
13587        // form the `validate_code_paths_accepts_explicit_relative_paths_
13588        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
13589        // (a past-the-guard duplicate sentinel — validate rejects
13590        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
13591        // set-not-multiset gate, but the accessor must ship the raw
13592        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
13593        // into(), "exe/cli".into()], .. }` fixtures continue to expose
13594        // the duplicate at the accessor).
13595        //
13596        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
13597        // pin on the substrate primitive — folds on the "outer
13598        // [`Caixa`] `&[T]` slice" projection pattern
13599        // `autores_returns_autores_slice_verbatim_across_permutations`
13600        // (b5d813f) opened,
13601        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13602        // (78c7d3c) folded on, and
13603        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13604        // (8a36c23) closed the universal-axis text-tag family of.
13605        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
13606        // the sibling `:servicos` future lift closes onto. Pins against
13607        // a future silent detour that returned an owned `Vec<String>`
13608        // (which would type-check but silently clone on every accessor
13609        // call, breaking the zero-cost projection every peer sibling
13610        // slice accessor carries), a `[""] → []` collapse (which would
13611        // silently absorb the `CodePathEmpty` refusal case at the
13612        // accessor boundary), or an `["exe/cli", "exe/cli"] →
13613        // ["exe/cli"]` dedup collapse (which would silently absorb the
13614        // `CodePathDuplicate` refusal case at the accessor boundary —
13615        // the per-slot set-not-multiset gate is downstream of the
13616        // accessor and must not be silently promoted into it).
13617        for exe in [
13618            vec![],
13619            vec![""],
13620            vec!["exe/cli"],
13621            vec!["exe/cli", "exe/serve"],
13622            vec!["exe/cli", "exe/cli"],
13623        ] {
13624            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
13625            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
13626            assert_eq!(
13627                c.exe(),
13628                expected.as_slice(),
13629                "Caixa::exe must return :exe verbatim (got {:?}, \
13630                 expected {expected:?})",
13631                c.exe(),
13632            );
13633            assert_eq!(
13634                c.exe(),
13635                c.exe.as_slice(),
13636                "Caixa::exe must byte-equal the raw \
13637                 `self.exe.as_slice()` field access across every value \
13638                 in the Vec<String> accept-set",
13639            );
13640        }
13641    }
13642
13643    #[test]
13644    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
13645        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13646        // empty-arm gate on the `:exe` slot must key off
13647        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
13648        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
13649        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
13650        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
13651        // (the canonical single-executable form every in-tree
13652        // `caixa_with_code_paths` positive control uses) must pass
13653        // validate. The pair jointly pins the accessor + validate-gate
13654        // composition: any future silent detour that had the accessor
13655        // return an empty slice on the `[""]` arm (a
13656        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
13657        // silently absorb the `CodePathEmpty` refusal at the accessor
13658        // boundary and the validate gate would accept a struct-literal
13659        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
13660        // catches that at caixa-core build time.
13661        //
13662        // Peer of the per-`Caixa`
13663        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13664        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
13665        // (b5d813f), and
13666        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13667        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13668        // composition axes — same "the validate / shape-gate predicate
13669        // must route through the substrate-primitive typed dispatch"
13670        // discipline extended onto the sibling outer top-level [`Caixa`]
13671        // `&[T]`-composition surface. Nominally the in-tree
13672        // `validate_code_paths` production body still keys off the
13673        // internal `[(":bibliotecas", &self.bibliotecas,
13674        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13675        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13676        // (the tuple's homogeneous slice-typed shape blocks a per-
13677        // element accessor swap in isolation — a future companion lift
13678        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
13679        // accessor axis closes that tuple onto the triple of typed
13680        // dispatches as a unit); the composition pin catches any future
13681        // accessor-side silent filter drop against that eventual tuple-
13682        // closure regardless of whether the `:exe` slot is threaded
13683        // through the accessor or the raw field access at the tuple's
13684        // construction site.
13685        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
13686        assert!(
13687            matches!(
13688                c.validate_code_paths(),
13689                Err(ManifestError::CodePathEmpty { slot: ":exe" })
13690            ),
13691            "validate_code_paths must reject exe == vec![\"\"] \
13692             with CodePathEmpty {{ slot: \":exe\" }} — the \
13693             accessor and the validate gate must route through the \
13694             same substrate-primitive typed dispatch on the \
13695             :exe per-entry empty arm",
13696        );
13697        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
13698        assert!(
13699            c.validate_code_paths().is_ok(),
13700            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
13701             (the canonical single-executable shape every in-tree \
13702             `caixa_with_code_paths` positive control uses)",
13703        );
13704    }
13705
13706    #[test]
13707    fn exe_projects_slice_by_borrow() {
13708        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
13709        // borrow — the returned slice borrows the underlying
13710        // `Vec<String>` storage of the `:exe` slot and the accessor
13711        // must not clone the backing `Vec` on every call. Peer of the
13712        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
13713        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
13714        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
13715        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
13716        // return axes — the accessor's returned slice must borrow from
13717        // `&self` (the returned reference's lifetime is tied to
13718        // `&self`), and calling the accessor twice on the same
13719        // [`Caixa`] must yield slices that are pointer-equal (the
13720        // underlying byte-buffer is the storage `Vec`'s allocation,
13721        // not a fresh copy) as well as value-equal (idempotent, no
13722        // side effects on `&self`).
13723        //
13724        // Pins against a future silent detour that returned an owned
13725        // `Vec<String>` (which would type-check but silently clone on
13726        // every call, breaking the zero-cost projection every peer
13727        // sibling slice accessor carries), a `&Vec<String>` return
13728        // (which would leak the backing `Vec`'s grow/push/reserve
13729        // surface no downstream consumer reaches for), or a one-arm-
13730        // only accessor that returned a saturating value on some
13731        // sentinel input (breaking the pass-through invariant the
13732        // sibling slice accessors carry).
13733        for exe in [
13734            vec![],
13735            vec!["exe/cli"],
13736            vec!["exe/cli", "exe/serve"],
13737            vec!["exe/cli", "exe/cli"],
13738        ] {
13739            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
13740            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
13741            let first = c.exe();
13742            let second = c.exe();
13743            assert_eq!(
13744                first, second,
13745                "Caixa::exe must be idempotent — two successive calls \
13746                 on the same &self must return the same &[String]",
13747            );
13748            assert_eq!(
13749                first.as_ptr(),
13750                second.as_ptr(),
13751                "Caixa::exe must borrow the underlying Vec<String> \
13752                 storage — two successive calls must return slices \
13753                 with the same backing pointer (a fresh Vec<String> \
13754                 clone would change the pointer on every call)",
13755            );
13756            assert_eq!(
13757                first,
13758                expected.as_slice(),
13759                "Caixa::exe must return :exe verbatim by borrow — \
13760                 got {first:?}, expected {expected:?}",
13761            );
13762        }
13763    }
13764
13765    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
13766
13767    #[test]
13768    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
13769        // The canonical per-`Caixa` `:servicos` universal-axis
13770        // ComputeUnit-CR-YAML-entry-path-list slice pin:
13771        // [`Caixa::servicos`] must return the `:servicos` typed
13772        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
13773        // the raw `self.servicos.as_slice()` access across every
13774        // representative value in the accept-set — `[]` (the "no
13775        // ComputeUnit-CR declared" arm every `:kind` other than
13776        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
13777        // `ServicoWithoutServicos` arm-gate fires exactly on this
13778        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
13779        // guard sentinel that pins the accessor doesn't perform a
13780        // silent `[""] → []` collapse on the empty-entry arm — validate
13781        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
13782        // but the accessor must ship the raw slot verbatim so a
13783        // validate-time gate regression surfaces at the layout /
13784        // per-Servico renderer boundary rather than being silently
13785        // absorbed into a component-drop),
13786        // `["servicos/demo.computeunit.yaml"]` (the canonical
13787        // singleton V0-shape every in-tree `caixa_with_code_paths`
13788        // positive control uses; the same shape
13789        // [`crate::require_single_servico`] admits),
13790        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
13791        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
13792        // singularity gate rejects through `ServicoCountMismatch
13793        // { count: 2 }` but the accessor must ship the raw slot
13794        // verbatim so struct-literal `Caixa { servicos: vec![...,
13795        // ...], .. }` fixtures continue to expose the count at the
13796        // accessor), and `["servicos/a.computeunit.yaml",
13797        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
13798        // sentinel — validate rejects through
13799        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
13800        // set-not-multiset gate, but the accessor must ship the raw
13801        // slot verbatim so struct-literal fixtures continue to expose
13802        // the duplicate at the accessor).
13803        //
13804        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
13805        // slice accessor pin on the substrate primitive — folds on the
13806        // "outer [`Caixa`] `&[T]` slice" projection pattern
13807        // `autores_returns_autores_slice_verbatim_across_permutations`
13808        // (b5d813f) opened,
13809        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13810        // (78c7d3c) folded on,
13811        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
13812        // (8a36c23) closed the universal-axis text-tag family of, and
13813        // `exe_returns_exe_slice_verbatim_across_permutations`
13814        // (65d9527) opened the foreign-code-slot sub-family of. Closes
13815        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
13816        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
13817        // `:servicos`) now each carries a substrate-canonical slice
13818        // accessor. Pins against a future silent detour that returned
13819        // an owned `Vec<String>` (which would type-check but silently
13820        // clone on every accessor call, breaking the zero-cost
13821        // projection every peer sibling slice accessor carries), a
13822        // `[""] → []` collapse (which would silently absorb the
13823        // `CodePathEmpty` refusal case at the accessor boundary), an
13824        // `[a, a] → [a]` dedup collapse (which would silently absorb
13825        // the `CodePathDuplicate` refusal case at the accessor
13826        // boundary — the per-slot set-not-multiset gate is downstream
13827        // of the accessor and must not be silently promoted into it),
13828        // or a `[a, b] → [a]` singleton collapse (which would silently
13829        // absorb the V0 `ServicoCountMismatch` refusal case at the
13830        // accessor boundary — the V0 singularity gate is downstream of
13831        // the accessor and must not be silently promoted into it).
13832        for servicos in [
13833            vec![],
13834            vec![""],
13835            vec!["servicos/demo.computeunit.yaml"],
13836            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
13837            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
13838        ] {
13839            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
13840            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
13841            assert_eq!(
13842                c.servicos(),
13843                expected.as_slice(),
13844                "Caixa::servicos must return :servicos verbatim (got \
13845                 {:?}, expected {expected:?})",
13846                c.servicos(),
13847            );
13848            assert_eq!(
13849                c.servicos(),
13850                c.servicos.as_slice(),
13851                "Caixa::servicos must byte-equal the raw \
13852                 `self.servicos.as_slice()` field access across every \
13853                 value in the Vec<String> accept-set",
13854            );
13855        }
13856    }
13857
13858    #[test]
13859    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
13860        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13861        // empty-arm gate on the `:servicos` slot must key off
13862        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
13863        // field-borrow walk. Structurally: a `Caixa { servicos:
13864        // vec!["".into()], .. }` must surface the `CodePathEmpty
13865        // { slot: ":servicos" }` refusal exactly, and a `Caixa
13866        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
13867        // .. }` (the canonical singleton V0-shape every in-tree
13868        // `caixa_with_code_paths` positive control uses) must pass
13869        // validate. The pair jointly pins the accessor + validate-gate
13870        // composition: any future silent detour that had the accessor
13871        // return an empty slice on the `[""]` arm (a `.iter().filter
13872        // (|s| !s.is_empty()).collect()` collapse) would silently
13873        // absorb the `CodePathEmpty` refusal at the accessor boundary
13874        // and the validate gate would accept a struct-literal
13875        // `Caixa { servicos: vec!["".into()], .. }` — the composition
13876        // pin catches that at caixa-core build time.
13877        //
13878        // Peer of the per-`Caixa`
13879        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
13880        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
13881        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
13882        // (b5d813f), and
13883        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13884        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13885        // composition axes — same "the validate / shape-gate predicate
13886        // must route through the substrate-primitive typed dispatch"
13887        // discipline extended onto the sibling outer top-level
13888        // [`Caixa`] `&[T]`-composition surface, closing the trio of
13889        // code-surface accessor-composition pins on the same axis.
13890        // Nominally the in-tree `validate_code_paths` production body
13891        // still keys off the internal
13892        // `[(":bibliotecas", &self.bibliotecas,
13893        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13894        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13895        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
13896        // per-element accessor swap in isolation — a future companion
13897        // lift promotes the tuple's element type to `&[String]` and
13898        // threads the triple of typed dispatches through as a unit);
13899        // the composition pin catches any future accessor-side silent
13900        // filter drop against that eventual tuple-closure regardless
13901        // of whether the `:servicos` slot is threaded through the
13902        // accessor or the raw field access at the tuple's construction
13903        // site.
13904        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
13905        assert!(
13906            matches!(
13907                c.validate_code_paths(),
13908                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
13909            ),
13910            "validate_code_paths must reject servicos == vec![\"\"] \
13911             with CodePathEmpty {{ slot: \":servicos\" }} — the \
13912             accessor and the validate gate must route through the \
13913             same substrate-primitive typed dispatch on the \
13914             :servicos per-entry empty arm",
13915        );
13916        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
13917        assert!(
13918            c.validate_code_paths().is_ok(),
13919            "validate_code_paths must accept servicos == \
13920             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
13921             singleton V0-shape every in-tree `caixa_with_code_paths` \
13922             positive control uses)",
13923        );
13924    }
13925
13926    #[test]
13927    fn servicos_projects_slice_by_borrow() {
13928        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
13929        // borrow — the returned slice borrows the underlying
13930        // `Vec<String>` storage of the `:servicos` slot and the
13931        // accessor must not clone the backing `Vec` on every call.
13932        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13933        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
13934        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
13935        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
13936        // the sibling outer top-level [`Caixa`] `&[String]`-return
13937        // axes — the accessor's returned slice must borrow from
13938        // `&self` (the returned reference's lifetime is tied to
13939        // `&self`), and calling the accessor twice on the same
13940        // [`Caixa`] must yield slices that are pointer-equal (the
13941        // underlying byte-buffer is the storage `Vec`'s allocation,
13942        // not a fresh copy) as well as value-equal (idempotent, no
13943        // side effects on `&self`).
13944        //
13945        // Pins against a future silent detour that returned an owned
13946        // `Vec<String>` (which would type-check but silently clone on
13947        // every call, breaking the zero-cost projection every peer
13948        // sibling slice accessor carries), a `&Vec<String>` return
13949        // (which would leak the backing `Vec`'s grow/push/reserve
13950        // surface no downstream consumer reaches for), or a one-arm-
13951        // only accessor that returned a saturating value on some
13952        // sentinel input (breaking the pass-through invariant the
13953        // sibling slice accessors carry).
13954        for servicos in [
13955            vec![],
13956            vec!["servicos/demo.computeunit.yaml"],
13957            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
13958            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
13959        ] {
13960            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
13961            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
13962            let first = c.servicos();
13963            let second = c.servicos();
13964            assert_eq!(
13965                first, second,
13966                "Caixa::servicos must be idempotent — two successive \
13967                 calls on the same &self must return the same &[String]",
13968            );
13969            assert_eq!(
13970                first.as_ptr(),
13971                second.as_ptr(),
13972                "Caixa::servicos must borrow the underlying \
13973                 Vec<String> storage — two successive calls must \
13974                 return slices with the same backing pointer (a fresh \
13975                 Vec<String> clone would change the pointer on every \
13976                 call)",
13977            );
13978            assert_eq!(
13979                first,
13980                expected.as_slice(),
13981                "Caixa::servicos must return :servicos verbatim by \
13982                 borrow — got {first:?}, expected {expected:?}",
13983            );
13984        }
13985    }
13986
13987    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
13988
13989    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
13990        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13991        c.deps = deps;
13992        c
13993    }
13994
13995    #[test]
13996    fn deps_returns_deps_slice_verbatim_across_permutations() {
13997        // The canonical per-`Caixa` `:deps` universal-axis runtime-
13998        // dependency-declaration-list slice pin: [`Caixa::deps`] must
13999        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
14000        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
14001        // access across every representative value in the accept-set —
14002        // `[]` (the "no runtime deps declared" arm every existing
14003        // fixture without a `:deps` line carries; the
14004        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
14005        // single-entry list (the shape most consumer caixas carry), a
14006        // canonical two-entry list (the multi-dep runtime closure), and
14007        // two past-the-guard sentinels — a `[""]`-`:nome` entry
14008        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
14009        // `NomeInvalid` but the accessor must ship the raw slot
14010        // verbatim) and a `[a, a]` duplicate (validate rejects through
14011        // `DuplicateNome { list: ":deps" }` but the accessor must ship
14012        // the raw slot verbatim so struct-literal fixtures continue to
14013        // expose the duplicate at the accessor).
14014        //
14015        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
14016        // pin on the substrate primitive — opens the outer-`Caixa`
14017        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
14018        // future lift closes on. Peer of the closed outer-`Caixa`
14019        // foreign-code-slot `&[String]` sub-family
14020        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14021        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
14022        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
14023        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
14024        // (`autores_returns_autores_slice_verbatim_across_permutations`
14025        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14026        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
14027        // projection pattern onto a novel element-type axis (`Dep`
14028        // composite vs the prior sibling family's `String` scalar).
14029        // Pins against a future silent detour that returned an owned
14030        // `Vec<Dep>` (which would type-check but silently clone on every
14031        // accessor call, breaking the zero-cost projection every peer
14032        // sibling slice accessor carries), a `[""] → []` collapse (which
14033        // would silently absorb the `NomeEmpty` refusal case at the
14034        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
14035        // would silently absorb the `DuplicateNome` refusal case at the
14036        // accessor boundary).
14037        for deps in [
14038            vec![],
14039            vec![Dep::simple("", "^0.1")],
14040            vec![Dep::simple("caixa-teia", "^0.1")],
14041            vec![
14042                Dep::simple("caixa-teia", "^0.1"),
14043                Dep::simple("caixa-core", "^0.1"),
14044            ],
14045            vec![
14046                Dep::simple("caixa-teia", "^0.1"),
14047                Dep::simple("caixa-teia", "^0.2"),
14048            ],
14049        ] {
14050            let c = caixa_with_deps(deps.clone());
14051            assert_eq!(
14052                c.deps(),
14053                deps.as_slice(),
14054                "Caixa::deps must return :deps verbatim (got {:?}, \
14055                 expected {deps:?})",
14056                c.deps(),
14057            );
14058            assert_eq!(
14059                c.deps(),
14060                c.deps.as_slice(),
14061                "Caixa::deps must element-equal the raw \
14062                 `self.deps.as_slice()` field access across every \
14063                 value in the Vec<Dep> accept-set",
14064            );
14065        }
14066    }
14067
14068    #[test]
14069    fn validate_deps_duplicate_arm_routes_through_accessor() {
14070        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
14071        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
14072        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
14073        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
14074        // "^0.2")], .. }` must surface the `DuplicateNome { list:
14075        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
14076        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
14077        // form) must pass validate. The pair jointly pins the accessor +
14078        // validate-gate composition: any future silent detour that had
14079        // the accessor return a dedupped slice on the `[a, a]` arm (a
14080        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
14081        // would silently absorb the `DuplicateNome` refusal at the
14082        // accessor boundary and the validate gate would accept a
14083        // struct-literal `Caixa` carrying the drift — the composition
14084        // pin catches that at caixa-core build time.
14085        //
14086        // Peer of the per-`Caixa`
14087        // `validate_autores_empty_entry_arm_routes_through_accessor`
14088        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14089        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
14090        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
14091        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
14092        // (611f78b) accessor-composition pins on the sibling `&[T]`-
14093        // composition axes — same "the validate gate must route through
14094        // the substrate-primitive typed dispatch" discipline extended
14095        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
14096        // composition surface, opening the outer-`Caixa` dependency-slot
14097        // arm of the composition-pin family.
14098        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
14099        let err = c.validate_deps().unwrap_err();
14100        assert!(
14101            matches!(
14102                err,
14103                DepError::DuplicateNome { ref nome, list } if nome == "d"
14104                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
14105            ),
14106            "validate_deps must reject deps == \
14107             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
14108             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
14109             accessor and the validate gate must route through the \
14110             same substrate-primitive typed dispatch on the :deps \
14111             within-list duplicate arm (got {err:?})",
14112        );
14113        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
14114        assert!(
14115            c.validate_deps().is_ok(),
14116            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
14117             (the canonical single-entry form)",
14118        );
14119    }
14120
14121    #[test]
14122    fn deps_projects_slice_by_borrow() {
14123        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
14124        // — the returned slice borrows the underlying `Vec<Dep>` storage
14125        // of the `:deps` slot and the accessor must not clone the
14126        // backing `Vec` on every call. Peer of the per-`Caixa`
14127        // `autores_projects_slice_by_borrow` (b5d813f),
14128        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
14129        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
14130        // `exe_projects_slice_by_borrow` (65d9527), and
14131        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
14132        // on the sibling outer top-level [`Caixa`] `&[String]`-return
14133        // axes — the accessor's returned slice must borrow from `&self`
14134        // (the returned reference's lifetime is tied to `&self`), and
14135        // calling the accessor twice on the same [`Caixa`] must yield
14136        // slices that are pointer-equal (the underlying byte-buffer is
14137        // the storage `Vec`'s allocation, not a fresh copy) as well as
14138        // value-equal (idempotent, no side effects on `&self`).
14139        //
14140        // Pins against a future silent detour that returned an owned
14141        // `Vec<Dep>` (which would type-check but silently clone on
14142        // every call), a `&Vec<Dep>` return (which would leak the
14143        // backing `Vec`'s grow/push/reserve surface no downstream
14144        // consumer reaches for), or a one-arm-only accessor that
14145        // returned a saturating value on some sentinel input.
14146        for deps in [
14147            vec![],
14148            vec![Dep::simple("caixa-teia", "^0.1")],
14149            vec![
14150                Dep::simple("caixa-teia", "^0.1"),
14151                Dep::simple("caixa-core", "^0.1"),
14152            ],
14153        ] {
14154            let c = caixa_with_deps(deps.clone());
14155            let first = c.deps();
14156            let second = c.deps();
14157            assert_eq!(
14158                first, second,
14159                "Caixa::deps must be idempotent — two successive calls \
14160                 on the same &self must return the same &[Dep]",
14161            );
14162            assert_eq!(
14163                first.as_ptr(),
14164                second.as_ptr(),
14165                "Caixa::deps must borrow the underlying Vec<Dep> \
14166                 storage — two successive calls must return slices \
14167                 with the same backing pointer (a fresh Vec<Dep> clone \
14168                 would change the pointer on every call)",
14169            );
14170            assert_eq!(
14171                first,
14172                deps.as_slice(),
14173                "Caixa::deps must return :deps verbatim by borrow — \
14174                 got {first:?}, expected {deps:?}",
14175            );
14176        }
14177    }
14178
14179    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
14180
14181    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
14182        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14183        c.deps_dev = deps_dev;
14184        c
14185    }
14186
14187    #[test]
14188    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
14189        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
14190        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
14191        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
14192        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
14193        // access across every representative value in the accept-set —
14194        // `[]` (the "no dev deps declared" arm every existing fixture
14195        // without a `:deps-dev` line carries; the [`Caixa::template`]
14196        // scaffold emits `:deps-dev ()`), a canonical single-entry list
14197        // (the shape most consumer caixas carry — a `tatara-check` dev
14198        // pin), a canonical two-entry list (the multi-dev-dep closure),
14199        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
14200        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
14201        // `NomeInvalid` but the accessor must ship the raw slot
14202        // verbatim) and a `[a, a]` duplicate (validate rejects through
14203        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
14204        // ship the raw slot verbatim so struct-literal fixtures continue
14205        // to expose the duplicate at the accessor).
14206        //
14207        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
14208        // pin on the substrate primitive — closes the outer-`Caixa`
14209        // dependency-slot `&[Dep]` sub-family the sibling
14210        // `deps_returns_deps_slice_verbatim_across_permutations`
14211        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
14212        // slice" projection pattern onto the sibling dev-dep axis —
14213        // pins against a future silent detour that returned an owned
14214        // `Vec<Dep>` (which would type-check but silently clone on every
14215        // accessor call, breaking the zero-cost projection every peer
14216        // sibling slice accessor carries), a `[""] → []` collapse (which
14217        // would silently absorb the `NomeEmpty` refusal case at the
14218        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
14219        // would silently absorb the `DuplicateNome` refusal case at the
14220        // accessor boundary).
14221        for deps_dev in [
14222            vec![],
14223            vec![Dep::simple("", "^0.1")],
14224            vec![Dep::simple("tatara-check", "^0.1")],
14225            vec![
14226                Dep::simple("tatara-check", "^0.1"),
14227                Dep::simple("caixa-lint", "^0.1"),
14228            ],
14229            vec![
14230                Dep::simple("tatara-check", "^0.1"),
14231                Dep::simple("tatara-check", "^0.2"),
14232            ],
14233        ] {
14234            let c = caixa_with_deps_dev(deps_dev.clone());
14235            assert_eq!(
14236                c.deps_dev(),
14237                deps_dev.as_slice(),
14238                "Caixa::deps_dev must return :deps-dev verbatim (got \
14239                 {:?}, expected {deps_dev:?})",
14240                c.deps_dev(),
14241            );
14242            assert_eq!(
14243                c.deps_dev(),
14244                c.deps_dev.as_slice(),
14245                "Caixa::deps_dev must element-equal the raw \
14246                 `self.deps_dev.as_slice()` field access across every \
14247                 value in the Vec<Dep> accept-set",
14248            );
14249        }
14250    }
14251
14252    #[test]
14253    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
14254        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
14255        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
14256        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
14257        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
14258        // Dep::simple("d", "^0.2")], .. }` must surface the
14259        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
14260        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
14261        // canonical single-entry form) must pass validate. The pair
14262        // jointly pins the accessor + validate-gate composition: any
14263        // future silent detour that had the accessor return a dedupped
14264        // slice on the `[a, a]` arm (a
14265        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
14266        // would silently absorb the `DuplicateNome` refusal at the
14267        // accessor boundary and the validate gate would accept a
14268        // struct-literal `Caixa` carrying the drift — the composition
14269        // pin catches that at caixa-core build time.
14270        //
14271        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
14272        // (ad34b4e) on the sibling `:deps` axis — same "the validate
14273        // gate must route through the substrate-primitive typed
14274        // dispatch" discipline folded onto the sibling `:deps-dev`
14275        // axis, closing the two-list dep-graph composition-pin family.
14276        // The `:deps-dev` diagnostic must carry the
14277        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
14278        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
14279        // offending list unambiguously.
14280        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
14281        let err = c.validate_deps().unwrap_err();
14282        assert!(
14283            matches!(
14284                err,
14285                DepError::DuplicateNome { ref nome, list } if nome == "d"
14286                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
14287            ),
14288            "validate_deps must reject deps_dev == \
14289             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
14290             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
14291             accessor and the validate gate must route through the \
14292             same substrate-primitive typed dispatch on the :deps-dev \
14293             within-list duplicate arm (got {err:?})",
14294        );
14295        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
14296        assert!(
14297            c.validate_deps().is_ok(),
14298            "validate_deps must accept deps_dev == \
14299             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
14300        );
14301    }
14302
14303    #[test]
14304    fn deps_dev_projects_slice_by_borrow() {
14305        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
14306        // borrow — the returned slice borrows the underlying `Vec<Dep>`
14307        // storage of the `:deps-dev` slot and the accessor must not
14308        // clone the backing `Vec` on every call. Peer of
14309        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
14310        // `:deps` axis, and of the per-`Caixa`
14311        // `autores_projects_slice_by_borrow` (b5d813f),
14312        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
14313        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
14314        // `exe_projects_slice_by_borrow` (65d9527), and
14315        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
14316        // on the sibling outer top-level [`Caixa`] `&[String]`-return
14317        // axes — the accessor's returned slice must borrow from `&self`
14318        // (the returned reference's lifetime is tied to `&self`), and
14319        // calling the accessor twice on the same [`Caixa`] must yield
14320        // slices that are pointer-equal (the underlying byte-buffer is
14321        // the storage `Vec`'s allocation, not a fresh copy) as well as
14322        // value-equal (idempotent, no side effects on `&self`).
14323        //
14324        // Pins against a future silent detour that returned an owned
14325        // `Vec<Dep>` (which would type-check but silently clone on
14326        // every call), a `&Vec<Dep>` return (which would leak the
14327        // backing `Vec`'s grow/push/reserve surface no downstream
14328        // consumer reaches for), or a one-arm-only accessor that
14329        // returned a saturating value on some sentinel input.
14330        for deps_dev in [
14331            vec![],
14332            vec![Dep::simple("tatara-check", "^0.1")],
14333            vec![
14334                Dep::simple("tatara-check", "^0.1"),
14335                Dep::simple("caixa-lint", "^0.1"),
14336            ],
14337        ] {
14338            let c = caixa_with_deps_dev(deps_dev.clone());
14339            let first = c.deps_dev();
14340            let second = c.deps_dev();
14341            assert_eq!(
14342                first, second,
14343                "Caixa::deps_dev must be idempotent — two successive \
14344                 calls on the same &self must return the same &[Dep]",
14345            );
14346            assert_eq!(
14347                first.as_ptr(),
14348                second.as_ptr(),
14349                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
14350                 storage — two successive calls must return slices \
14351                 with the same backing pointer (a fresh Vec<Dep> clone \
14352                 would change the pointer on every call)",
14353            );
14354            assert_eq!(
14355                first,
14356                deps_dev.as_slice(),
14357                "Caixa::deps_dev must return :deps-dev verbatim by \
14358                 borrow — got {first:?}, expected {deps_dev:?}",
14359            );
14360        }
14361    }
14362
14363    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
14364
14365    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
14366        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14367        c.limits = limits;
14368        c
14369    }
14370
14371    #[test]
14372    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
14373        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
14374        // composite optional-composite-reference-shape pin:
14375        // [`Caixa::limits`] must return the `:limits` typed
14376        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
14377        // reference over the same backing storage the raw
14378        // `self.limits.as_ref()` field access borrows from, byte-equal
14379        // across every representative fixture in the accept-set — the
14380        // author-omitted `None` shape (the "engine-default applies"
14381        // partition every downstream Servico M2 overlay emitter treats
14382        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
14383        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
14384        // per-axis cap is `None`, so the peer M2 overlay emitter's
14385        // `.is_empty()`-gated projection still emits nothing but the
14386        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
14387        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
14388        // fixture (only `:memory` set — the canonical shape most
14389        // memory-heavy Servicos carry), and a fully-populated composite
14390        // (every per-axis cap set — the canonical shape a
14391        // sandboxed-by-default Servico carries).
14392        //
14393        // Pins against a future silent detour that returned a fresh-
14394        // cloned [`LimitsSpec`] copy (which would type-check via the
14395        // `Clone` impl but silently break every downstream caller that
14396        // relied on the reference sharing the composite's backing
14397        // identity), a reference to an operator-resolved overlay (the
14398        // future per-cluster `:limits-overrides` slot — its resolution
14399        // must land at exactly this accessor body, not silently divert
14400        // the raw slot away from a second consumer), a
14401        // `None` → `Some(LimitsSpec::default)` cluster-default
14402        // projection (which would collapse the load-bearing
14403        // "author-omitted `:limits` ⇒ engine-default applies" partition
14404        // the peer [`crate::render::servico_m2_overlay`] emitter and
14405        // the peer [`Caixa::declared_servico_slots`] enumerator both
14406        // read), or an axis-shuffled projection (a future detour that
14407        // swapped `memory` and `fuel` through the accessor would
14408        // silently split the paired [`crate::StandardLayout::verify`]
14409        // per-`:limits` shape gate's traversal input from the peer
14410        // `servico_m2_overlay` emitter's projection input).
14411        //
14412        // First outer top-level [`Caixa`] `Option<&Composite>`-return
14413        // composite-reference accessor pin on the substrate primitive
14414        // — opens the outer-`Caixa` `Option<&Composite>` composite-
14415        // reference projection pattern the sibling `:behavior`
14416        // [`crate::BehaviorSpec`] / `:politicas`
14417        // [`crate::aplicacao::MeshPolicy`] / `:placement`
14418        // [`crate::aplicacao::Placement`] / `:entrada`
14419        // [`crate::aplicacao::Entrada`] future outer-composite lifts
14420        // fold on. Peer of the closed M3 outer-composite family the
14421        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
14422        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
14423        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
14424        // reference accessor pins already carry on the outer
14425        // [`crate::AplicacaoSpec`] altitude — extends the outer-
14426        // accessor byte-equal-projection discipline onto the outer
14427        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
14428        use crate::LimitsSpec;
14429        use std::time::Duration;
14430        let fixtures: Vec<Option<LimitsSpec>> = vec![
14431            None,
14432            Some(LimitsSpec::default()),
14433            Some(LimitsSpec {
14434                memory: Some(64 * 1024 * 1024),
14435                ..Default::default()
14436            }),
14437            Some(LimitsSpec {
14438                memory: Some(64 * 1024 * 1024),
14439                fuel: Some(1_000_000),
14440                wall_clock: Some(Duration::from_secs(30)),
14441                cpu: Some(500),
14442            }),
14443        ];
14444        for limits in fixtures {
14445            let c = caixa_with_limits(limits.clone());
14446            assert_eq!(
14447                c.limits(),
14448                limits.as_ref(),
14449                "Caixa::limits must return :limits verbatim (got {:?}, \
14450                 expected {:?})",
14451                c.limits(),
14452                limits.as_ref(),
14453            );
14454            match (c.limits(), c.limits.as_ref()) {
14455                (Some(a), Some(b)) => assert!(
14456                    std::ptr::eq(a, b),
14457                    "Caixa::limits accessor and self.limits.as_ref() \
14458                     field access must borrow the same backing storage \
14459                     — the accessor is the substrate-primitive typed \
14460                     dispatch every downstream Servico-M2-overlay \
14461                     composite consumer must route through, and a \
14462                     reference-identity split would silently break \
14463                     every consumer that relied on the borrow sharing \
14464                     the composite's storage",
14465                ),
14466                (None, None) => {}
14467                _ => panic!(
14468                    "Caixa::limits presence bit must byte-equal \
14469                     self.limits.is_some() — a presence-bit drift would \
14470                     silently split the paired StandardLayout::verify \
14471                     per-`:limits` shape gate's traversal head from \
14472                     the peer render::servico_m2_overlay M2 overlay \
14473                     emitter's traversal head from the peer \
14474                     Caixa::declared_servico_slots M2 declared-slot \
14475                     enumerator's presence probe",
14476                ),
14477            }
14478            assert_eq!(
14479                c.limits().is_some(),
14480                c.limits.is_some(),
14481                "Caixa::limits().is_some() must byte-equal \
14482                 self.limits.is_some() — a presence-bit drift would \
14483                 silently split every downstream Option<&LimitsSpec> \
14484                 consumer's partition on the engine-default arm",
14485            );
14486        }
14487    }
14488
14489    #[test]
14490    fn declared_servico_slots_limits_arm_routes_through_accessor() {
14491        // Composition pin: [`Caixa::declared_servico_slots`]'s
14492        // `:limits` presence-probe arm must key off [`Caixa::limits`],
14493        // not the raw `self.limits.is_some()` field-probe. Structurally:
14494        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
14495        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
14496        // (the presence bit is `Some`, so the M2 kind-coherence gate
14497        // must surface the slot as "declared" even when every per-axis
14498        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
14499        // push the label (the "author omitted the slot entirely"
14500        // partition). The pair jointly pins the accessor + declared-
14501        // slot enumerator composition: any future silent detour that
14502        // had the accessor collapse `Some(LimitsSpec::default())` to
14503        // `None` (a `.filter(|l| !l.is_empty())` projection) would
14504        // silently absorb the "declared but empty" arm at the
14505        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
14506        // kind-coherence gate would silently accept a
14507        // struct-literal `Caixa` carrying the drift.
14508        //
14509        // Peer of the sibling per-`Caixa`
14510        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
14511        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
14512        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
14513        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
14514        // enumerator gate must route through the substrate-primitive
14515        // typed dispatch" discipline extended onto the outer top-level
14516        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
14517        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
14518        // composition-pin family.
14519        use crate::LimitsSpec;
14520        let c = caixa_with_limits(Some(LimitsSpec::default()));
14521        let slots = c.declared_servico_slots();
14522        assert!(
14523            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
14524            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
14525             when `:limits` is Some (even for LimitsSpec::default()) \
14526             — the accessor and the enumerator gate must route through \
14527             the same substrate-primitive typed dispatch on the outer \
14528             :limits presence bit (got slots={slots:?})",
14529        );
14530        let c = caixa_with_limits(None);
14531        let slots = c.declared_servico_slots();
14532        assert!(
14533            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
14534            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
14535             when `:limits` is None — the author-omitted arm must \
14536             route through the accessor's None-return unchanged (got \
14537             slots={slots:?})",
14538        );
14539    }
14540
14541    #[test]
14542    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
14543        // Composition pin: [`crate::render::servico_m2_overlay`]'s
14544        // per-`:limits` M2 overlay emit arm must key off
14545        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
14546        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
14547        // Some(64 MiB), .. default }), .. }` must surface the
14548        // `M2_KEY_LIMITS` key with the per-axis
14549        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
14550        // limits: Some(LimitsSpec::default()), .. }` must omit the
14551        // key entirely (the `.is_empty()`-gated inner arm elides an
14552        // empty composite even when the outer presence bit is `Some`),
14553        // and a `Caixa { limits: None, .. }` must also omit the key
14554        // (the "author omitted the slot entirely" partition). The
14555        // three-fixture family jointly pins the accessor + M2 overlay
14556        // emitter composition: any future silent detour that had the
14557        // accessor return a fresh-cloned copy on the `Some` arm (a
14558        // `LimitsSpec::clone()` projection) would silently break the
14559        // reference-identity pin the peer per-axis
14560        // `serde_yaml::to_value(limits)` projection reads from.
14561        use crate::LimitsSpec;
14562        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
14563        let c = caixa_with_limits(Some(LimitsSpec {
14564            memory: Some(64 * 1024 * 1024),
14565            ..Default::default()
14566        }));
14567        let overlay = servico_m2_overlay(&c).unwrap();
14568        assert!(
14569            overlay.contains_key(M2_KEY_LIMITS),
14570            "servico_m2_overlay must surface M2_KEY_LIMITS when \
14571             `:limits` carries a non-empty composite — the accessor \
14572             and the M2 overlay emitter must route through the same \
14573             substrate-primitive typed dispatch on the outer :limits \
14574             composite (got overlay={overlay:?})",
14575        );
14576        let c = caixa_with_limits(Some(LimitsSpec::default()));
14577        let overlay = servico_m2_overlay(&c).unwrap();
14578        assert!(
14579            !overlay.contains_key(M2_KEY_LIMITS),
14580            "servico_m2_overlay must omit M2_KEY_LIMITS when \
14581             `:limits` is Some(LimitsSpec::default()) — the empty \
14582             composite's `.is_empty()`-gated inner arm must elide \
14583             the key regardless of the outer presence bit (got \
14584             overlay={overlay:?})",
14585        );
14586        let c = caixa_with_limits(None);
14587        let overlay = servico_m2_overlay(&c).unwrap();
14588        assert!(
14589            !overlay.contains_key(M2_KEY_LIMITS),
14590            "servico_m2_overlay must omit M2_KEY_LIMITS when \
14591             `:limits` is None — the author-omitted arm must route \
14592             through the accessor's None-return unchanged (got \
14593             overlay={overlay:?})",
14594        );
14595    }
14596
14597    #[test]
14598    fn limits_projects_option_ref_by_borrow() {
14599        // The by-borrow pin: [`Caixa::limits`] returns
14600        // `Option<&LimitsSpec>` by borrow — the returned reference
14601        // borrows the underlying `Option<LimitsSpec>` storage of the
14602        // `:limits` slot and the accessor must not clone the backing
14603        // composite on every call. Peer of the sibling
14604        // `deps_projects_slice_by_borrow` (ad34b4e) /
14605        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
14606        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
14607        // extended here to the outer [`Caixa`] `Option<&Composite>`-
14608        // return axis: the accessor's returned reference must borrow
14609        // from `&self` (the returned reference's lifetime is tied to
14610        // `&self`), and calling the accessor twice on the same
14611        // [`Caixa`] must yield references that are pointer-equal (the
14612        // underlying byte-buffer is the storage `LimitsSpec`'s
14613        // allocation, not a fresh copy) as well as value-equal
14614        // (idempotent, no side effects on `&self`).
14615        //
14616        // Pins against a future silent detour that returned an owned
14617        // `LimitsSpec` (which would type-check via the `Clone` impl
14618        // but silently clone on every call), a `&LimitsSpec` panic-
14619        // return on the `None` arm (which would collapse the load-
14620        // bearing `Option` presence-bit into a runtime panic), or a
14621        // one-arm-only accessor that returned a saturating composite
14622        // on some sentinel input.
14623        use crate::LimitsSpec;
14624        use std::time::Duration;
14625        for limits in [
14626            Some(LimitsSpec::default()),
14627            Some(LimitsSpec {
14628                memory: Some(64 * 1024 * 1024),
14629                fuel: Some(1_000_000),
14630                wall_clock: Some(Duration::from_secs(30)),
14631                cpu: Some(500),
14632            }),
14633        ] {
14634            let c = caixa_with_limits(limits.clone());
14635            let first = c.limits().unwrap();
14636            let second = c.limits().unwrap();
14637            assert_eq!(
14638                first, second,
14639                "Caixa::limits must be idempotent — two successive \
14640                 calls on the same &self must return the same \
14641                 &LimitsSpec",
14642            );
14643            assert!(
14644                std::ptr::eq(first, second),
14645                "Caixa::limits must borrow the underlying \
14646                 Option<LimitsSpec> storage — two successive calls \
14647                 must return references with the same backing pointer \
14648                 (a fresh LimitsSpec clone would change the pointer \
14649                 on every call)",
14650            );
14651            assert_eq!(
14652                Some(first),
14653                limits.as_ref(),
14654                "Caixa::limits must return :limits verbatim by borrow \
14655                 — got {first:?}, expected {:?}",
14656                limits.as_ref(),
14657            );
14658        }
14659        let c = caixa_with_limits(None);
14660        assert!(
14661            c.limits().is_none(),
14662            "Caixa::limits must return None when :limits is absent — \
14663             the author-omitted arm must project through the \
14664             accessor's Option::None unchanged",
14665        );
14666    }
14667
14668    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
14669
14670    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
14671        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14672        c.behavior = behavior;
14673        c
14674    }
14675
14676    #[test]
14677    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
14678        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
14679        // composite optional-composite-reference-shape pin:
14680        // [`Caixa::behavior`] must return the `:behavior` typed
14681        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
14682        // reference over the same backing storage the raw
14683        // `self.behavior.as_ref()` field access borrows from, byte-equal
14684        // across every representative fixture in the accept-set — the
14685        // author-omitted `None` shape (the "runtime-default applies"
14686        // partition every downstream Servico M2 overlay emitter treats
14687        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
14688        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
14689        // every per-callback path is `None`, so the peer M2 overlay
14690        // emitter's `.is_empty()`-gated projection still emits nothing
14691        // but the outer presence-bit is `Some`, so
14692        // [`Caixa::declared_servico_slots`] still pushes the
14693        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
14694        // (only `:on-state-change` set — the canonical shape a caixa
14695        // that only wires the hot-upgrade migration path carries), and
14696        // a fully-populated composite (every per-callback path set —
14697        // the canonical shape a fully-instrumented gen_server-shaped
14698        // Servico carries).
14699        //
14700        // Peer of the sibling
14701        // `limits_returns_limits_option_ref_verbatim_across_permutations`
14702        // (b2bd9d7) opening fixture-family + reference-identity +
14703        // presence-bit tetrad pin on the outer top-level [`Caixa`]
14704        // `Option<&Composite>`-return sub-family — extended here to the
14705        // second axis of that sub-family so both of the currently-lifted
14706        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
14707        // `:behavior`) carry the same "byte-equal, borrow-shared,
14708        // presence-bit-preserved" outer-accessor discipline.
14709        //
14710        // Pins against a future silent detour that returned a fresh-
14711        // cloned [`crate::BehaviorSpec`] copy (which would type-check
14712        // via the `Clone` impl but silently break every downstream
14713        // caller that relied on the reference sharing the composite's
14714        // backing identity), a reference to an operator-resolved
14715        // overlay (a future per-cluster `:behavior-overrides` slot —
14716        // its resolution must land at exactly this accessor body, not
14717        // silently divert the raw slot away from a second consumer), a
14718        // `None` → `Some(BehaviorSpec::default)` cluster-default
14719        // projection (which would collapse the load-bearing
14720        // "author-omitted `:behavior` ⇒ runtime-default applies"
14721        // partition the peer [`crate::render::servico_m2_overlay`]
14722        // emitter, the peer [`Caixa::declared_servico_slots`]
14723        // enumerator, and the cross-slot
14724        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
14725        // gate all read), or a callback-shuffled projection (a future
14726        // detour that swapped `on_init` and `on_terminate` through the
14727        // accessor would silently split the paired
14728        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
14729        // traversal input from the peer `servico_m2_overlay` emitter's
14730        // projection input from the cross-slot `:state-change`
14731        // composition gate's traversal input).
14732        use crate::BehaviorSpec;
14733        use std::path::PathBuf;
14734        let fixtures: Vec<Option<BehaviorSpec>> = vec![
14735            None,
14736            Some(BehaviorSpec::default()),
14737            Some(BehaviorSpec {
14738                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14739                ..Default::default()
14740            }),
14741            Some(BehaviorSpec {
14742                on_init: Some(PathBuf::from("lib/init.lisp")),
14743                on_call: Some(PathBuf::from("lib/handlers.lisp")),
14744                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
14745                on_info: Some(PathBuf::from("lib/handlers.lisp")),
14746                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14747                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
14748            }),
14749        ];
14750        for behavior in fixtures {
14751            let c = caixa_with_behavior(behavior.clone());
14752            assert_eq!(
14753                c.behavior(),
14754                behavior.as_ref(),
14755                "Caixa::behavior must return :behavior verbatim (got \
14756                 {:?}, expected {:?})",
14757                c.behavior(),
14758                behavior.as_ref(),
14759            );
14760            match (c.behavior(), c.behavior.as_ref()) {
14761                (Some(a), Some(b)) => assert!(
14762                    std::ptr::eq(a, b),
14763                    "Caixa::behavior accessor and self.behavior.as_ref() \
14764                     field access must borrow the same backing storage \
14765                     — the accessor is the substrate-primitive typed \
14766                     dispatch every downstream Servico-M2-overlay \
14767                     composite consumer must route through, and a \
14768                     reference-identity split would silently break \
14769                     every consumer that relied on the borrow sharing \
14770                     the composite's storage",
14771                ),
14772                (None, None) => {}
14773                _ => panic!(
14774                    "Caixa::behavior presence bit must byte-equal \
14775                     self.behavior.is_some() — a presence-bit drift \
14776                     would silently split the paired \
14777                     StandardLayout::verify per-`:behavior` shape \
14778                     gate's traversal head from the peer \
14779                     render::servico_m2_overlay M2 overlay emitter's \
14780                     traversal head from the cross-slot \
14781                     validate_upgrade_from_against_behavior \
14782                     composition gate's traversal head from the peer \
14783                     Caixa::declared_servico_slots M2 declared-slot \
14784                     enumerator's presence probe",
14785                ),
14786            }
14787            assert_eq!(
14788                c.behavior().is_some(),
14789                c.behavior.is_some(),
14790                "Caixa::behavior().is_some() must byte-equal \
14791                 self.behavior.is_some() — a presence-bit drift would \
14792                 silently split every downstream Option<&BehaviorSpec> \
14793                 consumer's partition on the runtime-default arm",
14794            );
14795        }
14796    }
14797
14798    #[test]
14799    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
14800        // Composition pin: [`Caixa::declared_servico_slots`]'s
14801        // `:behavior` presence-probe arm must key off
14802        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
14803        // field-probe. Structurally: a `Caixa { behavior:
14804        // Some(BehaviorSpec::default()), .. }` must still push
14805        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
14806        // presence bit is `Some`, so the M2 kind-coherence gate must
14807        // surface the slot as "declared" even when every per-callback
14808        // path is unset), and a `Caixa { behavior: None, .. }` must
14809        // NOT push the label (the "author omitted the slot entirely"
14810        // partition). The pair jointly pins the accessor + declared-
14811        // slot enumerator composition: any future silent detour that
14812        // had the accessor collapse `Some(BehaviorSpec::default())`
14813        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
14814        // silently absorb the "declared but empty" arm at the
14815        // accessor boundary and the
14816        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
14817        // kind-coherence gate would silently accept a struct-literal
14818        // `Caixa` carrying the drift.
14819        //
14820        // Peer of the sibling
14821        // `declared_servico_slots_limits_arm_routes_through_accessor`
14822        // (b2bd9d7) composition pin on the sibling `:limits` outer-
14823        // `Option<&LimitsSpec>` arm of the same
14824        // [`Caixa::declared_servico_slots`] M2 declared-slot
14825        // enumerator's traversal — same "the enumerator gate must
14826        // route through the substrate-primitive typed dispatch"
14827        // discipline extended onto the outer top-level [`Caixa`]
14828        // `Option<&BehaviorSpec>`-composition surface.
14829        use crate::BehaviorSpec;
14830        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
14831        let slots = c.declared_servico_slots();
14832        assert!(
14833            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
14834            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
14835             when `:behavior` is Some (even for BehaviorSpec::default()) \
14836             — the accessor and the enumerator gate must route through \
14837             the same substrate-primitive typed dispatch on the outer \
14838             :behavior presence bit (got slots={slots:?})",
14839        );
14840        let c = caixa_with_behavior(None);
14841        let slots = c.declared_servico_slots();
14842        assert!(
14843            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
14844            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
14845             when `:behavior` is None — the author-omitted arm must \
14846             route through the accessor's None-return unchanged (got \
14847             slots={slots:?})",
14848        );
14849    }
14850
14851    #[test]
14852    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
14853        // Composition pin: [`crate::render::servico_m2_overlay`]'s
14854        // per-`:behavior` M2 overlay emit arm must key off
14855        // [`Caixa::behavior`], not the raw `&caixa.behavior`
14856        // field-borrow. Structurally: a `Caixa { behavior:
14857        // Some(BehaviorSpec { on_state_change: Some(...), .. default
14858        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
14859        // per-callback `onStateChange` sub-mapping in the overlay, a
14860        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
14861        // must omit the key entirely (the `.is_empty()`-gated inner
14862        // arm elides an empty composite even when the outer presence
14863        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
14864        // also omit the key (the "author omitted the slot entirely"
14865        // partition). The three-fixture family jointly pins the
14866        // accessor + M2 overlay emitter composition: any future
14867        // silent detour that had the accessor return a fresh-cloned
14868        // copy on the `Some` arm (a `BehaviorSpec::clone()`
14869        // projection) would silently break the reference-identity
14870        // pin the peer per-callback `serde_yaml::to_value(behavior)`
14871        // projection reads from.
14872        //
14873        // Peer of the sibling
14874        // `servico_m2_overlay_limits_arm_routes_through_accessor`
14875        // (b2bd9d7) composition pin on the sibling `:limits` outer-
14876        // `Option<&LimitsSpec>` arm of the same
14877        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
14878        // traversal — same "the emitter must route through the
14879        // substrate-primitive typed dispatch on the outer composite"
14880        // discipline extended onto the outer top-level [`Caixa`]
14881        // `Option<&BehaviorSpec>`-composition surface.
14882        use crate::BehaviorSpec;
14883        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
14884        use std::path::PathBuf;
14885        let c = caixa_with_behavior(Some(BehaviorSpec {
14886            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14887            ..Default::default()
14888        }));
14889        let overlay = servico_m2_overlay(&c).unwrap();
14890        assert!(
14891            overlay.contains_key(M2_KEY_BEHAVIOR),
14892            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
14893             `:behavior` carries a non-empty composite — the accessor \
14894             and the M2 overlay emitter must route through the same \
14895             substrate-primitive typed dispatch on the outer :behavior \
14896             composite (got overlay={overlay:?})",
14897        );
14898        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
14899        let overlay = servico_m2_overlay(&c).unwrap();
14900        assert!(
14901            !overlay.contains_key(M2_KEY_BEHAVIOR),
14902            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
14903             `:behavior` is Some(BehaviorSpec::default()) — the empty \
14904             composite's `.is_empty()`-gated inner arm must elide the \
14905             key regardless of the outer presence bit (got \
14906             overlay={overlay:?})",
14907        );
14908        let c = caixa_with_behavior(None);
14909        let overlay = servico_m2_overlay(&c).unwrap();
14910        assert!(
14911            !overlay.contains_key(M2_KEY_BEHAVIOR),
14912            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
14913             `:behavior` is None — the author-omitted arm must route \
14914             through the accessor's None-return unchanged (got \
14915             overlay={overlay:?})",
14916        );
14917    }
14918
14919    #[test]
14920    fn behavior_projects_option_ref_by_borrow() {
14921        // The by-borrow pin: [`Caixa::behavior`] returns
14922        // `Option<&BehaviorSpec>` by borrow — the returned reference
14923        // borrows the underlying `Option<BehaviorSpec>` storage of the
14924        // `:behavior` slot and the accessor must not clone the backing
14925        // composite on every call. Peer of the sibling
14926        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
14927        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
14928        // return sub-family — extended here to the second axis of the
14929        // same sub-family: the accessor's returned reference must
14930        // borrow from `&self` (the returned reference's lifetime is
14931        // tied to `&self`), and calling the accessor twice on the same
14932        // [`Caixa`] must yield references that are pointer-equal (the
14933        // underlying byte-buffer is the storage `BehaviorSpec`'s
14934        // allocation, not a fresh copy) as well as value-equal
14935        // (idempotent, no side effects on `&self`).
14936        //
14937        // Pins against a future silent detour that returned an owned
14938        // `BehaviorSpec` (which would type-check via the `Clone` impl
14939        // but silently clone on every call), a `&BehaviorSpec` panic-
14940        // return on the `None` arm (which would collapse the load-
14941        // bearing `Option` presence-bit into a runtime panic), or a
14942        // one-arm-only accessor that returned a saturating composite
14943        // on some sentinel input.
14944        use crate::BehaviorSpec;
14945        use std::path::PathBuf;
14946        for behavior in [
14947            Some(BehaviorSpec::default()),
14948            Some(BehaviorSpec {
14949                on_init: Some(PathBuf::from("lib/init.lisp")),
14950                on_call: Some(PathBuf::from("lib/handlers.lisp")),
14951                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
14952                on_info: Some(PathBuf::from("lib/handlers.lisp")),
14953                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
14954                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
14955            }),
14956        ] {
14957            let c = caixa_with_behavior(behavior.clone());
14958            let first = c.behavior().unwrap();
14959            let second = c.behavior().unwrap();
14960            assert_eq!(
14961                first, second,
14962                "Caixa::behavior must be idempotent — two successive \
14963                 calls on the same &self must return the same \
14964                 &BehaviorSpec",
14965            );
14966            assert!(
14967                std::ptr::eq(first, second),
14968                "Caixa::behavior must borrow the underlying \
14969                 Option<BehaviorSpec> storage — two successive calls \
14970                 must return references with the same backing pointer \
14971                 (a fresh BehaviorSpec clone would change the pointer \
14972                 on every call)",
14973            );
14974            assert_eq!(
14975                Some(first),
14976                behavior.as_ref(),
14977                "Caixa::behavior must return :behavior verbatim by \
14978                 borrow — got {first:?}, expected {:?}",
14979                behavior.as_ref(),
14980            );
14981        }
14982        let c = caixa_with_behavior(None);
14983        assert!(
14984            c.behavior().is_none(),
14985            "Caixa::behavior must return None when :behavior is absent \
14986             — the author-omitted arm must project through the \
14987             accessor's Option::None unchanged",
14988        );
14989    }
14990
14991    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
14992
14993    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
14994        use crate::aplicacao::{Membro, WitContract};
14995        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14996        c.kind = CaixaKind::Aplicacao;
14997        c.membros = vec![Membro {
14998            caixa: "a".into(),
14999            versao: "^0.1".into(),
15000        }];
15001        c.contratos = vec![WitContract {
15002            de: "a".into(),
15003            para: "a".into(),
15004            wit: "wasi:http/proxy".into(),
15005            endpoint: Some("/x".into()),
15006            subject: None,
15007            slot: None,
15008        }];
15009        c.politicas = politicas;
15010        c
15011    }
15012
15013    #[test]
15014    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
15015        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
15016        // composite optional-composite-reference-shape pin:
15017        // [`Caixa::politicas`] must return the `:politicas` typed
15018        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
15019        // reference over the same backing storage the raw
15020        // `self.politicas.as_ref()` field access borrows from,
15021        // byte-equal across every representative fixture in the
15022        // accept-set — the author-omitted `None` shape (the "cluster-
15023        // default applies" partition every downstream mesh-artifact
15024        // emitter treats as "emit no `:politicas` overlay"), the
15025        // empty-composite `Some(MeshPolicy { .. default })` shape
15026        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
15027        // per-axis mesh-policy scalar is `None`, so the peer inner
15028        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
15029        // caixa-mesh overlay elides every per-axis emit but the outer
15030        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
15031        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
15032        // single-axis fixture (only `:timeout` set — the canonical
15033        // shape a latency-sensitive Aplicacao carries), and a
15034        // fully-populated composite (every per-axis mesh-policy
15035        // scalar set — the canonical shape a fully-governed
15036        // Aplicacao carries).
15037        //
15038        // Pins against a future silent detour that returned a fresh-
15039        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
15040        // type-check via the `Clone` impl but silently break every
15041        // downstream caller that relied on the reference sharing the
15042        // composite's backing identity), a reference to an operator-
15043        // resolved overlay (the future per-cluster
15044        // `:politicas-overrides` slot — its resolution must land at
15045        // exactly this accessor body, not silently divert the raw
15046        // slot away from the peer [`Caixa::declared_mesh_slots`]
15047        // enumerator's presence probe), a
15048        // `None` → `Some(MeshPolicy::default)` cluster-default
15049        // projection (which would collapse the load-bearing
15050        // "author-omitted `:politicas` ⇒ cluster-default applies"
15051        // partition the peer [`Caixa::declared_mesh_slots`]
15052        // enumerator and the peer [`Caixa::aplicacao_view`]
15053        // Aplicacao-composition seed both read), or an axis-shuffled
15054        // projection (a future detour that swapped `timeout` and
15055        // `retries` through the accessor would silently split the
15056        // paired [`Caixa::aplicacao_view`] seed's fold input from the
15057        // sibling M3 mesh-artifact emitter's projection input).
15058        //
15059        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
15060        // composite-reference accessor pin on the substrate primitive
15061        // — peer of the sibling
15062        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15063        // (b2bd9d7) and
15064        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15065        // (35d8b52) opening tetrad pins on the outer top-level
15066        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15067        // here to the first of the three M3 mesh-slot axes so the
15068        // opening third of the outer `Option<&Composite>` sub-family
15069        // carries the same "byte-equal, borrow-shared, presence-bit-
15070        // preserved" outer-accessor discipline.
15071        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
15072        use std::time::Duration;
15073        let fixtures: Vec<Option<MeshPolicy>> = vec![
15074            None,
15075            Some(MeshPolicy::default()),
15076            Some(MeshPolicy {
15077                timeout: Some(Duration::from_secs(30)),
15078                ..Default::default()
15079            }),
15080            Some(MeshPolicy {
15081                timeout: Some(Duration::from_secs(30)),
15082                retries: Some(3),
15083                circuit_breaker: Some(CircuitBreaker {
15084                    max_failures: 5,
15085                    window: Duration::from_secs(60),
15086                }),
15087                mtls_required: Some(true),
15088                rate_limit: Some(RateLimit {
15089                    rate: 100,
15090                    window: Duration::from_secs(1),
15091                }),
15092            }),
15093        ];
15094        for politicas in fixtures {
15095            let c = caixa_aplicacao_with_politicas(politicas.clone());
15096            assert_eq!(
15097                c.politicas(),
15098                politicas.as_ref(),
15099                "Caixa::politicas must return :politicas verbatim (got \
15100                 {:?}, expected {:?})",
15101                c.politicas(),
15102                politicas.as_ref(),
15103            );
15104            match (c.politicas(), c.politicas.as_ref()) {
15105                (Some(a), Some(b)) => assert!(
15106                    std::ptr::eq(a, b),
15107                    "Caixa::politicas accessor and self.politicas.as_ref() \
15108                     field access must borrow the same backing storage \
15109                     — the accessor is the substrate-primitive typed \
15110                     dispatch every downstream Aplicacao-mesh-overlay \
15111                     composite consumer must route through, and a \
15112                     reference-identity split would silently break \
15113                     every consumer that relied on the borrow sharing \
15114                     the composite's storage",
15115                ),
15116                (None, None) => {}
15117                _ => panic!(
15118                    "Caixa::politicas presence bit must byte-equal \
15119                     self.politicas.is_some() — a presence-bit drift \
15120                     would silently split the paired \
15121                     Caixa::aplicacao_view Aplicacao-composition seed's \
15122                     traversal head from the peer \
15123                     Caixa::declared_mesh_slots M3 declared-slot \
15124                     enumerator's presence probe",
15125                ),
15126            }
15127            assert_eq!(
15128                c.politicas().is_some(),
15129                c.politicas.is_some(),
15130                "Caixa::politicas().is_some() must byte-equal \
15131                 self.politicas.is_some() — a presence-bit drift would \
15132                 silently split every downstream Option<&MeshPolicy> \
15133                 consumer's partition on the cluster-default arm",
15134            );
15135        }
15136    }
15137
15138    #[test]
15139    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
15140        // Composition pin: [`Caixa::declared_mesh_slots`]'s
15141        // `:politicas` presence-probe arm must key off
15142        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
15143        // field-probe. Structurally: a `Caixa { politicas:
15144        // Some(MeshPolicy::default()), .. }` must still push
15145        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
15146        // presence bit is `Some`, so the M3 kind-coherence gate must
15147        // surface the slot as "declared" even when every per-axis
15148        // scalar is unset), and a `Caixa { politicas: None, .. }` must
15149        // NOT push the label (the "author omitted the slot entirely"
15150        // partition). The pair jointly pins the accessor + declared-
15151        // slot enumerator composition: any future silent detour that
15152        // had the accessor collapse `Some(MeshPolicy::default())` to
15153        // `None` (a `.filter(|p| !p.is_empty())` projection) would
15154        // silently absorb the "declared but empty" arm at the
15155        // accessor boundary and the
15156        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15157        // coherence gate would silently accept a struct-literal
15158        // `Caixa` carrying the drift.
15159        //
15160        // Peer of the sibling
15161        // `declared_servico_slots_limits_arm_routes_through_accessor`
15162        // (b2bd9d7) and
15163        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15164        // (35d8b52) composition pins on the sibling `:limits` /
15165        // `:behavior` outer-`Option<&Composite>` arms of the peer
15166        // [`Caixa::declared_servico_slots`] M2 declared-slot
15167        // enumerator's traversal — same "the enumerator gate must
15168        // route through the substrate-primitive typed dispatch"
15169        // discipline extended onto the outer top-level [`Caixa`] M3
15170        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
15171        // enumerator carries the same routing invariant as its M2
15172        // sibling.
15173        use crate::aplicacao::MeshPolicy;
15174        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
15175        let slots = c.declared_mesh_slots();
15176        assert!(
15177            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
15178            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
15179             when `:politicas` is Some (even for MeshPolicy::default()) \
15180             — the accessor and the enumerator gate must route through \
15181             the same substrate-primitive typed dispatch on the outer \
15182             :politicas presence bit (got slots={slots:?})",
15183        );
15184        let c = caixa_aplicacao_with_politicas(None);
15185        let slots = c.declared_mesh_slots();
15186        assert!(
15187            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
15188            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
15189             when `:politicas` is None — the author-omitted arm must \
15190             route through the accessor's None-return unchanged (got \
15191             slots={slots:?})",
15192        );
15193    }
15194
15195    #[test]
15196    fn aplicacao_view_politicas_arm_folds_through_accessor() {
15197        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
15198        // Aplicacao-composition seed must fold through
15199        // [`Caixa::politicas`], not the raw
15200        // `self.politicas.clone().unwrap_or_default()` field-borrow.
15201        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
15202        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
15203        // must surface a projected [`crate::AplicacaoSpec`] whose
15204        // `politicas().timeout()` field byte-equals the outer
15205        // composite's `timeout` scalar (the fold must project the
15206        // authored composite verbatim), a `Caixa { politicas:
15207        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
15208        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
15209        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
15210        // fold's empty-composite arm collapses to the same default the
15211        // author-omitted arm does), and a `Caixa { politicas: None,
15212        // kind: Aplicacao, .. }` must surface an
15213        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
15214        // [`crate::aplicacao::MeshPolicy::default`] (the "author
15215        // omitted the slot entirely" arm folds through the
15216        // `unwrap_or_default` onto the cluster-default). The triad
15217        // jointly pins the accessor + Aplicacao-composition seed
15218        // composition: any future silent detour that had the accessor
15219        // divert the raw slot away from the seed's fold (an operator-
15220        // resolved overlay's default-fold arm silently differing from
15221        // the raw slot's default-fold arm) would silently split the
15222        // build-time mesh-artifact emission gate from the caixa-mesh
15223        // renderer's Aplicacao-view input at the composition boundary.
15224        use crate::aplicacao::MeshPolicy;
15225        use std::time::Duration;
15226        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
15227            timeout: Some(Duration::from_secs(30)),
15228            ..Default::default()
15229        }));
15230        let view = c.aplicacao_view().unwrap();
15231        assert_eq!(
15232            view.politicas().timeout(),
15233            Some(Duration::from_secs(30)),
15234            "Caixa::aplicacao_view must fold the authored :politicas \
15235             :timeout scalar through the accessor verbatim onto the \
15236             projected AplicacaoSpec — a future silent detour at the \
15237             seed's fold arm would surface here as a projected-scalar \
15238             drift (got {:?})",
15239            view.politicas().timeout(),
15240        );
15241        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
15242        let view = c.aplicacao_view().unwrap();
15243        assert_eq!(
15244            view.politicas(),
15245            &MeshPolicy::default(),
15246            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
15247             through the accessor onto MeshPolicy::default — the empty- \
15248             composite arm collapses to the same default the author- \
15249             omitted arm does (got {:?})",
15250            view.politicas(),
15251        );
15252        let c = caixa_aplicacao_with_politicas(None);
15253        let view = c.aplicacao_view().unwrap();
15254        assert_eq!(
15255            view.politicas(),
15256            &MeshPolicy::default(),
15257            "Caixa::aplicacao_view must fold None through the accessor's \
15258             unwrap_or_default onto MeshPolicy::default — the author- \
15259             omitted arm must route through the accessor's None-return \
15260             unchanged (got {:?})",
15261            view.politicas(),
15262        );
15263    }
15264
15265    #[test]
15266    fn politicas_projects_option_ref_by_borrow() {
15267        // The by-borrow pin: [`Caixa::politicas`] returns
15268        // `Option<&MeshPolicy>` by borrow — the returned reference
15269        // borrows the underlying `Option<MeshPolicy>` storage of the
15270        // `:politicas` slot and the accessor must not clone the
15271        // backing composite on every call. Peer of the sibling
15272        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
15273        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
15274        // pins on the outer top-level [`Caixa`]
15275        // `Option<&Composite>`-return sub-family — extended here to
15276        // the third axis of the same sub-family: the accessor's
15277        // returned reference must borrow from `&self` (the returned
15278        // reference's lifetime is tied to `&self`), and calling the
15279        // accessor twice on the same [`Caixa`] must yield references
15280        // that are pointer-equal (the underlying byte-buffer is the
15281        // storage `MeshPolicy`'s allocation, not a fresh copy) as
15282        // well as value-equal (idempotent, no side effects on
15283        // `&self`).
15284        //
15285        // Pins against a future silent detour that returned an owned
15286        // `MeshPolicy` (which would type-check via the `Clone` impl
15287        // but silently clone on every call), a `&MeshPolicy` panic-
15288        // return on the `None` arm (which would collapse the load-
15289        // bearing `Option` presence-bit into a runtime panic), or a
15290        // one-arm-only accessor that returned a saturating composite
15291        // on some sentinel input.
15292        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
15293        use std::time::Duration;
15294        for politicas in [
15295            Some(MeshPolicy::default()),
15296            Some(MeshPolicy {
15297                timeout: Some(Duration::from_secs(30)),
15298                retries: Some(3),
15299                circuit_breaker: Some(CircuitBreaker {
15300                    max_failures: 5,
15301                    window: Duration::from_secs(60),
15302                }),
15303                mtls_required: Some(true),
15304                rate_limit: Some(RateLimit {
15305                    rate: 100,
15306                    window: Duration::from_secs(1),
15307                }),
15308            }),
15309        ] {
15310            let c = caixa_aplicacao_with_politicas(politicas.clone());
15311            let first = c.politicas().unwrap();
15312            let second = c.politicas().unwrap();
15313            assert_eq!(
15314                first, second,
15315                "Caixa::politicas must be idempotent — two successive \
15316                 calls on the same &self must return the same \
15317                 &MeshPolicy",
15318            );
15319            assert!(
15320                std::ptr::eq(first, second),
15321                "Caixa::politicas must borrow the underlying \
15322                 Option<MeshPolicy> storage — two successive calls \
15323                 must return references with the same backing pointer \
15324                 (a fresh MeshPolicy clone would change the pointer on \
15325                 every call)",
15326            );
15327            assert_eq!(
15328                Some(first),
15329                politicas.as_ref(),
15330                "Caixa::politicas must return :politicas verbatim by \
15331                 borrow — got {first:?}, expected {:?}",
15332                politicas.as_ref(),
15333            );
15334        }
15335        let c = caixa_aplicacao_with_politicas(None);
15336        assert!(
15337            c.politicas().is_none(),
15338            "Caixa::politicas must return None when :politicas is \
15339             absent — the author-omitted arm must project through the \
15340             accessor's Option::None unchanged",
15341        );
15342    }
15343
15344    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
15345
15346    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
15347        use crate::aplicacao::{Membro, WitContract};
15348        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15349        c.kind = CaixaKind::Aplicacao;
15350        c.membros = vec![Membro {
15351            caixa: "a".into(),
15352            versao: "^0.1".into(),
15353        }];
15354        c.contratos = vec![WitContract {
15355            de: "a".into(),
15356            para: "a".into(),
15357            wit: "wasi:http/proxy".into(),
15358            endpoint: Some("/x".into()),
15359            subject: None,
15360            slot: None,
15361        }];
15362        c.placement = placement;
15363        c
15364    }
15365
15366    #[test]
15367    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
15368        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
15369        // composite optional-composite-reference-shape pin:
15370        // [`Caixa::placement`] must return the `:placement` typed
15371        // `Option<Placement>` verbatim as an `Option<&Placement>`
15372        // reference over the same backing storage the raw
15373        // `self.placement.as_ref()` field access borrows from,
15374        // byte-equal across every representative fixture in the
15375        // accept-set — the author-omitted `None` shape (the
15376        // "cluster-default applies" partition every downstream mesh-
15377        // artifact emitter treats as "emit no `:placement` overlay"),
15378        // the empty-composite `Some(Placement { .. default })` shape
15379        // (`estrategia: SingleNode`, empty clusters, no shard-key /
15380        // affinity — the outer presence-bit is `Some` so
15381        // [`Caixa::declared_mesh_slots`] still pushes the
15382        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
15383        // `Replicated`-on-two-clusters fixture (the canonical shape a
15384        // stateless HTTP Aplicacao carries), and a fully-populated
15385        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
15386        // shape a stateful Akka-style cluster-sharding Aplicacao
15387        // carries).
15388        //
15389        // Pins against a future silent detour that returned a fresh-
15390        // cloned [`crate::aplicacao::Placement`] copy (which would
15391        // type-check via the `Clone` impl but silently break every
15392        // downstream caller that relied on the reference sharing the
15393        // composite's backing identity), a reference to an operator-
15394        // resolved overlay (the future per-cluster
15395        // `:placement-overrides` slot — its resolution must land at
15396        // exactly this accessor body, not silently divert the raw
15397        // slot away from the peer [`Caixa::declared_mesh_slots`]
15398        // enumerator's presence probe), a `None` →
15399        // `Some(Placement::default)` cluster-default projection (which
15400        // would collapse the load-bearing "author-omitted `:placement`
15401        // ⇒ cluster-default applies" partition the peer
15402        // [`Caixa::declared_mesh_slots`] enumerator and the peer
15403        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
15404        // read), or an axis-shuffled projection (a future detour that
15405        // swapped `clusters` and `affinity` through the accessor would
15406        // silently split the paired [`Caixa::aplicacao_view`] seed's
15407        // fold input from the sibling M3 mesh-artifact emitter's
15408        // projection input).
15409        //
15410        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
15411        // composite-reference accessor pin on the substrate primitive
15412        // — peer of the sibling
15413        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15414        // (b2bd9d7),
15415        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15416        // (35d8b52), and
15417        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
15418        // (5d23d29) opening triad pins on the outer top-level
15419        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15420        // here to the second of the three M3 mesh-slot axes so the
15421        // opening four-fifths of the outer `Option<&Composite>` sub-
15422        // family carries the same "byte-equal, borrow-shared,
15423        // presence-bit-preserved" outer-accessor discipline.
15424        use crate::aplicacao::{Placement, PlacementStrategy};
15425        let fixtures: Vec<Option<Placement>> = vec![
15426            None,
15427            Some(Placement::default()),
15428            Some(Placement {
15429                estrategia: PlacementStrategy::Replicated,
15430                clusters: vec!["rio".into(), "sao-paulo".into()],
15431                affinity: None,
15432                shard_key: None,
15433            }),
15434            Some(Placement {
15435                estrategia: PlacementStrategy::Sharded,
15436                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
15437                affinity: Some("data-locality".into()),
15438                shard_key: Some("$tenantId".into()),
15439            }),
15440        ];
15441        for placement in fixtures {
15442            let c = caixa_aplicacao_with_placement(placement.clone());
15443            assert_eq!(
15444                c.placement(),
15445                placement.as_ref(),
15446                "Caixa::placement must return :placement verbatim (got \
15447                 {:?}, expected {:?})",
15448                c.placement(),
15449                placement.as_ref(),
15450            );
15451            match (c.placement(), c.placement.as_ref()) {
15452                (Some(a), Some(b)) => assert!(
15453                    std::ptr::eq(a, b),
15454                    "Caixa::placement accessor and self.placement.as_ref() \
15455                     field access must borrow the same backing storage \
15456                     — the accessor is the substrate-primitive typed \
15457                     dispatch every downstream Aplicacao-distribution- \
15458                     overlay composite consumer must route through, and \
15459                     a reference-identity split would silently break \
15460                     every consumer that relied on the borrow sharing \
15461                     the composite's storage",
15462                ),
15463                (None, None) => {}
15464                _ => panic!(
15465                    "Caixa::placement presence bit must byte-equal \
15466                     self.placement.is_some() — a presence-bit drift \
15467                     would silently split the paired \
15468                     Caixa::aplicacao_view Aplicacao-composition seed's \
15469                     traversal head from the peer \
15470                     Caixa::declared_mesh_slots M3 declared-slot \
15471                     enumerator's presence probe",
15472                ),
15473            }
15474            assert_eq!(
15475                c.placement().is_some(),
15476                c.placement.is_some(),
15477                "Caixa::placement().is_some() must byte-equal \
15478                 self.placement.is_some() — a presence-bit drift would \
15479                 silently split every downstream Option<&Placement> \
15480                 consumer's partition on the cluster-default arm",
15481            );
15482        }
15483    }
15484
15485    #[test]
15486    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
15487        // Composition pin: [`Caixa::declared_mesh_slots`]'s
15488        // `:placement` presence-probe arm must key off
15489        // [`Caixa::placement`], not the raw `self.placement.is_some()`
15490        // field-probe. Structurally: a `Caixa { placement:
15491        // Some(Placement::default()), .. }` must still push
15492        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
15493        // presence bit is `Some`, so the M3 kind-coherence gate must
15494        // surface the slot as "declared" even when every per-axis
15495        // scalar defers to the cluster-default arm), and a `Caixa {
15496        // placement: None, .. }` must NOT push the label (the "author
15497        // omitted the slot entirely" partition). The pair jointly pins
15498        // the accessor + declared-slot enumerator composition: any
15499        // future silent detour that had the accessor collapse
15500        // `Some(Placement::default())` to `None` (a `.filter(|p|
15501        // p.clusters().is_empty().not())` projection) would silently
15502        // absorb the "declared but empty" arm at the accessor boundary
15503        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
15504        // kind-coherence gate would silently accept a struct-literal
15505        // `Caixa` carrying the drift.
15506        //
15507        // Peer of the sibling
15508        // `declared_servico_slots_limits_arm_routes_through_accessor`
15509        // (b2bd9d7),
15510        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15511        // (35d8b52), and
15512        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
15513        // (5d23d29) composition pins on the sibling `:limits` /
15514        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
15515        // — same "the enumerator gate must route through the
15516        // substrate-primitive typed dispatch" discipline extended onto
15517        // the second of the three M3 mesh-slot axes so the
15518        // [`Caixa::declared_mesh_slots`] enumerator carries the same
15519        // routing invariant on the `:placement` arm as the peer
15520        // `:politicas` arm.
15521        use crate::aplicacao::Placement;
15522        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
15523        let slots = c.declared_mesh_slots();
15524        assert!(
15525            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
15526            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
15527             when `:placement` is Some (even for Placement::default()) \
15528             — the accessor and the enumerator gate must route through \
15529             the same substrate-primitive typed dispatch on the outer \
15530             :placement presence bit (got slots={slots:?})",
15531        );
15532        let c = caixa_aplicacao_with_placement(None);
15533        let slots = c.declared_mesh_slots();
15534        assert!(
15535            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
15536            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
15537             when `:placement` is None — the author-omitted arm must \
15538             route through the accessor's None-return unchanged (got \
15539             slots={slots:?})",
15540        );
15541    }
15542
15543    #[test]
15544    fn aplicacao_view_placement_arm_folds_through_accessor() {
15545        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
15546        // Aplicacao-composition seed must fold through
15547        // [`Caixa::placement`], not the raw
15548        // `self.placement.clone().unwrap_or_default()` field-borrow.
15549        // Structurally: a `Caixa { placement: Some(Placement {
15550        // estrategia: Replicated, clusters: ["rio"], .. default }),
15551        // kind: Aplicacao, .. }` must surface a projected
15552        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
15553        // `placement().clusters()` byte-equal the outer composite's
15554        // authored values (the fold must project the authored
15555        // composite verbatim), a `Caixa { placement:
15556        // Some(Placement::default()), kind: Aplicacao, .. }` must
15557        // surface an [`crate::AplicacaoSpec`] whose `placement()`
15558        // byte-equals [`crate::aplicacao::Placement::default`] (the
15559        // fold's empty-composite arm collapses to the same default
15560        // the author-omitted arm does), and a `Caixa { placement:
15561        // None, kind: Aplicacao, .. }` must surface an
15562        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
15563        // [`crate::aplicacao::Placement::default`] (the "author
15564        // omitted the slot entirely" arm folds through the
15565        // `unwrap_or_default` onto the cluster-default). The triad
15566        // jointly pins the accessor + Aplicacao-composition seed
15567        // composition: any future silent detour that had the accessor
15568        // divert the raw slot away from the seed's fold (an operator-
15569        // resolved overlay's default-fold arm silently differing from
15570        // the raw slot's default-fold arm) would silently split the
15571        // build-time distribution-artifact emission gate from the
15572        // caixa-mesh renderer's Aplicacao-view input at the
15573        // composition boundary.
15574        use crate::aplicacao::{Placement, PlacementStrategy};
15575        let c = caixa_aplicacao_with_placement(Some(Placement {
15576            estrategia: PlacementStrategy::Replicated,
15577            clusters: vec!["rio".into()],
15578            affinity: None,
15579            shard_key: None,
15580        }));
15581        let view = c.aplicacao_view().unwrap();
15582        assert_eq!(
15583            view.placement().estrategia(),
15584            PlacementStrategy::Replicated,
15585            "Caixa::aplicacao_view must fold the authored :placement \
15586             :estrategia scalar through the accessor verbatim onto the \
15587             projected AplicacaoSpec — a future silent detour at the \
15588             seed's fold arm would surface here as a projected-scalar \
15589             drift (got {:?})",
15590            view.placement().estrategia(),
15591        );
15592        assert_eq!(
15593            view.placement().clusters(),
15594            &["rio"],
15595            "Caixa::aplicacao_view must fold the authored :placement \
15596             :clusters list through the accessor verbatim onto the \
15597             projected AplicacaoSpec — a future silent detour at the \
15598             seed's fold arm would surface here as a projected-list \
15599             drift (got {:?})",
15600            view.placement().clusters(),
15601        );
15602        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
15603        let view = c.aplicacao_view().unwrap();
15604        assert_eq!(
15605            view.placement(),
15606            &Placement::default(),
15607            "Caixa::aplicacao_view must fold Some(Placement::default()) \
15608             through the accessor onto Placement::default — the empty- \
15609             composite arm collapses to the same default the author- \
15610             omitted arm does (got {:?})",
15611            view.placement(),
15612        );
15613        let c = caixa_aplicacao_with_placement(None);
15614        let view = c.aplicacao_view().unwrap();
15615        assert_eq!(
15616            view.placement(),
15617            &Placement::default(),
15618            "Caixa::aplicacao_view must fold None through the accessor's \
15619             unwrap_or_default onto Placement::default — the author- \
15620             omitted arm must route through the accessor's None-return \
15621             unchanged (got {:?})",
15622            view.placement(),
15623        );
15624    }
15625
15626    #[test]
15627    fn placement_projects_option_ref_by_borrow() {
15628        // The by-borrow pin: [`Caixa::placement`] returns
15629        // `Option<&Placement>` by borrow — the returned reference
15630        // borrows the underlying `Option<Placement>` storage of the
15631        // `:placement` slot and the accessor must not clone the
15632        // backing composite on every call. Peer of the sibling
15633        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
15634        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
15635        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
15636        // pins on the outer top-level [`Caixa`]
15637        // `Option<&Composite>`-return sub-family — extended here to
15638        // the fourth axis of the same sub-family: the accessor's
15639        // returned reference must borrow from `&self` (the returned
15640        // reference's lifetime is tied to `&self`), and calling the
15641        // accessor twice on the same [`Caixa`] must yield references
15642        // that are pointer-equal (the underlying byte-buffer is the
15643        // storage `Placement`'s allocation, not a fresh copy) as well
15644        // as value-equal (idempotent, no side effects on `&self`).
15645        //
15646        // Pins against a future silent detour that returned an owned
15647        // `Placement` (which would type-check via the `Clone` impl
15648        // but silently clone on every call), a `&Placement` panic-
15649        // return on the `None` arm (which would collapse the load-
15650        // bearing `Option` presence-bit into a runtime panic), or a
15651        // one-arm-only accessor that returned a saturating composite
15652        // on some sentinel input.
15653        use crate::aplicacao::{Placement, PlacementStrategy};
15654        for placement in [
15655            Some(Placement::default()),
15656            Some(Placement {
15657                estrategia: PlacementStrategy::Sharded,
15658                clusters: vec!["rio".into(), "sao-paulo".into()],
15659                affinity: Some("data-locality".into()),
15660                shard_key: Some("$tenantId".into()),
15661            }),
15662        ] {
15663            let c = caixa_aplicacao_with_placement(placement.clone());
15664            let first = c.placement().unwrap();
15665            let second = c.placement().unwrap();
15666            assert_eq!(
15667                first, second,
15668                "Caixa::placement must be idempotent — two successive \
15669                 calls on the same &self must return the same \
15670                 &Placement",
15671            );
15672            assert!(
15673                std::ptr::eq(first, second),
15674                "Caixa::placement must borrow the underlying \
15675                 Option<Placement> storage — two successive calls \
15676                 must return references with the same backing pointer \
15677                 (a fresh Placement clone would change the pointer on \
15678                 every call)",
15679            );
15680            assert_eq!(
15681                Some(first),
15682                placement.as_ref(),
15683                "Caixa::placement must return :placement verbatim by \
15684                 borrow — got {first:?}, expected {:?}",
15685                placement.as_ref(),
15686            );
15687        }
15688        let c = caixa_aplicacao_with_placement(None);
15689        assert!(
15690            c.placement().is_none(),
15691            "Caixa::placement must return None when :placement is \
15692             absent — the author-omitted arm must project through the \
15693             accessor's Option::None unchanged",
15694        );
15695    }
15696
15697    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
15698
15699    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
15700        use crate::aplicacao::{Membro, WitContract};
15701        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15702        c.kind = CaixaKind::Aplicacao;
15703        c.membros = vec![Membro {
15704            caixa: "a".into(),
15705            versao: "^0.1".into(),
15706        }];
15707        c.contratos = vec![WitContract {
15708            de: "a".into(),
15709            para: "a".into(),
15710            wit: "wasi:http/proxy".into(),
15711            endpoint: Some("/x".into()),
15712            subject: None,
15713            slot: None,
15714        }];
15715        c.entrada = entrada;
15716        c
15717    }
15718
15719    #[test]
15720    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
15721        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
15722        // composite optional-composite-reference-shape pin:
15723        // [`Caixa::entrada`] must return the `:entrada` typed
15724        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
15725        // reference over the same backing storage the raw
15726        // `self.entrada.as_ref()` field access borrows from,
15727        // byte-equal across every representative fixture in the
15728        // accept-set — the author-omitted `None` shape (the
15729        // "cluster-internal Aplicacao" partition every downstream
15730        // Gateway-API emitter treats as "emit no listener + no
15731        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
15732        // (empty `paths` — the resolved-paths fallback the peer
15733        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
15734        // onto the substrate catch-all), and a fully-populated
15735        // multi-path-with-non-default-port fixture (the canonical
15736        // shape a public HTTP Aplicacao carries).
15737        //
15738        // Pins against a future silent detour that returned a fresh-
15739        // cloned [`crate::aplicacao::Entrada`] copy (which would
15740        // type-check via the `Clone` impl but silently break every
15741        // downstream caller that relied on the reference sharing the
15742        // composite's backing identity), a reference to an operator-
15743        // resolved overlay (the future per-cluster
15744        // `:entrada-overrides` slot — its resolution must land at
15745        // exactly this accessor body, not silently divert the raw
15746        // slot away from the peer [`Caixa::declared_mesh_slots`]
15747        // enumerator's presence probe), or an axis-shuffled projection
15748        // (a future detour that swapped `host` and `para` through the
15749        // accessor would silently split the paired
15750        // [`Caixa::aplicacao_view`] seed's forward input from the
15751        // sibling M3 gateway-artifact emitter's projection input).
15752        //
15753        // Fifth and final outer top-level [`Caixa`]
15754        // `Option<&Composite>`-return composite-reference accessor pin
15755        // on the substrate primitive — peer of the sibling
15756        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15757        // (b2bd9d7),
15758        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15759        // (35d8b52),
15760        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
15761        // (5d23d29), and
15762        // `placement_returns_placement_option_ref_verbatim_across_permutations`
15763        // (4fb8074) opening tetrad pins on the outer top-level
15764        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15765        // here to the third and final M3 mesh-slot axis so the closed
15766        // outer `Option<&Composite>` sub-family carries the same
15767        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
15768        // accessor discipline across all five arms.
15769        use crate::aplicacao::Entrada;
15770        let fixtures: Vec<Option<Entrada>> = vec![
15771            None,
15772            Some(Entrada {
15773                host: "checkout.quero.cloud".into(),
15774                para: "gateway".into(),
15775                paths: Vec::new(),
15776                port: crate::DEFAULT_SERVICO_PORT,
15777            }),
15778            Some(Entrada {
15779                host: "api.pleme.io".into(),
15780                para: "public-api".into(),
15781                paths: vec!["/v1".into(), "/v2".into()],
15782                port: 8080,
15783            }),
15784        ];
15785        for entrada in fixtures {
15786            let c = caixa_aplicacao_with_entrada(entrada.clone());
15787            assert_eq!(
15788                c.entrada(),
15789                entrada.as_ref(),
15790                "Caixa::entrada must return :entrada verbatim (got \
15791                 {:?}, expected {:?})",
15792                c.entrada(),
15793                entrada.as_ref(),
15794            );
15795            match (c.entrada(), c.entrada.as_ref()) {
15796                (Some(a), Some(b)) => assert!(
15797                    std::ptr::eq(a, b),
15798                    "Caixa::entrada accessor and self.entrada.as_ref() \
15799                     field access must borrow the same backing storage \
15800                     — the accessor is the substrate-primitive typed \
15801                     dispatch every downstream Aplicacao-external- \
15802                     gateway composite consumer must route through, and \
15803                     a reference-identity split would silently break \
15804                     every consumer that relied on the borrow sharing \
15805                     the composite's storage",
15806                ),
15807                (None, None) => {}
15808                _ => panic!(
15809                    "Caixa::entrada presence bit must byte-equal \
15810                     self.entrada.is_some() — a presence-bit drift \
15811                     would silently split the paired \
15812                     Caixa::aplicacao_view Aplicacao-composition seed's \
15813                     traversal head from the peer \
15814                     Caixa::declared_mesh_slots M3 declared-slot \
15815                     enumerator's presence probe",
15816                ),
15817            }
15818            assert_eq!(
15819                c.entrada().is_some(),
15820                c.entrada.is_some(),
15821                "Caixa::entrada().is_some() must byte-equal \
15822                 self.entrada.is_some() — a presence-bit drift would \
15823                 silently split every downstream Option<&Entrada> \
15824                 consumer's partition on the cluster-internal arm",
15825            );
15826        }
15827    }
15828
15829    #[test]
15830    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
15831        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
15832        // presence-probe arm must key off [`Caixa::entrada`], not the
15833        // raw `self.entrada.is_some()` field-probe. Structurally: a
15834        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
15835        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
15836        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
15837        // presence bit is `Some`, so the M3 kind-coherence gate must
15838        // surface the slot as "declared" even when every per-axis
15839        // scalar defers to the substrate catch-all / default port),
15840        // and a `Caixa { entrada: None, .. }` must NOT push the label
15841        // (the "author omitted the slot entirely" partition). The pair
15842        // jointly pins the accessor + declared-slot enumerator
15843        // composition: any future silent detour that had the accessor
15844        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
15845        // `.filter(|e| !e.paths.is_empty())` projection) would silently
15846        // absorb the "declared but empty-paths" arm at the accessor
15847        // boundary and the
15848        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15849        // coherence gate would silently accept a struct-literal
15850        // `Caixa` carrying the drift.
15851        //
15852        // Peer of the sibling
15853        // `declared_servico_slots_limits_arm_routes_through_accessor`
15854        // (b2bd9d7),
15855        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15856        // (35d8b52),
15857        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
15858        // (5d23d29), and
15859        // `declared_mesh_slots_placement_arm_routes_through_accessor`
15860        // (4fb8074) composition pins on the sibling `:limits` /
15861        // `:behavior` / `:politicas` / `:placement` outer-
15862        // `Option<&Composite>` arms — same "the enumerator gate must
15863        // route through the substrate-primitive typed dispatch"
15864        // discipline extended onto the third and final M3 mesh-slot
15865        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
15866        // carries the routing invariant on every M3 mesh-slot arm.
15867        use crate::aplicacao::Entrada;
15868        let c = caixa_aplicacao_with_entrada(Some(Entrada {
15869            host: "checkout.quero.cloud".into(),
15870            para: "gateway".into(),
15871            paths: Vec::new(),
15872            port: crate::DEFAULT_SERVICO_PORT,
15873        }));
15874        let slots = c.declared_mesh_slots();
15875        assert!(
15876            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
15877            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
15878             `:entrada` is Some (even for empty-paths / default-port) \
15879             — the accessor and the enumerator gate must route through \
15880             the same substrate-primitive typed dispatch on the outer \
15881             :entrada presence bit (got slots={slots:?})",
15882        );
15883        let c = caixa_aplicacao_with_entrada(None);
15884        let slots = c.declared_mesh_slots();
15885        assert!(
15886            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
15887            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
15888             when `:entrada` is None — the author-omitted arm must \
15889             route through the accessor's None-return unchanged (got \
15890             slots={slots:?})",
15891        );
15892    }
15893
15894    #[test]
15895    fn aplicacao_view_entrada_arm_folds_through_accessor() {
15896        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
15897        // Aplicacao-composition seed must fold through
15898        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
15899        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
15900        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
15901        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
15902        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
15903        // equals the outer composite's authored value (the fold must
15904        // project the authored composite verbatim), and a `Caixa {
15905        // entrada: None, kind: Aplicacao, .. }` must surface an
15906        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
15907        // "author omitted the slot entirely" arm folds through the
15908        // accessor's `Option::cloned` onto the same `None` presence
15909        // bit — unlike the peer `:politicas` / `:placement` arms
15910        // `:entrada` has no cluster-default fold, the omitted arm
15911        // stays omitted). The pair jointly pins the accessor +
15912        // Aplicacao-composition seed composition: any future silent
15913        // detour that had the accessor divert the raw slot away from
15914        // the seed's fold (an operator-resolved overlay's forward arm
15915        // silently differing from the raw slot's forward arm) would
15916        // silently split the build-time gateway-artifact emission gate
15917        // from the caixa-mesh renderer's Aplicacao-view input at the
15918        // composition boundary.
15919        use crate::aplicacao::Entrada;
15920        let authored = Entrada {
15921            host: "api.pleme.io".into(),
15922            para: "public-api".into(),
15923            paths: vec!["/v1".into()],
15924            port: 8080,
15925        };
15926        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
15927        let view = c.aplicacao_view().unwrap();
15928        assert_eq!(
15929            view.entrada(),
15930            Some(&authored),
15931            "Caixa::aplicacao_view must fold the authored :entrada \
15932             composite through the accessor verbatim onto the \
15933             projected AplicacaoSpec — a future silent detour at the \
15934             seed's fold arm would surface here as a projected- \
15935             composite drift (got {:?})",
15936            view.entrada(),
15937        );
15938        let c = caixa_aplicacao_with_entrada(None);
15939        let view = c.aplicacao_view().unwrap();
15940        assert!(
15941            view.entrada().is_none(),
15942            "Caixa::aplicacao_view must fold None through the \
15943             accessor's Option::cloned onto None — the author- \
15944             omitted arm must route through the accessor's None-return \
15945             unchanged (got {:?})",
15946            view.entrada(),
15947        );
15948    }
15949
15950    #[test]
15951    fn entrada_projects_option_ref_by_borrow() {
15952        // The by-borrow pin: [`Caixa::entrada`] returns
15953        // `Option<&Entrada>` by borrow — the returned reference
15954        // borrows the underlying `Option<Entrada>` storage of the
15955        // `:entrada` slot and the accessor must not clone the backing
15956        // composite on every call. Peer of the sibling
15957        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
15958        // `behavior_projects_option_ref_by_borrow` (35d8b52),
15959        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
15960        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
15961        // borrow pins on the outer top-level [`Caixa`]
15962        // `Option<&Composite>`-return sub-family — extended here to
15963        // the fifth and final axis of the same sub-family, closing
15964        // the discipline: the accessor's returned reference must
15965        // borrow from `&self` (the returned reference's lifetime is
15966        // tied to `&self`), and calling the accessor twice on the
15967        // same [`Caixa`] must yield references that are pointer-equal
15968        // (the underlying byte-buffer is the storage `Entrada`'s
15969        // allocation, not a fresh copy) as well as value-equal
15970        // (idempotent, no side effects on `&self`).
15971        //
15972        // Pins against a future silent detour that returned an owned
15973        // `Entrada` (which would type-check via the `Clone` impl but
15974        // silently clone on every call), a `&Entrada` panic-return on
15975        // the `None` arm (which would collapse the load-bearing
15976        // `Option` presence-bit into a runtime panic), or a one-arm-
15977        // only accessor that returned a saturating composite on some
15978        // sentinel input.
15979        use crate::aplicacao::Entrada;
15980        for entrada in [
15981            Some(Entrada {
15982                host: "checkout.quero.cloud".into(),
15983                para: "gateway".into(),
15984                paths: Vec::new(),
15985                port: crate::DEFAULT_SERVICO_PORT,
15986            }),
15987            Some(Entrada {
15988                host: "api.pleme.io".into(),
15989                para: "public-api".into(),
15990                paths: vec!["/v1".into(), "/v2".into()],
15991                port: 8080,
15992            }),
15993        ] {
15994            let c = caixa_aplicacao_with_entrada(entrada.clone());
15995            let first = c.entrada().unwrap();
15996            let second = c.entrada().unwrap();
15997            assert_eq!(
15998                first, second,
15999                "Caixa::entrada must be idempotent — two successive \
16000                 calls on the same &self must return the same &Entrada",
16001            );
16002            assert!(
16003                std::ptr::eq(first, second),
16004                "Caixa::entrada must borrow the underlying \
16005                 Option<Entrada> storage — two successive calls must \
16006                 return references with the same backing pointer (a \
16007                 fresh Entrada clone would change the pointer on every \
16008                 call)",
16009            );
16010            assert_eq!(
16011                Some(first),
16012                entrada.as_ref(),
16013                "Caixa::entrada must return :entrada verbatim by \
16014                 borrow — got {first:?}, expected {:?}",
16015                entrada.as_ref(),
16016            );
16017        }
16018        let c = caixa_aplicacao_with_entrada(None);
16019        assert!(
16020            c.entrada().is_none(),
16021            "Caixa::entrada must return None when :entrada is absent \
16022             — the author-omitted arm must project through the \
16023             accessor's Option::None unchanged",
16024        );
16025    }
16026
16027    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
16028
16029    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
16030        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16031        c.estrategia = estrategia;
16032        c
16033    }
16034
16035    #[test]
16036    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
16037        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
16038        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
16039        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
16040        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
16041        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
16042        // over the same discriminant the raw `self.estrategia` field
16043        // access carries, byte-equal across every representative fixture
16044        // in the accept-set — the author-omitted `None` shape (the
16045        // "defer to [`RestartStrategy::default`] through the
16046        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
16047        // every non-`Supervisor`-kind `defcaixa` carries by
16048        // `#[serde(default)]`), and each of the four closed-set variants
16049        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
16050        // / [`RestartStrategy::RestForOne`] /
16051        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
16052        // partitions on.
16053        //
16054        // Pins against a future silent detour that re-derived the
16055        // strategy from a peer axis (an accidental fallback to
16056        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
16057        // collapse that read the outer `:children` list-length axis into
16058        // the strategy discriminator at the accessor boundary), a
16059        // stale-derive detour that substituted [`RestartStrategy::default`]
16060        // when the outer `Option` held `None` (which would silently
16061        // collapse the load-bearing "author explicitly declared
16062        // `:estrategia OneForOne`" vs "author omitted the slot and
16063        // inherited the default" partition the [`Self::declared_supervisor_slots`]
16064        // presence-probe reads — the enumerator gate would still push
16065        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
16066        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
16067        // kind-coherence gate's traversal head from the
16068        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
16069        // composition head), a reference to an operator-resolved overlay
16070        // (the future per-cluster `:estrategia-overrides` slot — its
16071        // resolution must land at exactly this accessor body, not
16072        // silently divert the raw slot away from a second consumer), or
16073        // an axis-remap projection (a future detour that mapped
16074        // `OneForAll` through the accessor onto `OneForOne` would
16075        // silently split every downstream sibling-restart-strategy
16076        // consumer's per-arm fan-out).
16077        //
16078        // First outer top-level [`Caixa`] `Option<Copy>`-return
16079        // supervisor-tree-slot flat-spread accessor pin on the substrate
16080        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
16081        // projection pattern the sibling per-`Caixa` `:max-restarts` /
16082        // `:restart-window` future outer-scalar pins fold on. Peer of
16083        // the inner-altitude
16084        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
16085        // (eafb619) pin on the post-composition [`SupervisorSpec`]
16086        // altitude — same "the substrate-primitive accessor must byte-
16087        // equal the raw field access verbatim across every author-
16088        // declared value" discipline extended onto the pre-composition
16089        // outer author-surface [`Caixa`] altitude. Peer of the closed
16090        // outer-`Caixa` `Option<&Composite>` composite-reference family
16091        // the sibling `limits` / `behavior` / `politicas` / `placement` /
16092        // `entrada`
16093        // `..._returns_..._option_ref_verbatim_across_permutations` pins
16094        // already carry on the outer `Option<&Composite>` altitude.
16095        use crate::supervisor::RestartStrategy;
16096        let fixtures: Vec<Option<RestartStrategy>> = vec![
16097            None,
16098            Some(RestartStrategy::OneForOne),
16099            Some(RestartStrategy::OneForAll),
16100            Some(RestartStrategy::RestForOne),
16101            Some(RestartStrategy::SimpleOneForOne),
16102        ];
16103        for estrategia in fixtures {
16104            let c = caixa_with_estrategia(estrategia);
16105            assert_eq!(
16106                c.estrategia(),
16107                estrategia,
16108                "Caixa::estrategia must return :estrategia verbatim (got \
16109                 {:?}, expected {:?})",
16110                c.estrategia(),
16111                estrategia,
16112            );
16113            assert_eq!(
16114                c.estrategia(),
16115                c.estrategia,
16116                "Caixa::estrategia accessor and self.estrategia field \
16117                 access must byte-equal — the accessor is the substrate-\
16118                 primitive typed dispatch every downstream supervisor-\
16119                 tree flat-spread consumer must route through, and a \
16120                 discriminant split would silently break every consumer \
16121                 that relied on the accessor sharing the field's own \
16122                 Option<Copy> shape",
16123            );
16124            assert_eq!(
16125                c.estrategia().is_some(),
16126                c.estrategia.is_some(),
16127                "Caixa::estrategia().is_some() must byte-equal \
16128                 self.estrategia.is_some() — a presence-bit drift would \
16129                 silently split the paired Caixa::declared_supervisor_slots \
16130                 presence-probe arm from the Caixa::supervisor_view \
16131                 unwrap_or_default() fold's composition input",
16132            );
16133        }
16134    }
16135
16136    #[test]
16137    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
16138        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16139        // `:estrategia` presence-probe arm must key off
16140        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
16141        // field-probe. Structurally: every `Caixa { estrategia:
16142        // Some(RestartStrategy::_), .. }` variant must push
16143        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
16144        // (the presence bit is `Some` for every closed-set variant, so
16145        // the M2 supervisor-tree kind-coherence gate must surface the
16146        // slot as "declared" regardless of which variant the author
16147        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
16148        // the label (the "author omitted the slot entirely, deferring
16149        // to [`RestartStrategy::default`] through the supervisor_view
16150        // fold" partition). The pair jointly pins the accessor +
16151        // declared-slot enumerator composition: any future silent detour
16152        // that had the accessor collapse `Some(RestartStrategy::default())`
16153        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
16154        // projection) would silently absorb the "declared but default-
16155        // valued" arm at the accessor boundary and the
16156        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
16157        // coherence gate would silently accept a struct-literal `Caixa`
16158        // carrying the drift.
16159        //
16160        // Peer of the sibling per-`Caixa`
16161        // `declared_servico_slots_limits_arm_routes_through_accessor`
16162        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
16163        // `Option<&LimitsSpec>` composition axis — same "the enumerator
16164        // gate must route through the substrate-primitive typed
16165        // dispatch" discipline extended onto the flat-spread M2
16166        // supervisor-tree `Option<RestartStrategy>`-composition surface,
16167        // opening the outer-`Caixa` supervisor-tree-slot arm of the
16168        // composition-pin family.
16169        use crate::supervisor::RestartStrategy;
16170        for estrategia in [
16171            RestartStrategy::OneForOne,
16172            RestartStrategy::OneForAll,
16173            RestartStrategy::RestForOne,
16174            RestartStrategy::SimpleOneForOne,
16175        ] {
16176            let c = caixa_with_estrategia(Some(estrategia));
16177            let slots = c.declared_supervisor_slots();
16178            assert!(
16179                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
16180                "declared_supervisor_slots must push \
16181                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
16182                 Some({estrategia:?}) — the accessor and the enumerator \
16183                 gate must route through the same substrate-primitive \
16184                 typed dispatch on the outer :estrategia presence bit \
16185                 (got slots={slots:?})",
16186            );
16187        }
16188        let c = caixa_with_estrategia(None);
16189        let slots = c.declared_supervisor_slots();
16190        assert!(
16191            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
16192            "declared_supervisor_slots must NOT push \
16193             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
16194             — the author-omitted arm must route through the accessor's \
16195             None-return unchanged (got slots={slots:?})",
16196        );
16197    }
16198
16199    #[test]
16200    fn supervisor_view_estrategia_arm_routes_through_accessor() {
16201        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
16202        // [`SupervisorSpec`] construction arm must key off
16203        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
16204        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
16205        // for every `:kind Supervisor` `Caixa` carrying an author-
16206        // declared `Some(RestartStrategy::_)` variant, the composed
16207        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
16208        // outer accessor's declared variant unchanged; and for a
16209        // `:kind Supervisor` `Caixa` carrying `None`, the composed
16210        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
16211        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
16212        // arm the flat-spread `unwrap_or_default()` fold projects to on
16213        // the author-omitted arm — this is the *composition* between the
16214        // outer `Option<RestartStrategy>` accessor's presence-bit
16215        // surface and the inner post-composition non-`Option`
16216        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
16217        // pins the accessor + supervisor_view composition: any future
16218        // silent detour that had the accessor promote `None` to
16219        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
16220        // projection) would silently collapse the two arms into one at
16221        // the accessor boundary and the [`Self::declared_supervisor_slots`]
16222        // presence probe would silently drift from the composition site.
16223        //
16224        // Peer of the sibling M2 supervisor-slot post-composition
16225        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
16226        // pin on the [`SupervisorSpec::validate`] altitude — this pin
16227        // extends that inner-altitude accessor-routing discipline onto
16228        // the pre-composition outer author-surface [`Caixa`] altitude,
16229        // pinning the composition edge between the flat-spread outer
16230        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
16231        // `RestartStrategy` axes.
16232        use crate::CaixaKind;
16233        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16234        for estrategia in [
16235            RestartStrategy::OneForOne,
16236            RestartStrategy::OneForAll,
16237            RestartStrategy::RestForOne,
16238            RestartStrategy::SimpleOneForOne,
16239        ] {
16240            let mut c = caixa_with_estrategia(Some(estrategia));
16241            c.kind = CaixaKind::Supervisor;
16242            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
16243            // shape partition through the [`gen_platform::IsVariant`]
16244            // derive-generated
16245            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
16246            // than the raw `matches!(estrategia, RestartStrategy::
16247            // SimpleOneForOne)` open-coded pattern-match — same closed-
16248            // set-typed-enum arm-discriminator dispatch discipline the
16249            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
16250            // convergence (915a934) extended onto its two paired positive
16251            // / negated `matches!` sites and the peer
16252            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
16253            // predicate convergence (766ec63) extended onto the M3 mesh-
16254            // slot per-`:placement` distribution-strategy discriminator
16255            // axis. See the sibling `supervisor::tests::
16256            // round_trip_all_strategies` and
16257            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
16258            // fixtures — the three sites (all test-only,
16259            // acknowledged in 915a934's Prior-commits footnote as the
16260            // outstanding follow-up) now consult one typed dispatch on
16261            // the substrate primitive.
16262            c.children = if estrategia.is_simple_one_for_one() {
16263                Vec::new()
16264            } else {
16265                vec![ChildSpec {
16266                    caixa: "worker".into(),
16267                    versao: "^0.1".into(),
16268                    restart: RestartPolicy::Permanent,
16269                }]
16270            };
16271            let view = c.supervisor_view().expect(
16272                "supervisor_view must materialize a SupervisorSpec for a \
16273                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
16274            );
16275            assert_eq!(
16276                view.estrategia(),
16277                c.estrategia().unwrap(),
16278                "supervisor_view must carry the outer Caixa::estrategia() \
16279                 declared variant onto the composed SupervisorSpec.estrategia \
16280                 field verbatim on the Some arm (got {:?}, expected {:?})",
16281                view.estrategia(),
16282                c.estrategia().unwrap(),
16283            );
16284        }
16285        // The author-omitted arm: outer `None` → composed
16286        // `RestartStrategy::default()` through the flat-spread
16287        // `unwrap_or_default()` fold.
16288        let mut c = caixa_with_estrategia(None);
16289        c.kind = CaixaKind::Supervisor;
16290        // Populate children so the sibling supervisor slots are coherent
16291        // for the [`Self::supervisor_view`] projection; the `:estrategia`
16292        // arm still defers to [`RestartStrategy::default`] on the
16293        // author-omitted arm even when the sibling slots carry values.
16294        c.children = vec![ChildSpec {
16295            caixa: "worker".into(),
16296            versao: "^0.1".into(),
16297            restart: RestartPolicy::Permanent,
16298        }];
16299        let view = c.supervisor_view().expect(
16300            "supervisor_view must materialize a SupervisorSpec for a \
16301             :kind Supervisor Caixa carrying a None `:estrategia` slot",
16302        );
16303        assert_eq!(
16304            view.estrategia(),
16305            RestartStrategy::default(),
16306            "supervisor_view must project the outer Caixa::estrategia() \
16307             None arm onto RestartStrategy::default() through the flat-\
16308             spread unwrap_or_default() fold (got {:?}, expected {:?})",
16309            view.estrategia(),
16310            RestartStrategy::default(),
16311        );
16312        assert!(
16313            c.estrategia().is_none(),
16314            "Caixa::estrategia() must remain None on the author-omitted \
16315             arm — the supervisor_view fold must not mutate the outer \
16316             flat-spread presence bit",
16317        );
16318    }
16319
16320    #[test]
16321    fn estrategia_projects_option_by_copy() {
16322        // The by-`Copy` pin: [`Caixa::estrategia`] returns
16323        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
16324        // the accessor does not borrow `&self` past the call (no
16325        // lifetime on the return type), and calling the accessor twice
16326        // on the same [`Caixa`] must yield discriminant-equal values
16327        // (idempotent, no side effects on `&self`). Peer of the sibling
16328        // outer-`Caixa` `Option<&Composite>` by-borrow
16329        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
16330        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
16331        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
16332        // `placement_projects_option_ref_by_borrow` (4fb8074) /
16333        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
16334        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
16335        // extended here to the outer-`Caixa` `Option<Copy>`-return
16336        // flat-spread axis. The `Copy` discipline replaces the pointer-
16337        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
16338        // `Copy` discriminant is definitionally the same discriminant, so
16339        // the axis reduces to discriminant equality).
16340        //
16341        // Pins against a future silent detour that returned a fresh
16342        // `Option<&RestartStrategy>` (which would type-check but silently
16343        // introduce a borrow of `&self` past the call, collapsing the
16344        // load-bearing "no lifetime on the return type" `Copy` projection
16345        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
16346        // read side effect that flipped the outer discriminant on
16347        // successive calls, or an axis-remap projection that returned a
16348        // different variant than the field storage.
16349        use crate::supervisor::RestartStrategy;
16350        for estrategia in [
16351            Some(RestartStrategy::OneForOne),
16352            Some(RestartStrategy::OneForAll),
16353            Some(RestartStrategy::RestForOne),
16354            Some(RestartStrategy::SimpleOneForOne),
16355        ] {
16356            let c = caixa_with_estrategia(estrategia);
16357            let first = c.estrategia();
16358            let second = c.estrategia();
16359            assert_eq!(
16360                first, second,
16361                "Caixa::estrategia must be idempotent — two successive \
16362                 calls on the same &self must return the same \
16363                 Option<RestartStrategy>",
16364            );
16365            assert_eq!(
16366                first, estrategia,
16367                "Caixa::estrategia must return :estrategia verbatim by \
16368                 Copy — got {first:?}, expected {estrategia:?}",
16369            );
16370        }
16371        let c = caixa_with_estrategia(None);
16372        assert!(
16373            c.estrategia().is_none(),
16374            "Caixa::estrategia must return None when :estrategia is \
16375             absent — the author-omitted arm must project through the \
16376             accessor's Option::None unchanged",
16377        );
16378    }
16379
16380    // ── Caixa::max_restarts / Caixa::restart_window —
16381    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
16382    //    (Option<u32> / Option<&str>) folding on the ed04d3c
16383    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
16384
16385    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
16386        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16387        c.max_restarts = max_restarts;
16388        c
16389    }
16390
16391    fn caixa_supervisor_with_max_restarts_and_window(
16392        max_restarts: Option<u32>,
16393        restart_window: Option<&str>,
16394    ) -> Caixa {
16395        use crate::CaixaKind;
16396        use crate::supervisor::{ChildSpec, RestartPolicy};
16397        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
16398        c.kind = CaixaKind::Supervisor;
16399        c.max_restarts = max_restarts;
16400        c.restart_window = restart_window.map(str::to_string);
16401        c.children = vec![ChildSpec {
16402            caixa: "worker".into(),
16403            versao: "^0.1".into(),
16404            restart: RestartPolicy::Permanent,
16405        }];
16406        c
16407    }
16408
16409    #[test]
16410    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
16411        // Value-shape pin: [`Caixa::max_restarts`] returns the
16412        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
16413        // from the typed slot's own storage, byte-equal across the
16414        // author-omitted `None` arm (the "defer to the
16415        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
16416        // `{intensity, 5, 60}` default" partition every
16417        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
16418        // and each of the representative fixtures in the accept-set —
16419        // `0` (the zero-floor arm the peer
16420        // [`crate::supervisor::SupervisorSpec::validate`]
16421        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
16422        // the post-composition altitude — the accessor must ship the
16423        // raw slot verbatim so struct-literal fixtures continue to
16424        // expose the zero at the accessor boundary), the OTP-canonical
16425        // `5` default (`{intensity, 5, 60}` worker-supervisor from
16426        // Learn You Some Erlang), `1000` (the
16427        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
16428        // upper-bound gate accepts on the boundary), `u32::MAX` (a
16429        // past-the-cap sentinel that the substrate-primitive accessor
16430        // must still ship verbatim). Second outer top-level
16431        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
16432        // pin — folds on the sibling
16433        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
16434        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
16435        // onto the sibling `Option<u32>` restart-budget-count arm.
16436        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
16437        for max_restarts in fixtures {
16438            let c = caixa_with_max_restarts(max_restarts);
16439            assert_eq!(
16440                c.max_restarts(),
16441                max_restarts,
16442                "Caixa::max_restarts must return :max-restarts verbatim \
16443                 (got {:?}, expected {max_restarts:?})",
16444                c.max_restarts(),
16445            );
16446            assert_eq!(
16447                c.max_restarts(),
16448                c.max_restarts,
16449                "Caixa::max_restarts accessor and self.max_restarts \
16450                 field access must byte-equal — a presence-bit or count \
16451                 drift would silently split the paired \
16452                 Caixa::declared_supervisor_slots presence-probe arm \
16453                 from the Caixa::supervisor_view unwrap_or(5) fold's \
16454                 composition input",
16455            );
16456        }
16457    }
16458
16459    #[test]
16460    fn max_restarts_projects_option_by_copy() {
16461        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
16462        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
16463        // borrow `&self` past the call (no lifetime on the return type),
16464        // and calling the accessor twice on the same [`Caixa`] must
16465        // yield equal values (idempotent, no side effects). Peer of the
16466        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
16467        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
16468        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
16469            let c = caixa_with_max_restarts(max_restarts);
16470            let first = c.max_restarts();
16471            let second = c.max_restarts();
16472            assert_eq!(
16473                first, second,
16474                "Caixa::max_restarts must be idempotent — two successive \
16475                 calls on the same &self must return the same Option<u32>",
16476            );
16477            assert_eq!(
16478                first, max_restarts,
16479                "Caixa::max_restarts must return :max-restarts verbatim \
16480                 by Copy — got {first:?}, expected {max_restarts:?}",
16481            );
16482        }
16483    }
16484
16485    #[test]
16486    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
16487        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16488        // `:max-restarts` presence-probe arm must key off
16489        // [`Caixa::max_restarts`], not the raw
16490        // `self.max_restarts.is_some()` field-probe. Structurally: every
16491        // `Caixa { max_restarts: Some(_), .. }` variant must push
16492        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
16493        // list (the presence bit is `Some` for every representative
16494        // count, so the M2 kind-coherence gate must surface the slot as
16495        // "declared"), and a `Caixa { max_restarts: None, .. }` must
16496        // NOT push the label. Peer of the sibling
16497        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
16498        // (ed04d3c) composition pin — same routing-through-accessor
16499        // discipline extended onto the sibling flat-spread `Option<u32>`
16500        // arm.
16501        for max_restarts in [0u32, 5, 1000, u32::MAX] {
16502            let c = caixa_with_max_restarts(Some(max_restarts));
16503            let slots = c.declared_supervisor_slots();
16504            assert!(
16505                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
16506                "declared_supervisor_slots must push \
16507                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
16508                 is Some({max_restarts}) — the accessor and the \
16509                 enumerator gate must route through the same \
16510                 substrate-primitive typed dispatch on the outer \
16511                 :max-restarts presence bit (got slots={slots:?})",
16512            );
16513        }
16514        let c = caixa_with_max_restarts(None);
16515        let slots = c.declared_supervisor_slots();
16516        assert!(
16517            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
16518            "declared_supervisor_slots must NOT push \
16519             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
16520             None — the author-omitted arm must route through the \
16521             accessor's None-return unchanged (got slots={slots:?})",
16522        );
16523    }
16524
16525    #[test]
16526    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
16527        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
16528        // [`SupervisorSpec`] construction arm must key off
16529        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
16530        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
16531        // every `:kind Supervisor` `Caixa` carrying an author-declared
16532        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
16533        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
16534        // carrying `None`, the composed [`SupervisorSpec`]'s
16535        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
16536        // of the sibling
16537        // `supervisor_view_estrategia_arm_routes_through_accessor`
16538        // (ed04d3c) composition pin.
16539        for max_restarts in [1u32, 5, 1000] {
16540            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
16541            let view = c.supervisor_view().expect(
16542                "supervisor_view must materialize a SupervisorSpec for a \
16543                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
16544            );
16545            assert_eq!(
16546                view.max_restarts(),
16547                max_restarts,
16548                "supervisor_view must carry the outer \
16549                 Caixa::max_restarts() Some arm onto the composed \
16550                 SupervisorSpec.max_restarts field verbatim (got {}, \
16551                 expected {max_restarts})",
16552                view.max_restarts(),
16553            );
16554        }
16555        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16556        let view = c.supervisor_view().expect(
16557            "supervisor_view must materialize a SupervisorSpec for a \
16558             :kind Supervisor Caixa carrying a None :max-restarts",
16559        );
16560        assert_eq!(
16561            view.max_restarts(),
16562            5,
16563            "supervisor_view must project the outer \
16564             Caixa::max_restarts() None arm onto the OTP-canonical \
16565             {{intensity, 5, 60}} default (5) through the flat-spread \
16566             unwrap_or(5) fold (got {})",
16567            view.max_restarts(),
16568        );
16569        assert!(
16570            c.max_restarts().is_none(),
16571            "Caixa::max_restarts() must remain None on the author-\
16572             omitted arm — the supervisor_view fold must not mutate \
16573             the outer flat-spread presence bit",
16574        );
16575    }
16576
16577    #[test]
16578    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
16579        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
16580        // `:estrategia` arm must degrade onto the substrate-canonical
16581        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
16582        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
16583        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
16584        // worker-supervisor default — rather than the transitively-
16585        // derived [`crate::supervisor::RestartStrategy::default`] route
16586        // the prior `.unwrap_or_default()` fold reached for. Prior to the
16587        // lift the composition site carried `.unwrap_or_default()` with
16588        // no compile-time link back to the shared OTP-canonical strategy
16589        // default that the paired [`crate::supervisor::Default for
16590        // RestartStrategy`] impl and the [`crate::supervisor::Default for
16591        // SupervisorSpec`] impl's struct-literal `estrategia` field both
16592        // (now) route through the same lifted constant — so a future
16593        // rebrand of the OTP-canonical strategy default (an OTP
16594        // `rest_for_one` widening once the substrate discovers startup-
16595        // order-coupled child cohorts as the more common worker-
16596        // supervisor shape, a per-cluster overlay the operator pins
16597        // through the MESH-COMPOSITION §III.2 supervision-canary
16598        // `:estrategia-overrides` roadmap slot) would have had to migrate
16599        // the paired `MaxIntensity` + `Period` halves through the lifted
16600        // constants and the `one_for_one` half through a
16601        // `RestartStrategy::default()` route in lockstep or a
16602        // `:kind Supervisor` caixa carrying an author-omitted
16603        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
16604        // whose `estrategia` disagreed with the paired
16605        // `SupervisorSpec::default()` view. Byte-parity against the
16606        // lifted constant closes the split. Peer of the sibling
16607        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
16608        // composition pin on the paired `MaxIntensity` half + the
16609        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
16610        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
16611        // pins on the sibling entry points onto the shared substrate
16612        // constant.
16613        use crate::CaixaKind;
16614        use crate::supervisor::{ChildSpec, RestartPolicy};
16615        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
16616        c.kind = CaixaKind::Supervisor;
16617        c.estrategia = None;
16618        c.children = vec![ChildSpec {
16619            caixa: "worker".into(),
16620            versao: "^0.1".into(),
16621            restart: RestartPolicy::Permanent,
16622        }];
16623        let view = c.supervisor_view().expect(
16624            "supervisor_view must materialize a SupervisorSpec for a \
16625             :kind Supervisor Caixa carrying a None :estrategia",
16626        );
16627        assert_eq!(
16628            view.estrategia(),
16629            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
16630            "supervisor_view must degrade the outer \
16631             Caixa::estrategia() None arm onto the lifted \
16632             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
16633             expected {:?})",
16634            view.estrategia(),
16635            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
16636        );
16637    }
16638
16639    #[test]
16640    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
16641        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
16642        // `:max-restarts` arm must degrade onto the substrate-canonical
16643        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
16644        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
16645        // `MaxIntensity` default — rather than a raw `5` literal. Prior
16646        // to the lift the composition site carried an inline
16647        // `.unwrap_or(5)` with no compile-time link back to the shared
16648        // OTP-canonical default that the serde-side
16649        // `#[serde(default = "default_max_restarts")]` wire-format arm
16650        // and the [`Default for crate::supervisor::SupervisorSpec`]
16651        // struct-literal default arm both key off — so a future rebrand
16652        // of the OTP-canonical default (Elixir's `Supervisor` `3`
16653        // default, a per-cluster overlay the operator pins through the
16654        // MESH-COMPOSITION §III.2 supervision-canary
16655        // `:supervisor :max-restarts-overrides` roadmap slot) would
16656        // have had to be threaded through both the serde-side helper
16657        // and this view-construction arm in lockstep or a `:kind
16658        // Supervisor` caixa carrying `:max-restarts ()` would silently
16659        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
16660        // with the same fixture's serde-side `SupervisorSpec` view (an
16661        // author-omitted slot round-tripping through
16662        // `SupervisorSpec::default()` to the lifted constant, then
16663        // splitting to a stale literal past `supervisor_view`).
16664        // Byte-parity against the lifted constant closes the split.
16665        // Peer of the sibling
16666        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
16667        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
16668        // composition pins that close the same routing on the two
16669        // sibling entry points onto the shared substrate constant.
16670        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16671        let view = c.supervisor_view().expect(
16672            "supervisor_view must materialize a SupervisorSpec for a \
16673             :kind Supervisor Caixa carrying a None :max-restarts",
16674        );
16675        assert_eq!(
16676            view.max_restarts(),
16677            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
16678            "supervisor_view must degrade the outer \
16679             Caixa::max_restarts() None arm onto the lifted \
16680             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
16681             expected {})",
16682            view.max_restarts(),
16683            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
16684        );
16685    }
16686
16687    #[test]
16688    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
16689        // Value-shape pin: [`Caixa::restart_window`] returns the
16690        // `:restart-window` typed `Option<String>` verbatim as an
16691        // `Option<&str>`, borrowed from the typed slot's own storage,
16692        // byte-equal across the author-omitted `None` arm and each of
16693        // the representative fixtures in the accept-set — the canonical
16694        // `"60s"` from `{intensity, 5, 60}`, the sibling
16695        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
16696        // / `"0s"`) the shared codec's positive-set sweep pin covers,
16697        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
16698        // seconds drift the sibling [`Self::validate_restart_window`]
16699        // gate refuses; the accessor must ship the raw slot verbatim
16700        // so struct-literal fixtures continue to expose the drift at
16701        // the accessor boundary). Third outer top-level [`Caixa`]
16702        // supervisor-tree flat-spread pin — extends the sub-family onto
16703        // the sibling `Option<&str>` raw-duration-string arm.
16704        for window in [
16705            None,
16706            Some("60s"),
16707            Some("5m"),
16708            Some("1h"),
16709            Some("500ms"),
16710            Some("1.5s"),
16711            Some(""),
16712        ] {
16713            let c = caixa_with_restart_window(window);
16714            assert_eq!(
16715                c.restart_window(),
16716                window,
16717                "Caixa::restart_window must return :restart-window \
16718                 verbatim as Option<&str> (got {:?}, expected {window:?})",
16719                c.restart_window(),
16720            );
16721            assert_eq!(
16722                c.restart_window(),
16723                c.restart_window.as_deref(),
16724                "Caixa::restart_window accessor and \
16725                 self.restart_window.as_deref() field access must \
16726                 byte-equal — a byte-level drift would silently split \
16727                 the paired Caixa::declared_supervisor_slots \
16728                 presence-probe arm from the \
16729                 Caixa::validate_restart_window shared-codec gate and \
16730                 the Caixa::supervisor_view soft-swallowing fold",
16731            );
16732        }
16733    }
16734
16735    #[test]
16736    fn restart_window_projects_slice_by_borrow() {
16737        // The by-borrow pin: [`Caixa::restart_window`] returns
16738        // `Option<&str>` by borrow — the returned string slice borrows
16739        // the underlying `Option<String>` storage of the `:restart-window`
16740        // slot and the accessor must not clone on every call. Peer of
16741        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
16742        // by-borrow pins on the universal-axis scalar family
16743        // (`licenca_projects_option_ref_by_borrow` /
16744        // `descricao_projects_option_ref_by_borrow` and siblings) —
16745        // extended onto the M2 supervisor-tree flat-spread
16746        // `Option<&str>` raw-duration-string axis.
16747        for window in [None, Some("60s"), Some("5m"), Some("")] {
16748            let c = caixa_with_restart_window(window);
16749            let first = c.restart_window();
16750            let second = c.restart_window();
16751            assert_eq!(
16752                first, second,
16753                "Caixa::restart_window must be idempotent — two \
16754                 successive calls on the same &self must return the \
16755                 same Option<&str>",
16756            );
16757            if let (Some(a), Some(b)) = (first, second) {
16758                assert_eq!(
16759                    a.as_ptr(),
16760                    b.as_ptr(),
16761                    "Caixa::restart_window must borrow the underlying \
16762                     String storage — two successive Some-arm calls must \
16763                     return slices with the same backing pointer (a fresh \
16764                     String clone would change the pointer on every call)",
16765                );
16766            }
16767            assert_eq!(
16768                first, window,
16769                "Caixa::restart_window must return :restart-window \
16770                 verbatim by borrow — got {first:?}, expected {window:?}",
16771            );
16772        }
16773    }
16774
16775    #[test]
16776    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
16777        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16778        // `:restart-window` presence-probe arm must key off
16779        // [`Caixa::restart_window`], not the raw
16780        // `self.restart_window.is_some()` field-probe. Structurally:
16781        // every `Caixa { restart_window: Some(_), .. }` must push
16782        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
16783        // list, and a `Caixa { restart_window: None, .. }` must NOT
16784        // push the label. Peer of the sibling
16785        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
16786        // routing pin.
16787        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
16788            let c = caixa_with_restart_window(Some(window));
16789            let slots = c.declared_supervisor_slots();
16790            assert!(
16791                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
16792                "declared_supervisor_slots must push \
16793                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
16794                 `:restart-window` is Some({window:?}) — the accessor \
16795                 and the enumerator gate must route through the same \
16796                 substrate-primitive typed dispatch on the outer \
16797                 :restart-window presence bit (got slots={slots:?})",
16798            );
16799        }
16800        let c = caixa_with_restart_window(None);
16801        let slots = c.declared_supervisor_slots();
16802        assert!(
16803            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
16804            "declared_supervisor_slots must NOT push \
16805             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
16806             is None — the author-omitted arm must route through the \
16807             accessor's None-return unchanged (got slots={slots:?})",
16808        );
16809    }
16810
16811    #[test]
16812    fn validate_restart_window_arm_routes_through_accessor() {
16813        // Composition pin: [`Caixa::validate_restart_window`]'s
16814        // shared-codec fold arm must key off [`Caixa::restart_window`],
16815        // not the raw `self.restart_window.as_deref()` field-projection.
16816        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
16817        // express no reset" canonical shape); (2) a canonical `Some`
16818        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
16819        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
16820        // .. })` carrying the offending raw string verbatim. The three
16821        // arms jointly pin that the validator's raw-string binding is
16822        // the accessor's return, not a peer projection — any future
16823        // silent detour that had the accessor collapse `Some("")` to
16824        // `None` would silently absorb the empty-after-trim refusal
16825        // case at the accessor boundary.
16826        caixa_with_restart_window(None)
16827            .validate_restart_window()
16828            .expect("None :restart-window must validate through the accessor");
16829        caixa_with_restart_window(Some("60s"))
16830            .validate_restart_window()
16831            .expect("canonical :restart-window \"60s\" must validate through the accessor");
16832        let err = caixa_with_restart_window(Some("1.5s"))
16833            .validate_restart_window()
16834            .expect_err("fractional-seconds :restart-window must fail through the accessor");
16835        assert!(
16836            matches!(
16837                err,
16838                ManifestError::RestartWindowMalformed { ref restart_window, .. }
16839                    if restart_window == "1.5s"
16840            ),
16841            "validator must carry the offending raw string verbatim \
16842             from the accessor's borrowed &str (got {err:?})",
16843        );
16844    }
16845
16846    #[test]
16847    fn supervisor_view_restart_window_arm_routes_through_accessor() {
16848        // Composition pin: [`Caixa::supervisor_view`]'s
16849        // per-`:restart-window` [`SupervisorSpec`] construction arm
16850        // must key off [`Caixa::restart_window`]'s soft-swallowing
16851        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
16852        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
16853        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
16854        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
16855        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
16856        // (the shared codec's canonical parse); (3) codec-rejected
16857        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
16858        // (the soft-swallow preserving the view's best-effort shape).
16859        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
16860        let view = c.supervisor_view().expect("Supervisor kind has a view");
16861        assert_eq!(
16862            view.restart_window(),
16863            None,
16864            "supervisor_view must project outer None :restart-window \
16865             onto None on the composed SupervisorSpec (never-reset \
16866             sentinel) through the accessor's None-return unchanged",
16867        );
16868
16869        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
16870        let view = c.supervisor_view().expect("Supervisor kind has a view");
16871        assert_eq!(
16872            view.restart_window(),
16873            Some(std::time::Duration::from_secs(60)),
16874            "supervisor_view must fold outer Some(\"60s\") through the \
16875             shared duration_codec into Duration::from_secs(60) on the \
16876             composed SupervisorSpec (accessor's Some(&str) → codec \
16877             parse → Some(Duration))",
16878        );
16879
16880        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
16881        let view = c.supervisor_view().expect("Supervisor kind has a view");
16882        assert_eq!(
16883            view.restart_window(),
16884            None,
16885            "supervisor_view must soft-swallow the shared-codec parse \
16886             failure to None (the view's best-effort shape the sibling \
16887             manifest-level validate_restart_window surfaces as \
16888             RestartWindowMalformed); the accessor's raw-string return \
16889             is the single input every downstream consumer keys off",
16890        );
16891    }
16892
16893    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
16894
16895    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
16896        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16897        c.upgrade_from = upgrade_from;
16898        c
16899    }
16900
16901    #[test]
16902    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
16903        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
16904        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
16905        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
16906        // typed `Vec<UpgradeFromEntry>` verbatim as a
16907        // `&[UpgradeFromEntry]` slice-view over the same backing
16908        // buffer the raw `self.upgrade_from.as_slice()` field access
16909        // borrows from, element-equal across every representative
16910        // fixture in the accept-set — `[]` (the "no hot-upgrade path
16911        // declared" arm every `defcaixa` without an `:upgrade-from`
16912        // block carries; `#[serde(default)]` folds an omitted slot
16913        // onto `Vec::new()`), a canonical single-entry `Restart`
16914        // fixture (the shape most Servicos carry — a single prior
16915        // version with the fallback strategy), a canonical multi-
16916        // entry list carrying every typed instruction variant
16917        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
16918        // `Restart`), and a past-the-guard sentinel — a duplicate-
16919        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
16920        // ([`crate::upgrade::validate_upgrade_from`] rejects through
16921        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
16922        // ship the raw slot verbatim so struct-literal fixtures
16923        // continue to expose the duplicate at the accessor boundary).
16924        //
16925        // Pins against a future silent detour that returned an owned
16926        // `Vec<UpgradeFromEntry>` (which would type-check but silently
16927        // clone on every accessor call, breaking the zero-cost
16928        // projection every peer sibling slice accessor carries), a
16929        // `[dup, dup] → [dup]` dedup collapse (which would silently
16930        // absorb the `DuplicateFrom` refusal case at the accessor
16931        // boundary and the [`crate::StandardLayout::verify`] cross-
16932        // entry gate would silently accept a struct-literal `Caixa`
16933        // carrying the drift), a reference to an operator-resolved
16934        // overlay (the future per-cluster `:upgrade-overrides` slot
16935        // — its resolution must land at exactly this accessor body,
16936        // not silently divert the raw slot away from a second
16937        // consumer), or an axis-shuffled projection (a future detour
16938        // that reordered entries through the accessor would silently
16939        // split the paired [`crate::StandardLayout::verify`] per-
16940        // `:upgrade-from` shape gate's traversal input from the peer
16941        // [`crate::render::servico_m2_overlay`] emitter's projection
16942        // input, since the operator's hot-upgrade dispatch matches
16943        // per-`:from` and axis reordering would silently split the
16944        // per-entry script-path existence probe's iteration order
16945        // from the M2 overlay emitter's serialized-entry order).
16946        //
16947        // First outer top-level [`Caixa`] `&[Composite]`-return
16948        // slice accessor pin on the substrate primitive for M2 / M3
16949        // typed-slot vec-carry axes — opens the outer-`Caixa`
16950        // `&[Composite]` composite-slice projection pattern the
16951        // sibling `:children` [`crate::supervisor::ChildSpec`] /
16952        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
16953        // [`crate::aplicacao::WitContract`] future outer-composite-
16954        // slice pins fold on. Peer of the closed outer-`Caixa`
16955        // scalar `Option<&Composite>` composite-reference family the
16956        // sibling `limits` / `behavior` / `politicas` / `placement`
16957        // / `entrada` `..._returns_..._option_ref_verbatim_across_
16958        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
16959        // the "byte-equal, borrow-shared" outer-accessor discipline
16960        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
16961        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
16962        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
16963            vec![],
16964            vec![UpgradeFromEntry {
16965                from: "0.0.1".into(),
16966                instructions: vec![UpgradeInstruction::Restart],
16967            }],
16968            vec![
16969                UpgradeFromEntry {
16970                    from: "0.0.1".into(),
16971                    instructions: vec![
16972                        UpgradeInstruction::LoadModule {
16973                            module: "demo".into(),
16974                        },
16975                        UpgradeInstruction::SoftPurge {
16976                            module: "demo".into(),
16977                        },
16978                    ],
16979                },
16980                UpgradeFromEntry {
16981                    from: "0.0.2".into(),
16982                    instructions: vec![
16983                        UpgradeInstruction::StateChange {
16984                            script: "servicos/upgrade.lisp".into(),
16985                        },
16986                        UpgradeInstruction::Purge {
16987                            module: "demo".into(),
16988                        },
16989                        UpgradeInstruction::Restart,
16990                    ],
16991                },
16992            ],
16993            vec![
16994                UpgradeFromEntry {
16995                    from: "0.1.0".into(),
16996                    instructions: vec![UpgradeInstruction::Restart],
16997                },
16998                UpgradeFromEntry {
16999                    from: "0.1.0".into(),
17000                    instructions: vec![UpgradeInstruction::Restart],
17001                },
17002            ],
17003        ];
17004        for upgrade_from in fixtures {
17005            let c = caixa_with_upgrade_from(upgrade_from.clone());
17006            assert_eq!(
17007                c.upgrade_from(),
17008                upgrade_from.as_slice(),
17009                "Caixa::upgrade_from must return :upgrade-from \
17010                 verbatim (got {:?}, expected {upgrade_from:?})",
17011                c.upgrade_from(),
17012            );
17013            assert_eq!(
17014                c.upgrade_from(),
17015                c.upgrade_from.as_slice(),
17016                "Caixa::upgrade_from must element-equal the raw \
17017                 `self.upgrade_from.as_slice()` field access across \
17018                 every value in the Vec<UpgradeFromEntry> accept-set",
17019            );
17020            assert_eq!(
17021                c.upgrade_from().is_empty(),
17022                c.upgrade_from.is_empty(),
17023                "Caixa::upgrade_from().is_empty() must byte-equal \
17024                 self.upgrade_from.is_empty() — a presence-bit drift \
17025                 would silently split the paired \
17026                 Caixa::declared_servico_slots M2 declared-slot \
17027                 enumerator's presence probe from the peer \
17028                 crate::render::servico_m2_overlay M2 overlay \
17029                 emitter's presence gate",
17030            );
17031        }
17032    }
17033
17034    #[test]
17035    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
17036        // Composition pin: [`Caixa::declared_servico_slots`]'s
17037        // `:upgrade-from` presence-probe arm must key off
17038        // [`Caixa::upgrade_from`], not the raw
17039        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
17040        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
17041        // instructions: vec![Restart] }], .. }` must push
17042        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
17043        // (the presence bit is non-empty, so the M2 kind-coherence
17044        // gate must surface the slot as "declared"), and a `Caixa {
17045        // upgrade_from: vec![], .. }` must NOT push the label (the
17046        // "author omitted the slot entirely" arm — the empty-slice
17047        // partition the serde-default folds onto). The pair jointly
17048        // pins the accessor + declared-slot enumerator composition:
17049        // any future silent detour that had the accessor collapse
17050        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
17051        // is_empty())` projection) would silently absorb the
17052        // "declared but degenerate" arm at the accessor boundary and
17053        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
17054        // coherence gate would silently accept a struct-literal
17055        // `Caixa` carrying the drift.
17056        //
17057        // Peer of the sibling
17058        // `declared_servico_slots_limits_arm_routes_through_accessor`
17059        // (b2bd9d7) and
17060        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17061        // (35d8b52) composition pins on the sibling `:limits` /
17062        // `:behavior` outer-`Option<&Composite>` arms — same "the
17063        // enumerator gate must route through the substrate-primitive
17064        // typed dispatch" discipline extended onto the third M2
17065        // Servico-runtime slot axis, closing the enumerator's routing
17066        // invariant on every M2 arm.
17067        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17068        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
17069            from: "0.0.1".into(),
17070            instructions: vec![UpgradeInstruction::Restart],
17071        }]);
17072        let slots = c.declared_servico_slots();
17073        assert!(
17074            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
17075            "declared_servico_slots must push \
17076             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
17077             non-empty — the accessor and the enumerator gate must \
17078             route through the same substrate-primitive typed \
17079             dispatch on the outer :upgrade-from presence bit (got \
17080             slots={slots:?})",
17081        );
17082        let c = caixa_with_upgrade_from(vec![]);
17083        let slots = c.declared_servico_slots();
17084        assert!(
17085            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
17086            "declared_servico_slots must NOT push \
17087             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
17088             empty — the author-omitted arm must route through the \
17089             accessor's empty-slice return unchanged (got \
17090             slots={slots:?})",
17091        );
17092    }
17093
17094    #[test]
17095    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
17096        // Composition pin: [`crate::render::servico_m2_overlay`]'s
17097        // per-`:upgrade-from` M2 overlay emit arm must key off
17098        // [`Caixa::upgrade_from`], not the raw
17099        // `!caixa.upgrade_from.is_empty()` presence gate + the
17100        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
17101        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
17102        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
17103        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
17104        // sequence in the overlay (the emitter fans onto the serde
17105        // slice-serialization), and a `Caixa { upgrade_from: vec![],
17106        // .. }` must omit the key entirely (the empty-slice
17107        // partition — the `!.is_empty()` outer gate elides the key
17108        // when the author omitted the slot). The pair jointly pins
17109        // the accessor + M2 overlay emitter composition: any future
17110        // silent detour that had the accessor return a fresh-cloned
17111        // `Vec<UpgradeFromEntry>` copy would silently break the
17112        // reference-identity pin the peer per-entry
17113        // `serde_yaml::to_value(caixa.upgrade_from())` projection
17114        // reads from — the projection would clone once per accessor
17115        // call instead of borrowing the storage buffer verbatim.
17116        //
17117        // Peer of the sibling
17118        // `servico_m2_overlay_limits_arm_routes_through_accessor`
17119        // (b2bd9d7) and
17120        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
17121        // (35d8b52) composition pins on the sibling `:limits` /
17122        // `:behavior` outer-`Option<&Composite>` arms — same "the
17123        // M2 overlay emitter must route through the substrate-
17124        // primitive typed dispatch" discipline extended onto the
17125        // third M2 Servico-runtime slot axis, closing the overlay
17126        // emitter's routing invariant on every M2 arm.
17127        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
17128        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17129        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
17130            from: "0.0.1".into(),
17131            instructions: vec![UpgradeInstruction::Restart],
17132        }]);
17133        let overlay = servico_m2_overlay(&c).unwrap();
17134        assert!(
17135            overlay.contains_key(M2_KEY_UPGRADE_FROM),
17136            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
17137             `:upgrade-from` is non-empty — the accessor and the M2 \
17138             overlay emitter must route through the same substrate- \
17139             primitive typed dispatch on the outer :upgrade-from \
17140             slice (got overlay={overlay:?})",
17141        );
17142        let c = caixa_with_upgrade_from(vec![]);
17143        let overlay = servico_m2_overlay(&c).unwrap();
17144        assert!(
17145            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
17146            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
17147             `:upgrade-from` is empty — the empty-slice partition \
17148             must route through the accessor's empty-slice return \
17149             unchanged (got overlay={overlay:?})",
17150        );
17151    }
17152
17153    #[test]
17154    fn upgrade_from_projects_slice_by_borrow() {
17155        // The by-borrow pin: [`Caixa::upgrade_from`] returns
17156        // `&[UpgradeFromEntry]` by borrow — the returned slice
17157        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
17158        // the `:upgrade-from` slot and the accessor must not clone
17159        // the backing `Vec` on every call. Peer of the sibling
17160        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
17161        // (`autores_projects_slice_by_borrow` b5d813f,
17162        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17163        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17164        // `exe_projects_slice_by_borrow` 65d9527,
17165        // `servicos_projects_slice_by_borrow` 611f78b,
17166        // `deps_projects_slice_by_borrow` ad34b4e,
17167        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
17168        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
17169        // axes — extended here to the first outer-`Caixa`
17170        // composite-element `&[Composite]` axis: the accessor's
17171        // returned slice must borrow from `&self` (the returned
17172        // reference's lifetime is tied to `&self`), and calling the
17173        // accessor twice on the same [`Caixa`] must yield slices
17174        // that are pointer-equal (the underlying byte-buffer is the
17175        // storage `Vec`'s allocation, not a fresh copy) as well as
17176        // value-equal (idempotent, no side effects on `&self`).
17177        //
17178        // Pins against a future silent detour that returned an owned
17179        // `Vec<UpgradeFromEntry>` (which would type-check but
17180        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
17181        // return (which would leak the backing `Vec`'s
17182        // grow/push/reserve surface no downstream consumer reaches
17183        // for), or a one-arm-only accessor that returned a
17184        // saturating value on some sentinel input.
17185        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17186        for upgrade_from in [
17187            vec![],
17188            vec![UpgradeFromEntry {
17189                from: "0.0.1".into(),
17190                instructions: vec![UpgradeInstruction::Restart],
17191            }],
17192            vec![
17193                UpgradeFromEntry {
17194                    from: "0.0.1".into(),
17195                    instructions: vec![UpgradeInstruction::Restart],
17196                },
17197                UpgradeFromEntry {
17198                    from: "0.0.2".into(),
17199                    instructions: vec![UpgradeInstruction::SoftPurge {
17200                        module: "demo".into(),
17201                    }],
17202                },
17203            ],
17204        ] {
17205            let c = caixa_with_upgrade_from(upgrade_from.clone());
17206            let first = c.upgrade_from();
17207            let second = c.upgrade_from();
17208            assert_eq!(
17209                first, second,
17210                "Caixa::upgrade_from must be idempotent — two \
17211                 successive calls on the same &self must return the \
17212                 same &[UpgradeFromEntry]",
17213            );
17214            assert_eq!(
17215                first.as_ptr(),
17216                second.as_ptr(),
17217                "Caixa::upgrade_from must borrow the underlying \
17218                 Vec<UpgradeFromEntry> storage — two successive calls \
17219                 must return slices with the same backing pointer (a \
17220                 fresh Vec<UpgradeFromEntry> clone would change the \
17221                 pointer on every call)",
17222            );
17223            assert_eq!(
17224                first,
17225                upgrade_from.as_slice(),
17226                "Caixa::upgrade_from must return :upgrade-from \
17227                 verbatim by borrow — got {first:?}, expected \
17228                 {upgrade_from:?}",
17229            );
17230        }
17231    }
17232
17233    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
17234
17235    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
17236        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17237        c.children = children;
17238        c
17239    }
17240
17241    #[test]
17242    fn children_returns_children_slice_verbatim_across_permutations() {
17243        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
17244        // outer-composite `&[ChildSpec]`-return slice-shape pin:
17245        // [`Caixa::children`] must return the `:children` typed
17246        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
17247        // the same backing buffer the raw `self.children.as_slice()`
17248        // field access borrows from, element-equal across every
17249        // representative fixture in the accept-set — `[]` (the "no
17250        // static children declared" arm every non-`Supervisor`-kind
17251        // `defcaixa` carries by `#[serde(default)]` and every
17252        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
17253        // a canonical single-child `Permanent` fixture (the shape
17254        // most `OneForOne` supervisors carry — a single long-running
17255        // worker child), a canonical multi-child list carrying every
17256        // typed restart-policy variant (`Permanent` / `Transient` /
17257        // `Temporary`), and a past-the-guard sentinel — a duplicate
17258        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
17259        // ([`crate::SupervisorSpec::validate`] rejects through
17260        // `DuplicateChildNome { nome: "w" }` but the accessor must
17261        // ship the raw slot verbatim so struct-literal fixtures
17262        // continue to expose the duplicate at the accessor boundary).
17263        //
17264        // Pins against a future silent detour that returned an owned
17265        // `Vec<ChildSpec>` (which would type-check but silently clone
17266        // on every accessor call, breaking the zero-cost projection
17267        // every peer sibling slice accessor carries), a `[dup, dup] →
17268        // [dup]` dedup collapse (which would silently absorb the
17269        // `DuplicateChildNome` refusal case at the accessor boundary
17270        // and the [`crate::StandardLayout::verify`] cross-child gate
17271        // would silently accept a struct-literal `Caixa` carrying the
17272        // drift), a reference to an operator-resolved overlay (the
17273        // future per-cluster `:children-overrides` slot — its
17274        // resolution must land at exactly this accessor body, not
17275        // silently divert the raw slot away from a second consumer),
17276        // or an axis-shuffled projection (a future detour that
17277        // reordered children through the accessor would silently
17278        // split the paired [`crate::StandardLayout::verify`] per-
17279        // supervisor gate's traversal input from the peer
17280        // [`Self::supervisor_view`] fold-in path's clone-order input,
17281        // since the OTP `RestForOne` restart strategy dispatches on
17282        // declared child order and axis reordering would silently
17283        // split the operator's per-cluster restart-fan-out order
17284        // from the caixa.lisp source-order).
17285        //
17286        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
17287        // accessor pin on the substrate primitive for M2 / M3 typed-
17288        // slot vec-carry axes — folds on the outer-`Caixa`
17289        // `&[Composite]` composite-slice sub-family the sibling
17290        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17291        // (2a1f907) pin opened, peer at the outer altitude of the
17292        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
17293        // (bc92bce) accessor on the same OTP-supervisor static-child-
17294        // list axis.
17295        use crate::supervisor::{ChildSpec, RestartPolicy};
17296        let fixtures: Vec<Vec<ChildSpec>> = vec![
17297            vec![],
17298            vec![ChildSpec {
17299                caixa: "worker".into(),
17300                versao: "^0.1".into(),
17301                restart: RestartPolicy::Permanent,
17302            }],
17303            vec![
17304                ChildSpec {
17305                    caixa: "worker-a".into(),
17306                    versao: "^0.1".into(),
17307                    restart: RestartPolicy::Permanent,
17308                },
17309                ChildSpec {
17310                    caixa: "worker-b".into(),
17311                    versao: "^0.1".into(),
17312                    restart: RestartPolicy::Transient,
17313                },
17314                ChildSpec {
17315                    caixa: "worker-c".into(),
17316                    versao: "^0.1".into(),
17317                    restart: RestartPolicy::Temporary,
17318                },
17319            ],
17320            vec![
17321                ChildSpec {
17322                    caixa: "w".into(),
17323                    versao: "^0.1".into(),
17324                    restart: RestartPolicy::Permanent,
17325                },
17326                ChildSpec {
17327                    caixa: "w".into(),
17328                    versao: "^0.1".into(),
17329                    restart: RestartPolicy::Permanent,
17330                },
17331            ],
17332        ];
17333        for children in fixtures {
17334            let c = caixa_with_children(children.clone());
17335            assert_eq!(
17336                c.children(),
17337                children.as_slice(),
17338                "Caixa::children must return :children verbatim \
17339                 (got {:?}, expected {children:?})",
17340                c.children(),
17341            );
17342            assert_eq!(
17343                c.children(),
17344                c.children.as_slice(),
17345                "Caixa::children must element-equal the raw \
17346                 `self.children.as_slice()` field access across \
17347                 every value in the Vec<ChildSpec> accept-set",
17348            );
17349            assert_eq!(
17350                c.children().is_empty(),
17351                c.children.is_empty(),
17352                "Caixa::children().is_empty() must byte-equal \
17353                 self.children.is_empty() — a presence-bit drift \
17354                 would silently split the paired \
17355                 Caixa::declared_supervisor_slots supervisor-tree \
17356                 declared-slot enumerator's presence probe from the \
17357                 peer Caixa::supervisor_view typed-view composer's \
17358                 fold-in path",
17359            );
17360        }
17361    }
17362
17363    #[test]
17364    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
17365        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17366        // `:children` presence-probe arm must key off
17367        // [`Caixa::children`], not the raw
17368        // `!self.children.is_empty()` field-probe. Structurally: a
17369        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
17370        // "^0.1", restart: Permanent }], .. }` must push
17371        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
17372        // (the presence bit is non-empty, so the supervisor-tree
17373        // kind-coherence gate must surface the slot as "declared"),
17374        // and a `Caixa { children: vec![], .. }` must NOT push the
17375        // label (the "author omitted the slot entirely" arm — the
17376        // empty-slice partition the serde-default folds onto). The
17377        // pair jointly pins the accessor + declared-slot enumerator
17378        // composition: any future silent detour that had the accessor
17379        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
17380        // "__reserved__")` projection) would silently absorb the
17381        // "declared but degenerate" arm at the accessor boundary and
17382        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17383        // kind-coherence gate would silently accept a struct-literal
17384        // `Caixa` carrying the drift.
17385        //
17386        // Peer of the sibling
17387        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17388        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
17389        // same "the enumerator gate must route through the substrate-
17390        // primitive typed dispatch" discipline extended onto the
17391        // supervisor-tree `:children` composite-slice arm.
17392        use crate::supervisor::{ChildSpec, RestartPolicy};
17393        let c = caixa_with_children(vec![ChildSpec {
17394            caixa: "w".into(),
17395            versao: "^0.1".into(),
17396            restart: RestartPolicy::Permanent,
17397        }]);
17398        let slots = c.declared_supervisor_slots();
17399        assert!(
17400            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
17401            "declared_supervisor_slots must push \
17402             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17403             non-empty — the accessor and the enumerator gate must \
17404             route through the same substrate-primitive typed \
17405             dispatch on the outer :children presence bit (got \
17406             slots={slots:?})",
17407        );
17408        let c = caixa_with_children(vec![]);
17409        let slots = c.declared_supervisor_slots();
17410        assert!(
17411            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
17412            "declared_supervisor_slots must NOT push \
17413             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17414             empty — the author-omitted arm must route through the \
17415             accessor's empty-slice return unchanged (got \
17416             slots={slots:?})",
17417        );
17418    }
17419
17420    #[test]
17421    fn supervisor_view_children_arm_routes_through_accessor() {
17422        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
17423        // fold-in arm must key off [`Caixa::children`], not the raw
17424        // `self.children.clone()` field-clone. Structurally: a `Caixa {
17425        // kind: Supervisor, estrategia: Some(OneForOne), children:
17426        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
17427        // per-child list through the accessor into the typed
17428        // [`SupervisorSpec`] view's `children` field verbatim — every
17429        // entry the accessor surfaces must land in the view's
17430        // `children` slot in the same order. The pair jointly pins the
17431        // accessor + view-composer composition: any future silent
17432        // detour that had the accessor return a fresh-cloned
17433        // `Vec<ChildSpec>` copy would silently break the reference-
17434        // identity pin the peer `supervisor_view` fold-in path reads
17435        // from — the fold would clone once more per accessor call
17436        // instead of borrowing the storage buffer verbatim once.
17437        //
17438        // Peer of the sibling
17439        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
17440        // family) composition pin on the peer kind-gate arm — same
17441        // "the view composer must route through the substrate-
17442        // primitive typed dispatch" discipline extended onto the
17443        // per-`:children` fold-in arm, closing the supervisor-view
17444        // composer's routing invariant on the composite-slice input.
17445        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17446        let mut c = caixa_with_children(vec![
17447            ChildSpec {
17448                caixa: "worker-a".into(),
17449                versao: "^0.1".into(),
17450                restart: RestartPolicy::Permanent,
17451            },
17452            ChildSpec {
17453                caixa: "worker-b".into(),
17454                versao: "^0.1".into(),
17455                restart: RestartPolicy::Transient,
17456            },
17457        ]);
17458        c.kind = crate::CaixaKind::Supervisor;
17459        c.estrategia = Some(RestartStrategy::OneForOne);
17460        let view = c
17461            .supervisor_view()
17462            .expect("Supervisor kind must produce a supervisor_view");
17463        assert_eq!(
17464            view.children(),
17465            c.children(),
17466            "supervisor_view must fold Caixa::children verbatim into \
17467             SupervisorSpec::children — the accessor and the view \
17468             composer must route through the same substrate-primitive \
17469             typed dispatch on the outer :children slice (got view \
17470             children={:?}, expected {:?})",
17471            view.children(),
17472            c.children(),
17473        );
17474    }
17475
17476    #[test]
17477    fn children_projects_slice_by_borrow() {
17478        // The by-borrow pin: [`Caixa::children`] returns
17479        // `&[ChildSpec]` by borrow — the returned slice borrows the
17480        // underlying `Vec<ChildSpec>` storage of the `:children` slot
17481        // and the accessor must not clone the backing `Vec` on every
17482        // call. Peer of the sibling outer top-level [`Caixa`]
17483        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
17484        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
17485        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17486        // `exe_projects_slice_by_borrow` 65d9527,
17487        // `servicos_projects_slice_by_borrow` 611f78b,
17488        // `deps_projects_slice_by_borrow` ad34b4e,
17489        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17490        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
17491        // sibling outer top-level [`Caixa`] scalar-element and
17492        // composite-element `&[T]` axes — folds on the outer-`Caixa`
17493        // composite-element `&[Composite]` axis: the accessor's
17494        // returned slice must borrow from `&self` (the returned
17495        // reference's lifetime is tied to `&self`), and calling the
17496        // accessor twice on the same [`Caixa`] must yield slices
17497        // that are pointer-equal (the underlying byte-buffer is the
17498        // storage `Vec`'s allocation, not a fresh copy) as well as
17499        // value-equal (idempotent, no side effects on `&self`).
17500        //
17501        // Pins against a future silent detour that returned an owned
17502        // `Vec<ChildSpec>` (which would type-check but silently clone
17503        // on every call), a `&Vec<ChildSpec>` return (which would leak
17504        // the backing `Vec`'s grow/push/reserve surface no downstream
17505        // consumer reaches for), or a one-arm-only accessor that
17506        // returned a saturating value on some sentinel input.
17507        use crate::supervisor::{ChildSpec, RestartPolicy};
17508        for children in [
17509            vec![],
17510            vec![ChildSpec {
17511                caixa: "w".into(),
17512                versao: "^0.1".into(),
17513                restart: RestartPolicy::Permanent,
17514            }],
17515            vec![
17516                ChildSpec {
17517                    caixa: "worker-a".into(),
17518                    versao: "^0.1".into(),
17519                    restart: RestartPolicy::Permanent,
17520                },
17521                ChildSpec {
17522                    caixa: "worker-b".into(),
17523                    versao: "^0.1".into(),
17524                    restart: RestartPolicy::Transient,
17525                },
17526            ],
17527        ] {
17528            let c = caixa_with_children(children.clone());
17529            let first = c.children();
17530            let second = c.children();
17531            assert_eq!(
17532                first, second,
17533                "Caixa::children must be idempotent — two successive \
17534                 calls on the same &self must return the same \
17535                 &[ChildSpec]",
17536            );
17537            assert_eq!(
17538                first.as_ptr(),
17539                second.as_ptr(),
17540                "Caixa::children must borrow the underlying \
17541                 Vec<ChildSpec> storage — two successive calls must \
17542                 return slices with the same backing pointer (a fresh \
17543                 Vec<ChildSpec> clone would change the pointer on \
17544                 every call)",
17545            );
17546            assert_eq!(
17547                first,
17548                children.as_slice(),
17549                "Caixa::children must return :children verbatim by \
17550                 borrow — got {first:?}, expected {children:?}",
17551            );
17552        }
17553    }
17554
17555    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
17556
17557    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
17558        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17559        c.kind = CaixaKind::Aplicacao;
17560        c.membros = membros;
17561        c
17562    }
17563
17564    #[test]
17565    fn membros_returns_membros_slice_verbatim_across_permutations() {
17566        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
17567        // composite `&[Membro]`-return slice-shape pin:
17568        // [`Caixa::membros`] must return the `:membros` typed
17569        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
17570        // same backing buffer the raw `self.membros.as_slice()` field
17571        // access borrows from, element-equal across every
17572        // representative fixture in the accept-set — `[]` (the "no
17573        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
17574        // carries by `#[serde(default)]` and every partially-authored
17575        // Aplicacao carries before the
17576        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
17577        // canonical single-member fixture (the shape a minimal
17578        // Aplicacao carries — one Servico wrapping one contained
17579        // computation), a canonical multi-member list carrying three
17580        // distinct entries (the canonical checkout-shape Aplicacao —
17581        // cart / pricing / auth — every canonical example carries), and
17582        // a past-the-guard sentinel — a duplicate `:caixa`
17583        // `[("cart", ...), ("cart", ...)]` entry pair
17584        // ([`crate::AplicacaoSpec::validate`] rejects through
17585        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
17586        // the raw slot verbatim so struct-literal fixtures continue to
17587        // expose the duplicate at the accessor boundary).
17588        //
17589        // Pins against a future silent detour that returned an owned
17590        // `Vec<Membro>` (which would type-check but silently clone on
17591        // every accessor call, breaking the zero-cost projection every
17592        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
17593        // dedup collapse (which would silently absorb the
17594        // `DuplicateMembro` refusal case at the accessor boundary and
17595        // the [`crate::StandardLayout::verify`] cross-member gate would
17596        // silently accept a struct-literal `Caixa` carrying the drift),
17597        // a reference to an operator-resolved overlay (the future per-
17598        // cluster `:membros-overrides` slot — its resolution must land
17599        // at exactly this accessor body, not silently divert the raw
17600        // slot away from a second consumer), or an axis-shuffled
17601        // projection (a future detour that reordered members through
17602        // the accessor would silently split the paired
17603        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
17604        // traversal input from the peer [`Self::aplicacao_view`] fold-
17605        // in path's clone-order input, since the canonical `:contratos`
17606        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
17607        // read the member set through the same slice).
17608        //
17609        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
17610        // accessor pin on the substrate primitive for M2 / M3 typed-
17611        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
17612        // arm of the `&[Composite]` composite-slice sub-family the
17613        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17614        // (2a1f907) and
17615        // `children_returns_children_slice_verbatim_across_permutations`
17616        // (c17b51e) pins opened, peer at the outer altitude of the
17617        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
17618        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
17619        // list axis.
17620        use crate::aplicacao::Membro;
17621        let fixtures: Vec<Vec<Membro>> = vec![
17622            vec![],
17623            vec![Membro {
17624                caixa: "cart".into(),
17625                versao: "^0.1".into(),
17626            }],
17627            vec![
17628                Membro {
17629                    caixa: "cart".into(),
17630                    versao: "^0.1".into(),
17631                },
17632                Membro {
17633                    caixa: "pricing".into(),
17634                    versao: "^0.2".into(),
17635                },
17636                Membro {
17637                    caixa: "auth".into(),
17638                    versao: "^1.0".into(),
17639                },
17640            ],
17641            vec![
17642                Membro {
17643                    caixa: "cart".into(),
17644                    versao: "^0.1".into(),
17645                },
17646                Membro {
17647                    caixa: "cart".into(),
17648                    versao: "^0.1".into(),
17649                },
17650            ],
17651        ];
17652        for membros in fixtures {
17653            let c = caixa_aplicacao_with_membros(membros.clone());
17654            assert_eq!(
17655                c.membros(),
17656                membros.as_slice(),
17657                "Caixa::membros must return :membros verbatim \
17658                 (got {:?}, expected {membros:?})",
17659                c.membros(),
17660            );
17661            assert_eq!(
17662                c.membros(),
17663                c.membros.as_slice(),
17664                "Caixa::membros must element-equal the raw \
17665                 `self.membros.as_slice()` field access across every \
17666                 value in the Vec<Membro> accept-set",
17667            );
17668            assert_eq!(
17669                c.membros().is_empty(),
17670                c.membros.is_empty(),
17671                "Caixa::membros().is_empty() must byte-equal \
17672                 self.membros.is_empty() — a presence-bit drift would \
17673                 silently split the paired Caixa::declared_mesh_slots \
17674                 mesh declared-slot enumerator's presence probe from \
17675                 the peer Caixa::aplicacao_view typed-view composer's \
17676                 fold-in path",
17677            );
17678        }
17679    }
17680
17681    #[test]
17682    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
17683        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
17684        // presence-probe arm must key off [`Caixa::membros`], not the
17685        // raw `!self.membros.is_empty()` field-probe. Structurally: a
17686        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
17687        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
17688        // declared-slot list (the presence bit is non-empty, so the
17689        // mesh kind-coherence gate must surface the slot as
17690        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
17691        // push the label (the "author omitted the slot entirely" arm
17692        // — the empty-slice partition the serde-default folds onto).
17693        // The pair jointly pins the accessor + declared-slot
17694        // enumerator composition: any future silent detour that had
17695        // the accessor collapse `[Membro { .. }]` to `[]` (a
17696        // `.filter(|m| m.nome() != "__reserved__")` projection) would
17697        // silently absorb the "declared but degenerate" arm at the
17698        // accessor boundary and the
17699        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17700        // coherence gate would silently accept a struct-literal
17701        // `Caixa` carrying the drift.
17702        //
17703        // Peer of the sibling
17704        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17705        // (2a1f907) and
17706        // `declared_supervisor_slots_children_arm_routes_through_accessor`
17707        // (c17b51e) composition pins on the M2 `:upgrade-from` /
17708        // `:children` composite-slice arms — same "the enumerator gate
17709        // must route through the substrate-primitive typed dispatch"
17710        // discipline extended onto the M3 `:membros` composite-slice
17711        // arm, opening the M3 arm of the declared-slot enumerator's
17712        // routing invariant.
17713        use crate::aplicacao::Membro;
17714        let c = caixa_aplicacao_with_membros(vec![Membro {
17715            caixa: "cart".into(),
17716            versao: "^0.1".into(),
17717        }]);
17718        let slots = c.declared_mesh_slots();
17719        assert!(
17720            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
17721            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
17722             `:membros` is non-empty — the accessor and the enumerator \
17723             gate must route through the same substrate-primitive \
17724             typed dispatch on the outer :membros presence bit (got \
17725             slots={slots:?})",
17726        );
17727        let c = caixa_aplicacao_with_membros(vec![]);
17728        let slots = c.declared_mesh_slots();
17729        assert!(
17730            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
17731            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
17732             when `:membros` is empty — the author-omitted arm must \
17733             route through the accessor's empty-slice return unchanged \
17734             (got slots={slots:?})",
17735        );
17736    }
17737
17738    #[test]
17739    fn aplicacao_view_membros_arm_routes_through_accessor() {
17740        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
17741        // fold-in arm must key off [`Caixa::membros`], not the raw
17742        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
17743        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
17744        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
17745        // member list through the accessor into the typed
17746        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
17747        // every entry the accessor surfaces must land in the view's
17748        // `membros` slot in the same order. The pair jointly pins the
17749        // accessor + view-composer composition: any future silent
17750        // detour that had the accessor return a fresh-cloned
17751        // `Vec<Membro>` copy would silently break the reference-
17752        // identity pin the peer `aplicacao_view` fold-in path reads
17753        // from — the fold would clone once more per accessor call
17754        // instead of borrowing the storage buffer verbatim once.
17755        //
17756        // Peer of the sibling
17757        // `aplicacao_view_politicas_arm_folds_through_accessor`
17758        // (5d23d29) /
17759        // `aplicacao_view_placement_arm_folds_through_accessor`
17760        // (4fb8074) /
17761        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
17762        // composition pins on the M3 `:politicas` / `:placement` /
17763        // `:entrada` outer-`Option<&Composite>` arms — extended here to
17764        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
17765        // closing the aplicacao-view composer's routing invariant on
17766        // the composite-slice input.
17767        use crate::aplicacao::Membro;
17768        let c = caixa_aplicacao_with_membros(vec![
17769            Membro {
17770                caixa: "cart".into(),
17771                versao: "^0.1".into(),
17772            },
17773            Membro {
17774                caixa: "pricing".into(),
17775                versao: "^0.2".into(),
17776            },
17777        ]);
17778        let view = c
17779            .aplicacao_view()
17780            .expect("Aplicacao kind must produce an aplicacao_view");
17781        assert_eq!(
17782            view.membros(),
17783            c.membros(),
17784            "aplicacao_view must fold Caixa::membros verbatim into \
17785             AplicacaoSpec::membros — the accessor and the view \
17786             composer must route through the same substrate-primitive \
17787             typed dispatch on the outer :membros slice (got view \
17788             membros={:?}, expected {:?})",
17789            view.membros(),
17790            c.membros(),
17791        );
17792    }
17793
17794    #[test]
17795    fn membros_projects_slice_by_borrow() {
17796        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
17797        // borrow — the returned slice borrows the underlying
17798        // `Vec<Membro>` storage of the `:membros` slot and the
17799        // accessor must not clone the backing `Vec` on every call.
17800        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
17801        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
17802        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17803        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17804        // `exe_projects_slice_by_borrow` 65d9527,
17805        // `servicos_projects_slice_by_borrow` 611f78b,
17806        // `deps_projects_slice_by_borrow` ad34b4e,
17807        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17808        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
17809        // `children_projects_slice_by_borrow` c17b51e) on the sibling
17810        // outer top-level [`Caixa`] scalar-element and composite-
17811        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
17812        // slot composite-element `&[Composite]` axis: the accessor's
17813        // returned slice must borrow from `&self` (the returned
17814        // reference's lifetime is tied to `&self`), and calling the
17815        // accessor twice on the same [`Caixa`] must yield slices that
17816        // are pointer-equal (the underlying byte-buffer is the storage
17817        // `Vec`'s allocation, not a fresh copy) as well as value-equal
17818        // (idempotent, no side effects on `&self`).
17819        //
17820        // Pins against a future silent detour that returned an owned
17821        // `Vec<Membro>` (which would type-check but silently clone on
17822        // every call), a `&Vec<Membro>` return (which would leak the
17823        // backing `Vec`'s grow/push/reserve surface no downstream
17824        // consumer reaches for), or a one-arm-only accessor that
17825        // returned a saturating value on some sentinel input.
17826        use crate::aplicacao::Membro;
17827        for membros in [
17828            vec![],
17829            vec![Membro {
17830                caixa: "cart".into(),
17831                versao: "^0.1".into(),
17832            }],
17833            vec![
17834                Membro {
17835                    caixa: "cart".into(),
17836                    versao: "^0.1".into(),
17837                },
17838                Membro {
17839                    caixa: "pricing".into(),
17840                    versao: "^0.2".into(),
17841                },
17842            ],
17843        ] {
17844            let c = caixa_aplicacao_with_membros(membros.clone());
17845            let first = c.membros();
17846            let second = c.membros();
17847            assert_eq!(
17848                first, second,
17849                "Caixa::membros must be idempotent — two successive \
17850                 calls on the same &self must return the same &[Membro]",
17851            );
17852            assert_eq!(
17853                first.as_ptr(),
17854                second.as_ptr(),
17855                "Caixa::membros must borrow the underlying Vec<Membro> \
17856                 storage — two successive calls must return slices with \
17857                 the same backing pointer (a fresh Vec<Membro> clone \
17858                 would change the pointer on every call)",
17859            );
17860            assert_eq!(
17861                first,
17862                membros.as_slice(),
17863                "Caixa::membros must return :membros verbatim by borrow \
17864                 — got {first:?}, expected {membros:?}",
17865            );
17866        }
17867    }
17868
17869    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
17870
17871    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
17872        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17873        c.kind = CaixaKind::Aplicacao;
17874        c.contratos = contratos;
17875        c
17876    }
17877
17878    fn contrato_http_for_test(
17879        de: &str,
17880        para: &str,
17881        endpoint: &str,
17882    ) -> crate::aplicacao::WitContract {
17883        crate::aplicacao::WitContract {
17884            de: de.into(),
17885            para: para.into(),
17886            wit: "wasi:http/proxy".into(),
17887            endpoint: Some(endpoint.into()),
17888            subject: None,
17889            slot: None,
17890        }
17891    }
17892
17893    #[test]
17894    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
17895        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
17896        // composite `&[WitContract]`-return slice-shape pin:
17897        // [`Caixa::contratos`] must return the `:contratos` typed
17898        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
17899        // over the same backing buffer the raw
17900        // `self.contratos.as_slice()` field access borrows from,
17901        // element-equal across every representative fixture in the
17902        // accept-set — `[]` (the "no contracts declared" arm every
17903        // non-`Aplicacao`-kind `defcaixa` carries by
17904        // `#[serde(default)]` and every leaf-Aplicacao with a single
17905        // member carries), a canonical single-edge fixture (the
17906        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
17907        // edge), and a canonical multi-edge fixture with three distinct
17908        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
17909        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
17910        //
17911        // Pins against a future silent detour that returned an owned
17912        // `Vec<WitContract>` (which would type-check but silently clone
17913        // on every accessor call, breaking the zero-cost projection
17914        // every peer sibling slice accessor carries), an axis-shuffled
17915        // projection (a future detour that reordered edges through the
17916        // accessor would silently split the paired
17917        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
17918        // traversal input from the peer [`Self::aplicacao_view`] fold-
17919        // in path's clone-order input, since every canonical
17920        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
17921        // seed dispatch reads the edge set through the same slice),
17922        // or a reference to an operator-resolved overlay (the future
17923        // per-cluster `:contratos-overrides` slot — its resolution
17924        // must land at exactly this accessor body, not silently divert
17925        // the raw slot away from a second consumer).
17926        //
17927        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
17928        // accessor pin on the substrate primitive for M2 / M3 typed-
17929        // slot vec-carry axes — closes the outer-`Caixa`
17930        // `&[Composite]` composite-slice sub-family the sibling M2
17931        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17932        // (2a1f907) and
17933        // `children_returns_children_slice_verbatim_across_permutations`
17934        // (c17b51e) pins opened and the M3
17935        // `membros_returns_membros_slice_verbatim_across_permutations`
17936        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
17937        // slot arm of the composite-slice sub-family. Peer at the outer
17938        // altitude of the closed inner-
17939        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
17940        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
17941        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
17942            vec![],
17943            vec![contrato_http_for_test("cart", "catalog", "/items")],
17944            vec![
17945                contrato_http_for_test("cart", "catalog", "/items"),
17946                contrato_http_for_test("cart", "pricing", "/price"),
17947                contrato_http_for_test("cart", "auth", "/whoami"),
17948            ],
17949        ];
17950        for contratos in fixtures {
17951            let c = caixa_aplicacao_with_contratos(contratos.clone());
17952            assert_eq!(
17953                c.contratos(),
17954                contratos.as_slice(),
17955                "Caixa::contratos must return :contratos verbatim \
17956                 (got {:?}, expected {contratos:?})",
17957                c.contratos(),
17958            );
17959            assert_eq!(
17960                c.contratos(),
17961                c.contratos.as_slice(),
17962                "Caixa::contratos must element-equal the raw \
17963                 `self.contratos.as_slice()` field access across every \
17964                 value in the Vec<WitContract> accept-set",
17965            );
17966            assert_eq!(
17967                c.contratos().is_empty(),
17968                c.contratos.is_empty(),
17969                "Caixa::contratos().is_empty() must byte-equal \
17970                 self.contratos.is_empty() — a presence-bit drift would \
17971                 silently split the paired Caixa::declared_mesh_slots \
17972                 mesh declared-slot enumerator's presence probe from \
17973                 the peer Caixa::aplicacao_view typed-view composer's \
17974                 fold-in path",
17975            );
17976        }
17977    }
17978
17979    #[test]
17980    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
17981        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
17982        // presence-probe arm must key off [`Caixa::contratos`], not the
17983        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
17984        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
17985        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
17986        // presence bit is non-empty, so the mesh kind-coherence gate
17987        // must surface the slot as "declared"), and a `Caixa {
17988        // contratos: vec![], .. }` must NOT push the label (the "author
17989        // omitted the slot entirely" arm — the empty-slice partition
17990        // the serde-default folds onto). The pair jointly pins the
17991        // accessor + declared-slot enumerator composition: any future
17992        // silent detour that had the accessor collapse
17993        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
17994        // "__reserved__")` projection) would silently absorb the
17995        // "declared but degenerate" arm at the accessor boundary and
17996        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17997        // coherence gate would silently accept a struct-literal
17998        // `Caixa` carrying the drift.
17999        //
18000        // Peer of the sibling
18001        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18002        // (2a1f907),
18003        // `declared_supervisor_slots_children_arm_routes_through_accessor`
18004        // (c17b51e), and
18005        // `declared_mesh_slots_membros_arm_routes_through_accessor`
18006        // (0f26987) composition pins on the M2 `:upgrade-from` /
18007        // `:children` / M3 `:membros` composite-slice arms — same "the
18008        // enumerator gate must route through the substrate-primitive
18009        // typed dispatch" discipline extended onto the M3 `:contratos`
18010        // composite-slice arm, closing the M3 mesh-slot arm of the
18011        // declared-slot enumerator's routing invariant on the
18012        // composite-slice inputs.
18013        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
18014            "cart", "catalog", "/items",
18015        )]);
18016        let slots = c.declared_mesh_slots();
18017        assert!(
18018            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
18019            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
18020             `:contratos` is non-empty — the accessor and the enumerator \
18021             gate must route through the same substrate-primitive \
18022             typed dispatch on the outer :contratos presence bit (got \
18023             slots={slots:?})",
18024        );
18025        let c = caixa_aplicacao_with_contratos(vec![]);
18026        let slots = c.declared_mesh_slots();
18027        assert!(
18028            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
18029            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
18030             when `:contratos` is empty — the author-omitted arm must \
18031             route through the accessor's empty-slice return unchanged \
18032             (got slots={slots:?})",
18033        );
18034    }
18035
18036    #[test]
18037    fn aplicacao_view_contratos_arm_routes_through_accessor() {
18038        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
18039        // fold-in arm must key off [`Caixa::contratos`], not the raw
18040        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
18041        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
18042        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
18043        // per-edge list through the accessor into the typed
18044        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
18045        // every entry the accessor surfaces must land in the view's
18046        // `contratos` slot in the same order. The pair jointly pins
18047        // the accessor + view-composer composition: a future silent
18048        // detour that had the accessor shuffle or drop an edge would
18049        // silently split the paired declared-slot enumerator's
18050        // presence bit from the typed-view composer's edge-list, a
18051        // two-consumer split at the enumerator and the view composer
18052        // far from the source `caixa.lisp`.
18053        //
18054        // Peer of the sibling
18055        // `aplicacao_view_membros_arm_routes_through_accessor`
18056        // (0f26987) composition pin on the M3 `:membros` outer-
18057        // `&[Composite]` composite-slice arm, closing the aplicacao-
18058        // view composer's routing invariant on the composite-slice
18059        // inputs at the outer altitude.
18060        let c = caixa_aplicacao_with_contratos(vec![
18061            contrato_http_for_test("cart", "catalog", "/items"),
18062            contrato_http_for_test("cart", "pricing", "/price"),
18063        ]);
18064        let view = c
18065            .aplicacao_view()
18066            .expect("Aplicacao kind must produce an aplicacao_view");
18067        assert_eq!(
18068            view.contratos(),
18069            c.contratos(),
18070            "aplicacao_view must fold Caixa::contratos verbatim into \
18071             AplicacaoSpec::contratos — the accessor and the view \
18072             composer must route through the same substrate-primitive \
18073             typed dispatch on the outer :contratos slice (got view \
18074             contratos={:?}, expected {:?})",
18075            view.contratos(),
18076            c.contratos(),
18077        );
18078    }
18079
18080    #[test]
18081    fn contratos_projects_slice_by_borrow() {
18082        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
18083        // by borrow — the returned slice borrows the underlying
18084        // `Vec<WitContract>` storage of the `:contratos` slot and the
18085        // accessor must not clone the backing `Vec` on every call.
18086        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
18087        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
18088        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18089        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18090        // `exe_projects_slice_by_borrow` 65d9527,
18091        // `servicos_projects_slice_by_borrow` 611f78b,
18092        // `deps_projects_slice_by_borrow` ad34b4e,
18093        // `deps_dev_projects_slice_by_borrow` f7fd81e,
18094        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
18095        // `children_projects_slice_by_borrow` c17b51e,
18096        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
18097        // outer top-level [`Caixa`] scalar-element and composite-
18098        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
18099        // composite-element `&[Composite]` axis on the by-borrow pin:
18100        // the accessor's returned slice must borrow from `&self` (the
18101        // returned reference's lifetime is tied to `&self`), and
18102        // calling the accessor twice on the same [`Caixa`] must yield
18103        // slices that are pointer-equal (the underlying byte-buffer is
18104        // the storage `Vec`'s allocation, not a fresh copy) as well as
18105        // value-equal (idempotent, no side effects on `&self`).
18106        //
18107        // Pins against a future silent detour that returned an owned
18108        // `Vec<WitContract>` (which would type-check but silently clone
18109        // on every call), a `&Vec<WitContract>` return (which would
18110        // leak the backing `Vec`'s grow/push/reserve surface no
18111        // downstream consumer reaches for), or a one-arm-only accessor
18112        // that returned a saturating value on some sentinel input.
18113        for contratos in [
18114            vec![],
18115            vec![contrato_http_for_test("cart", "catalog", "/items")],
18116            vec![
18117                contrato_http_for_test("cart", "catalog", "/items"),
18118                contrato_http_for_test("cart", "pricing", "/price"),
18119            ],
18120        ] {
18121            let c = caixa_aplicacao_with_contratos(contratos.clone());
18122            let first = c.contratos();
18123            let second = c.contratos();
18124            assert_eq!(
18125                first, second,
18126                "Caixa::contratos must be idempotent — two successive \
18127                 calls on the same &self must return the same \
18128                 &[WitContract]",
18129            );
18130            assert_eq!(
18131                first.as_ptr(),
18132                second.as_ptr(),
18133                "Caixa::contratos must borrow the underlying \
18134                 Vec<WitContract> storage — two successive calls must \
18135                 return slices with the same backing pointer (a fresh \
18136                 Vec<WitContract> clone would change the pointer on \
18137                 every call)",
18138            );
18139            assert_eq!(
18140                first,
18141                contratos.as_slice(),
18142                "Caixa::contratos must return :contratos verbatim by \
18143                 borrow — got {first:?}, expected {contratos:?}",
18144            );
18145        }
18146    }
18147
18148    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
18149
18150    #[test]
18151    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
18152        // Load-bearing invariant: every multi-word top-level [`Caixa`]
18153        // serde-derived JSON key routes through a lifted `&'static str`
18154        // const. The Rust field names are `snake_case`
18155        // (`deps_dev` / `upgrade_from` / `max_restarts` /
18156        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
18157        // "camelCase")]` derive attribute maps each to the camelCase
18158        // byte-string the [`Caixa::to_lisp`] round-trip's
18159        // `serde_json::to_value(self)` step lands under before
18160        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
18161        // to the kebab-case `:deps-dev` / `:upgrade-from` /
18162        // `:max-restarts` / `:restart-window` author surface. Serialize
18163        // a fully-populated [`Caixa`] and pin that each canonical
18164        // byte-sequence appears verbatim in the JSON — a future
18165        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
18166        // verbatim-field-name flip at the derive attribute (any of
18167        // which would silently break every [`Caixa::to_lisp`]
18168        // round-trip and the future M4 operator-side manifest ingest's
18169        // `Value::get(<key>)` navigation) surfaces here as a build-time
18170        // test failure at `manifest.rs`, not as an apply-time
18171        // `.get(<stale-canonical-const>)` returning `None` far from the
18172        // derive-attr drift's commit. Same discipline the sibling
18173        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
18174        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
18175        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
18176        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
18177        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
18178        // [`UpgradeFromEntry`] per-entry axes — extended here to the
18179        // enclosing M0 [`Caixa`] top-level axis so the last of the four
18180        // multi-word top-level [`Caixa`] serde-derived JSON keys
18181        // (`depsDev`) joins the substrate's "one canonical byte-string
18182        // per typed serialized-key axis" discipline.
18183        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
18184        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18185        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18186        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
18187        c.upgrade_from = vec![UpgradeFromEntry {
18188            from: "0.0.1".into(),
18189            instructions: vec![UpgradeInstruction::Restart],
18190        }];
18191        c.estrategia = Some(RestartStrategy::OneForOne);
18192        c.max_restarts = Some(3);
18193        c.restart_window = Some("60s".into());
18194        c.children = vec![ChildSpec {
18195            caixa: "child".into(),
18196            versao: "^0.1".into(),
18197            restart: RestartPolicy::Permanent,
18198        }];
18199        let json = serde_json::to_string(&c).unwrap();
18200        for key in [
18201            crate::render::CAIXA_KEY_DEPS_DEV,
18202            crate::render::M2_KEY_UPGRADE_FROM,
18203            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18204            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18205        ] {
18206            let quoted = format!("\"{key}\"");
18207            assert!(
18208                json.contains(&quoted),
18209                "serialized Caixa must carry the lifted top-level \
18210                 multi-word byte-sequence {quoted} verbatim in the JSON \
18211                 emission (got: {json})",
18212            );
18213        }
18214    }
18215
18216    #[test]
18217    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
18218        // Cross-axis drift-detection pin: a future collapse of the four
18219        // canonical [`Caixa`] top-level multi-word byte-strings onto the
18220        // same value (e.g. an accidental copy-paste flip of
18221        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
18222        // `"upgradeFrom"`) would silently reroute every downstream
18223        // `Value::get(<key>)` probe on one axis onto the sibling axis's
18224        // top-level entry and pass every propagation-probe test that
18225        // expected only the stale axis's value. Peer of the sibling
18226        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
18227        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
18228        let all = [
18229            crate::render::CAIXA_KEY_DEPS_DEV,
18230            crate::render::M2_KEY_UPGRADE_FROM,
18231            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18232            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18233        ];
18234        for (i, a) in all.iter().enumerate() {
18235            for b in all.iter().skip(i + 1) {
18236                assert_ne!(
18237                    a, b,
18238                    "Caixa top-level multi-word key consts must be \
18239                     pairwise-distinct canonical byte-sequences — got \
18240                     `{a}` == `{b}`",
18241                );
18242            }
18243        }
18244    }
18245
18246    #[test]
18247    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
18248        // Shape-pin: every [`Caixa`] top-level multi-word key const must
18249        // be a lowerCamelCase byte-sequence (no `snake_case`
18250        // underscores, no `kebab-case` hyphens, no leading colon, no
18251        // `PascalCase` leading capital, no whitespace / dots) — the
18252        // canonical shape the `#[serde(rename_all = "camelCase")]`
18253        // derive produces on [`Caixa`]. A future flip to a
18254        // non-camelCase attribute at the derive surfaces both here
18255        // (this test fails on the stale-constant shape) and at
18256        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18257        // (that test fails on the mismatch between const and derive).
18258        // Peer with `membro_key_consts_are_lower_camel_case_shape`
18259        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
18260        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
18261        for key in [
18262            crate::render::CAIXA_KEY_DEPS_DEV,
18263            crate::render::M2_KEY_UPGRADE_FROM,
18264            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18265            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18266        ] {
18267            assert!(
18268                !key.is_empty(),
18269                "Caixa top-level multi-word key const must be non-empty \
18270                 (got {key:?})"
18271            );
18272            let first = key.chars().next().unwrap();
18273            assert!(
18274                first.is_ascii_lowercase(),
18275                "Caixa top-level multi-word key const must lead with an \
18276                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
18277            );
18278            assert!(
18279                key.chars().all(|c| c.is_ascii_alphanumeric()),
18280                "Caixa top-level multi-word key const must be \
18281                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
18282                 whitespace (got {key:?})",
18283            );
18284        }
18285    }
18286
18287    #[test]
18288    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
18289        // Scalar-value pin: the byte-string the
18290        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
18291        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
18292        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
18293        // → `depsTest` matching a hypothetical per-test-target
18294        // vocabulary flip) lands as an edit to exactly one const AND
18295        // one derive attribute — the sibling
18296        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18297        // pin already ties the const to the derive attribute, so a
18298        // rebrand that touches only one side of the pair fails at
18299        // caixa-core build time. Same "scalar-value pin per const"
18300        // discipline the sibling
18301        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
18302        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
18303        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
18304        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
18305    }
18306
18307    #[test]
18308    fn caixa_key_deps_pins_canonical_byte_string() {
18309        // Scalar-value pin: the byte-string the
18310        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
18311        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
18312        // on the two-list dep-graph serialized-key axis — the sibling
18313        // pin covers the multi-word `deps_dev → depsDev` camelCase
18314        // arm, this pin covers the single-word `deps → deps` no-op arm
18315        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
18316        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
18317        // axis and the emitted JSON key equals the source-side field
18318        // name byte-for-byte). A future [`crate::Caixa::deps`] field
18319        // rename (`deps` → `dependencies` matching Cargo's verbatim
18320        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
18321        // hypothetical per-runtime-target vocabulary flip) OR an added
18322        // `#[serde(rename = "…")]` explicit override lands as an edit
18323        // to exactly one const AND one derive-attr / field name — the
18324        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
18325        // pin ties the const to the emitted JSON key, so a rebrand
18326        // that touches only one side of the pair fails at caixa-core
18327        // build time.
18328        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
18329    }
18330
18331    #[test]
18332    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
18333        // Load-bearing invariant on the single-word `deps` top-level
18334        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
18335        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
18336        // `serde_json::to_value(self)` step emits. Serialize a
18337        // populated [`Caixa`] whose `:deps` slot carries at least one
18338        // entry (the `#[serde(default)]` attribute on the field emits
18339        // an empty `[]` even without members, but a non-empty vec
18340        // additionally covers the codec's per-`Dep`-entry emission
18341        // path) and pin that `"deps"` appears verbatim in the JSON
18342        // emission — a future accidental `rename_all = "snake_case"` /
18343        // `"kebab-case"` flip at the derive attribute (or an added
18344        // `#[serde(rename = "…")]` explicit override on the field, or
18345        // a Rust field rename) would break every [`Caixa::to_lisp`]
18346        // round-trip and the future M4 operator-side manifest ingest's
18347        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
18348        // build-time test failure at `manifest.rs`, not as an
18349        // apply-time `.get(<stale-canonical-const>)` returning `None`
18350        // far from the drift's commit. Peer of the sibling
18351        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18352        // multi-word pin on the same M0 [`Caixa`] top-level
18353        // serialized-key axis, extended here to the single-word arm
18354        // the multi-word test's `rename_all = "camelCase"` sweep can't
18355        // reach (single-word `deps → deps` is a no-op the multi-word
18356        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
18357        // `\"restartWindow\"` byte-scan can never observe).
18358        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18359        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
18360        let json = serde_json::to_string(&c).unwrap();
18361        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
18362        assert!(
18363            json.contains(&quoted),
18364            "serialized Caixa must carry the lifted top-level `deps` \
18365             byte-sequence {quoted} verbatim in the JSON emission (got: \
18366             {json})",
18367        );
18368    }
18369
18370    #[test]
18371    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
18372        // Cross-axis drift-detection pin on the two-list dep-graph
18373        // renderer-side wire-key axis: a future collapse of the
18374        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
18375        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
18376        // same value (e.g. an accidental copy-paste flip of
18377        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
18378        // reroute every downstream `Value::get(<key>)` probe on one
18379        // axis onto the sibling axis's dep-list and pass every
18380        // propagation-probe test that expected only the stale axis's
18381        // value — a dev-only dep would land in the runtime closure at
18382        // publish time, or a runtime dep would be excluded from the
18383        // published lacre. Peer of the sibling four-way distinct pin
18384        // on the top-level multi-word tetrad
18385        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
18386        // and the two-way pin on the sibling
18387        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
18388        // author-facing arm (4da6fba's test), extended here to the
18389        // renderer-side wire-key arm of the same two-list dep-graph
18390        // axis so both halves of the "one canonical byte-string per
18391        // typed axis per (author, wire)" grid carry the same
18392        // distinct-ness discipline.
18393        assert_ne!(
18394            crate::render::CAIXA_KEY_DEPS,
18395            crate::render::CAIXA_KEY_DEPS_DEV,
18396            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
18397             canonical byte-sequences on the two-list dep-graph \
18398             renderer-side wire-key axis"
18399        );
18400    }
18401
18402    // ── DepList / Caixa::push_dep pin ────────────────────────────────
18403    //
18404    // The compounding pin: the two-arm closed-set typed enum
18405    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
18406    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
18407    // consumer of the top-level manifest's dep-mutation surface reads
18408    // through, and the typed dispatch [`Caixa::push_dep`] on the
18409    // substrate primitive folds the "select list → check within-list
18410    // dup → push" cascade onto one method call. Prior to this landing
18411    // the two axes lived across two `&'static str` constants
18412    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
18413    // set type carrying the pair; the `feira add` mutation site's
18414    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
18415    // caixa.deps }` dispatch expressed no compile-time link back to
18416    // the substrate primitive, and a future third dep-list axis would
18417    // have silently split at every open-coded mutation site.
18418
18419    #[test]
18420    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
18421        // Every arm returns the same `&'static str` the substrate's
18422        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
18423        // constants carry. A future rebrand on either constant reaches
18424        // the enum through one edit; a regression to inline literals
18425        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
18426        // quotes from the wire-format constants every consumer routes
18427        // through and this pin flags it at build time.
18428        assert_eq!(
18429            crate::dep::DepList::Prod.as_str(),
18430            crate::render::DEP_AUTHOR_KEY_DEPS
18431        );
18432        assert_eq!(
18433            crate::dep::DepList::Dev.as_str(),
18434            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
18435        );
18436    }
18437
18438    #[test]
18439    fn dep_list_display_routes_through_as_str() {
18440        // Same as-str-through-Display convergence discipline the
18441        // sibling closed-set typed enums carry — a `format!("{list}")`
18442        // call must land byte-for-byte on the accessor's return so a
18443        // future consumer that formats the enum for a diagnostic line
18444        // reaches the same wire-format constant the wire-format
18445        // producers do.
18446        assert_eq!(
18447            format!("{}", crate::dep::DepList::Prod),
18448            crate::dep::DepList::Prod.as_str()
18449        );
18450        assert_eq!(
18451            format!("{}", crate::dep::DepList::Dev),
18452            crate::dep::DepList::Dev.as_str()
18453        );
18454    }
18455
18456    #[test]
18457    fn dep_list_all_enumerates_every_variant_once() {
18458        // Exhaustive-iteration pin — every arm appears exactly once in
18459        // `ALL`, matching the closed set the compiler enforces on the
18460        // sibling `match self` arms. A future variant addition that
18461        // extends only one method's match without extending `ALL`
18462        // would silently drop the new arm from every consumer that
18463        // iterates the slice.
18464        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
18465        assert!(variants.contains(&crate::dep::DepList::Prod));
18466        assert!(variants.contains(&crate::dep::DepList::Dev));
18467        assert_eq!(variants.len(), 2);
18468    }
18469
18470    #[test]
18471    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
18472        // Reverse projection on the two-list dep-graph axis: the
18473        // author-surface wire tag the sibling `as_str` emitter walks
18474        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
18475        // `Some(DepList::Prod)`. A regression that hand-rolled the
18476        // per-arm match without routing through the lifted
18477        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
18478        // future wire-tag rebrand and this pin flags it at build time.
18479        assert_eq!(
18480            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
18481            Some(crate::dep::DepList::Prod)
18482        );
18483    }
18484
18485    #[test]
18486    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
18487        // Peer of the `Prod`-arm pin on the dev-only axis: the
18488        // author-surface wire tag the sibling `as_str` emitter walks
18489        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
18490        // back to `Some(DepList::Dev)`. Same drift-detection posture
18491        // as the peer arm — the sibling method `match` arms are
18492        // compiler-checked exhaustive so a future variant addition
18493        // trips at build time.
18494        assert_eq!(
18495            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
18496            Some(crate::dep::DepList::Dev)
18497        );
18498    }
18499
18500    #[test]
18501    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
18502        // Every input outside the closed-set arm-string set the
18503        // sibling `as_str` emitter walks lands on the terminal `None`
18504        // fallback — no silent-accept surface. Sweeps a set of
18505        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
18506        // rebrand candidates, foreign wire tags, empty string) so a
18507        // future variant addition that widened one wire form without
18508        // extending the emitter's arm-set would trip the sibling
18509        // round-trip pin below rather than silently accepting the new
18510        // form here.
18511        for candidate in [
18512            "",
18513            "deps",
18514            "deps-dev",
18515            ":deps ",
18516            ":Deps",
18517            ":DEPS",
18518            ":build-dep",
18519            ":tool-dep",
18520            "prod",
18521            "dev",
18522        ] {
18523            assert_eq!(
18524                crate::dep::DepList::from_wire(candidate),
18525                None,
18526                "from_wire({candidate:?}) must return None; every input outside \
18527                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
18528                 the sibling as_str emitter walks lands on the terminal fallback",
18529            );
18530        }
18531    }
18532
18533    #[test]
18534    fn dep_list_round_trips_through_as_str_and_from_wire() {
18535        // Load-bearing round-trip pin: every arm the `ALL` iteration
18536        // exposes survives the `as_str` → `from_wire` composition
18537        // byte-for-byte. Same discipline the sibling closed-set enums
18538        // carry — `CaixaKind` /
18539        // `RestartStrategy` / `RestartPolicy` /
18540        // `PlacementStrategy` — extended onto the two-list dep-graph
18541        // axis. A future variant addition that extends `ALL` +
18542        // `as_str` without extending `from_wire` (or vice versa)
18543        // trips at build time on this iteration because the compiler
18544        // enforces exhaustiveness on the sibling `match self` arms.
18545        for &list in crate::dep::DepList::ALL {
18546            assert_eq!(
18547                crate::dep::DepList::from_wire(list.as_str()),
18548                Some(list),
18549                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
18550                 a silent split between the forward emitter and the reverse parser \
18551                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
18552            );
18553        }
18554    }
18555
18556    #[test]
18557    fn push_dep_routes_to_deps_slot_on_prod_arm() {
18558        // The `Prod` arm dispatches to the runtime-closure `:deps`
18559        // slot every downstream lacre-pipeline consumer resolves at
18560        // build time. A future arm that regressed to inline `&mut
18561        // self.deps_dev` on the `Prod` path would silently reroute
18562        // every runtime dep into the dev-only closure at publish time
18563        // — this pin refuses that regression.
18564        let src = Caixa::template("host");
18565        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18566        let before_deps = caixa.deps().len();
18567        let before_deps_dev = caixa.deps_dev().len();
18568        let dep = Dep {
18569            nome: "caixa-teia".to_string(),
18570            versao: "^0.1".to_string(),
18571            fonte: None,
18572            opcional: false,
18573            caracteristicas: Vec::new(),
18574        };
18575        caixa
18576            .push_dep(crate::dep::DepList::Prod, dep)
18577            .expect("first push into :deps succeeds");
18578        assert_eq!(caixa.deps().len(), before_deps + 1);
18579        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
18580        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
18581    }
18582
18583    #[test]
18584    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
18585        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
18586        // must dispatch to the dev-only-closure `:deps-dev` slot every
18587        // downstream test-facing artifact resolver reads. A future
18588        // regression that inverted the two arms would silently route
18589        // every dev-only dep into the runtime closure at publish time
18590        // and this pin catches it before the drift ships.
18591        let src = Caixa::template("host");
18592        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18593        let dep = Dep {
18594            nome: "tatara-check".to_string(),
18595            versao: "*".to_string(),
18596            fonte: None,
18597            opcional: false,
18598            caracteristicas: Vec::new(),
18599        };
18600        caixa
18601            .push_dep(crate::dep::DepList::Dev, dep)
18602            .expect("first push into :deps-dev succeeds");
18603        assert!(caixa.deps().is_empty());
18604        assert_eq!(caixa.deps_dev().len(), 1);
18605        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
18606    }
18607
18608    #[test]
18609    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
18610        // Within-list dup check routes through the canonical
18611        // [`DepError::DuplicateNome`] carrier — the substrate's typed
18612        // diagnostic for the same axis [`Caixa::validate_deps`]'s
18613        // parse-time [`crate::render::insert_first_seen`] walk raises
18614        // on. Prior to the lift the mutation site's inline
18615        // `bail!("dep '{}' already declared", …)` string-diagnostic
18616        // path expressed no through-line back to the typed error;
18617        // routing every dep-list refusal through one carrier means an
18618        // author reading a `feira add` refusal and a `feira build`
18619        // refusal reaches for the same corrective surface without
18620        // switching diagnostic idioms.
18621        let src = Caixa::template("host");
18622        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18623        let dep = Dep {
18624            nome: "caixa-teia".to_string(),
18625            versao: "^0.1".to_string(),
18626            fonte: None,
18627            opcional: false,
18628            caracteristicas: Vec::new(),
18629        };
18630        caixa
18631            .push_dep(crate::dep::DepList::Prod, dep.clone())
18632            .expect("first push succeeds");
18633        let dup = Dep {
18634            nome: "caixa-teia".to_string(),
18635            versao: "^0.2".to_string(),
18636            fonte: None,
18637            opcional: false,
18638            caracteristicas: Vec::new(),
18639        };
18640        let err = caixa
18641            .push_dep(crate::dep::DepList::Prod, dup)
18642            .expect_err("second push with same :nome refuses");
18643        assert_eq!(
18644            err,
18645            DepError::DuplicateNome {
18646                nome: "caixa-teia".to_string(),
18647                list: crate::render::DEP_AUTHOR_KEY_DEPS,
18648            }
18649        );
18650        // The refused mutation must not corrupt the target list —
18651        // exactly one entry lives past the refusal, matching the
18652        // canonical single-source-of-truth invariant `Caixa::deps()`
18653        // carries.
18654        assert_eq!(caixa.deps().len(), 1);
18655    }
18656
18657    #[test]
18658    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
18659        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
18660        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
18661        // `list` payload so a future author reading the refusal grep's
18662        // for the correct `:deps-dev` block in their `caixa.lisp`,
18663        // not the sibling `:deps` block the runtime closure resolves.
18664        let src = Caixa::template("host");
18665        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18666        let dep = Dep {
18667            nome: "tatara-check".to_string(),
18668            versao: "*".to_string(),
18669            fonte: None,
18670            opcional: false,
18671            caracteristicas: Vec::new(),
18672        };
18673        caixa
18674            .push_dep(crate::dep::DepList::Dev, dep.clone())
18675            .expect("first push succeeds");
18676        let err = caixa
18677            .push_dep(crate::dep::DepList::Dev, dep)
18678            .expect_err("second push with same :nome refuses");
18679        assert!(matches!(
18680            err,
18681            DepError::DuplicateNome {
18682                ref nome,
18683                list,
18684            } if nome == "tatara-check"
18685                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
18686        ));
18687    }
18688
18689    #[test]
18690    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
18691        // The within-list dup check is scoped to the target arm — a
18692        // caixa may legitimately carry the same `:nome` under both
18693        // `:deps` and `:deps-dev` (though the substrate's peer
18694        // [`crate::Caixa::validate_deps`] walk still refuses the
18695        // shape at parse time; the mutation-site refusal is scoped to
18696        // the mutation-site's list to match the peer parse-time
18697        // per-list [`crate::render::insert_first_seen`] discipline).
18698        // The two arms hold independent seen-sets.
18699        let src = Caixa::template("host");
18700        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18701        let dep_prod = Dep {
18702            nome: "shared".to_string(),
18703            versao: "^0.1".to_string(),
18704            fonte: None,
18705            opcional: false,
18706            caracteristicas: Vec::new(),
18707        };
18708        let dep_dev = Dep {
18709            nome: "shared".to_string(),
18710            versao: "*".to_string(),
18711            fonte: None,
18712            opcional: false,
18713            caracteristicas: Vec::new(),
18714        };
18715        caixa
18716            .push_dep(crate::dep::DepList::Prod, dep_prod)
18717            .expect("push into :deps succeeds");
18718        caixa
18719            .push_dep(crate::dep::DepList::Dev, dep_dev)
18720            .expect("push same :nome into :deps-dev succeeds");
18721        assert_eq!(caixa.deps().len(), 1);
18722        assert_eq!(caixa.deps_dev().len(), 1);
18723    }
18724
18725    #[test]
18726    fn deps_of_prod_returns_the_deps_slot_verbatim() {
18727        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
18728        // accessor must project onto the runtime-closure `:deps` slot —
18729        // element-equal and length-equal to the sibling per-slot
18730        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
18731        // A future arm that regressed to `self.deps_dev()` on the `Prod`
18732        // path would silently reroute every downstream typed-dispatch
18733        // walker (the [`Caixa::validate_deps`] per-list
18734        // [`crate::render::insert_first_seen`] dedup walk, any future
18735        // per-axis-parametrised consumer) into the sibling dev-only
18736        // closure and this pin refuses that regression.
18737        let src = Caixa::template("host");
18738        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18739        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
18740        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
18741        let dep = Dep {
18742            nome: "caixa-teia".to_string(),
18743            versao: "^0.1".to_string(),
18744            fonte: None,
18745            opcional: false,
18746            caracteristicas: Vec::new(),
18747        };
18748        caixa
18749            .push_dep(crate::dep::DepList::Prod, dep.clone())
18750            .expect("push into :deps succeeds");
18751        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
18752        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
18753        assert_eq!(
18754            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
18755            "caixa-teia"
18756        );
18757    }
18758
18759    #[test]
18760    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
18761        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
18762        // [`Caixa::deps_of`] must project onto the dev-only-closure
18763        // `:deps-dev` slot, element-equal and length-equal to the
18764        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
18765        // future regression that inverted the two arms would silently
18766        // route every dev-list walker onto the runtime closure and this
18767        // pin catches it before the drift ships.
18768        let src = Caixa::template("host");
18769        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18770        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
18771        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
18772        let dep = Dep {
18773            nome: "tatara-check".to_string(),
18774            versao: "*".to_string(),
18775            fonte: None,
18776            opcional: false,
18777            caracteristicas: Vec::new(),
18778        };
18779        caixa
18780            .push_dep(crate::dep::DepList::Dev, dep)
18781            .expect("push into :deps-dev succeeds");
18782        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
18783        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
18784        assert_eq!(
18785            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
18786            "tatara-check"
18787        );
18788    }
18789
18790    #[test]
18791    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
18792        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
18793        // [`Caixa::deps_of`] must land on the same two-slot partition the
18794        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
18795        // expose — the canonical dispatch a future per-axis-parametrised
18796        // walker (a future `feira app graph` per-list dep summary, a
18797        // future M4 per-cluster dev-closure-audit overlay the CR
18798        // materializer resolves per-CR) reads through. Prior to the
18799        // lift the two-block iteration lived open-coded at every walker,
18800        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
18801        // §I) would have had to grow a third block at every consumer.
18802        // A regression that dropped the `Dev` arm from `ALL` would flip
18803        // the collected pairs to `[(":deps", &[])]` alone and this pin
18804        // refuses that shape.
18805        let src = Caixa::template("host");
18806        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18807        let prod_dep = Dep {
18808            nome: "caixa-teia".to_string(),
18809            versao: "^0.1".to_string(),
18810            fonte: None,
18811            opcional: false,
18812            caracteristicas: Vec::new(),
18813        };
18814        let dev_dep = Dep {
18815            nome: "tatara-check".to_string(),
18816            versao: "*".to_string(),
18817            fonte: None,
18818            opcional: false,
18819            caracteristicas: Vec::new(),
18820        };
18821        caixa
18822            .push_dep(crate::dep::DepList::Prod, prod_dep)
18823            .expect("push into :deps succeeds");
18824        caixa
18825            .push_dep(crate::dep::DepList::Dev, dev_dep)
18826            .expect("push into :deps-dev succeeds");
18827        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
18828            .iter()
18829            .map(|&list| {
18830                let slice = caixa.deps_of(list);
18831                (list.as_str(), slice.len(), slice[0].nome())
18832            })
18833            .collect();
18834        assert_eq!(
18835            collected,
18836            vec![
18837                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
18838                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
18839            ]
18840        );
18841    }
18842
18843    #[test]
18844    fn caixa_deps_of_is_const_fn() {
18845        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
18846        // `const`-eval-surface posture. The typed-dispatch read
18847        // accessor forwards through the sibling `pub const fn`
18848        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
18849        // accessors on the two [`crate::dep::DepList`] enum arms —
18850        // every operator in the body is already `const`-callable
18851        // (`DepList` is a plain `#[derive(Copy)]` closed-set
18852        // discriminator so the `match` arms are const-evaluable, and
18853        // each arm dispatches through the sibling `pub const fn`
18854        // slice accessor). Any future accidental downgrade to
18855        // non-`const` fails the `deps_of_via_const_fn` wrapper below
18856        // at caixa-core build time with E0015 (`cannot call non-const
18857        // method`), strictly stronger than a runtime `assert!` and
18858        // side-stepping the destructor-in-const restriction the
18859        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
18860        // rule out on the direct-`const _: () = assert!(...)`
18861        // residence.
18862        //
18863        // Peer of the sibling outer-`Caixa` accessor family pins
18864        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
18865        // on the `&[String]` universal-axis surface,
18866        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
18867        // on the outer `&[T]` composite-slice surface,
18868        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
18869        // on the outer `Option<&Composite>` surface) — this pin
18870        // extends the `const`-eval-surface discipline onto the outer-
18871        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
18872        // dep-list axis, closing the outer-`Caixa` accessor family's
18873        // last unlifted `pub fn` on the read side.
18874        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
18875            c.deps_of(list)
18876        }
18877        let src = Caixa::template("host");
18878        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18879        // Empty-list arm: both `Prod` and `Dev` degenerate to the
18880        // empty slice with no silent `None` collapse — the
18881        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
18882        // that omits the slot lands on.
18883        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
18884        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
18885        assert_eq!(
18886            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
18887            caixa.deps()
18888        );
18889        assert_eq!(
18890            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
18891            caixa.deps_dev()
18892        );
18893        // Populated arms: each list carries its own entry, and the
18894        // wrapper / direct dispatches agree byte-for-byte on the
18895        // slice-view under both non-empty arms.
18896        let prod_dep = Dep {
18897            nome: "caixa-teia".to_string(),
18898            versao: "^0.1".to_string(),
18899            fonte: None,
18900            opcional: false,
18901            caracteristicas: Vec::new(),
18902        };
18903        let dev_dep = Dep {
18904            nome: "tatara-check".to_string(),
18905            versao: "*".to_string(),
18906            fonte: None,
18907            opcional: false,
18908            caracteristicas: Vec::new(),
18909        };
18910        caixa
18911            .push_dep(crate::dep::DepList::Prod, prod_dep)
18912            .expect("push into :deps succeeds");
18913        caixa
18914            .push_dep(crate::dep::DepList::Dev, dev_dep)
18915            .expect("push into :deps-dev succeeds");
18916        assert_eq!(
18917            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
18918            caixa.deps()
18919        );
18920        assert_eq!(
18921            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
18922            caixa.deps_dev()
18923        );
18924        assert_eq!(
18925            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
18926            "caixa-teia"
18927        );
18928        assert_eq!(
18929            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
18930            "tatara-check"
18931        );
18932    }
18933
18934    #[test]
18935    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
18936        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
18937        // must route its per-list [`crate::render::insert_first_seen`]
18938        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
18939        // rather than the pre-lift open-coded two-block iteration over
18940        // `self.deps()` + `self.deps_dev()`. A regression that dropped
18941        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
18942        // stop refusing within-list dups on the sibling arm; a
18943        // regression that flipped the arm-to-list-key mapping
18944        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
18945        // diagnostic surface. Both drifts surface here through a paired
18946        // duplicate-name refusal per arm plus an offending-list-key
18947        // check on the emitted [`DepError::DuplicateNome`] carrier.
18948        for &list in crate::dep::DepList::ALL {
18949            let src = Caixa::template("host");
18950            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
18951            let dup = Dep {
18952                nome: "twin".to_string(),
18953                versao: "^0.1".to_string(),
18954                fonte: None,
18955                opcional: false,
18956                caracteristicas: Vec::new(),
18957            };
18958            match list {
18959                crate::dep::DepList::Prod => {
18960                    caixa.deps.push(dup.clone());
18961                    caixa.deps.push(dup);
18962                }
18963                crate::dep::DepList::Dev => {
18964                    caixa.deps_dev.push(dup.clone());
18965                    caixa.deps_dev.push(dup);
18966                }
18967            }
18968            let err = caixa
18969                .validate_deps()
18970                .expect_err("within-list duplicate :nome must refuse");
18971            assert_eq!(
18972                err,
18973                DepError::DuplicateNome {
18974                    nome: "twin".to_string(),
18975                    list: list.as_str(),
18976                },
18977                "validate_deps on {list} arm must emit \
18978                 DepError::DuplicateNome carrying the arm's own \
18979                 as_str() diagnostic — the arm-to-list-key mapping \
18980                 flowed through DepList::ALL + Caixa::deps_of"
18981            );
18982        }
18983    }
18984
18985    #[test]
18986    fn caixa_licenca_default_pins_canonical_mit_byte() {
18987        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
18988        // canonical SPDX-`"MIT"` byte today, the same license expression
18989        // every peer substrate-side consumer of the author-omitted
18990        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
18991        // `caixa-helm/src/lib.rs`, the future M4
18992        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
18993        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
18994        // crate's [`Caixa::validate_licenca`] docstring roadmap already
18995        // names as the second consumer) fills into its per-consumer
18996        // README/annotation emit site. Pin the literal here (peer with the
18997        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
18998        // [`crate::version::DEFAULT_GIT_REMOTE`] /
18999        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
19000        // on the sibling lifted-constant surfaces) so a future
19001        // substrate-side license-fallback rebrand surfaces here as a
19002        // coordinated edit-point: the sibling caixa-helm
19003        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
19004        // pinning test already pins the equality at the renderer-emit
19005        // axis; this pin closes the second coordinate of the pair by
19006        // anchoring the lifted constant's current byte to the canonical
19007        // CAIXA-SDLC §I license scaffold's documented shape.
19008        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
19009    }
19010}