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    ///
4166    /// Compound per-`Caixa` entry gate on the dep-graph axis: folds the
4167    /// two standalone dep-list validators — the per-entry + within-list
4168    /// duplicate-`:nome` walk (the [`Dep::validate`] +
4169    /// [`crate::render::insert_first_seen`] cascade this method opened
4170    /// on) and the cross-slot self-edge gate
4171    /// ([`crate::dep::validate_no_self_dep`]) — onto one substrate
4172    /// primitive on [`Caixa`]. The two arms run in the same canonical
4173    /// order the layout pipeline
4174    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4175    /// author-time gate) has always sequenced them (per-entry +
4176    /// cross-entry duplicate → cross-slot self-edge), so the fold is
4177    /// byte-for-byte equivalent to the pre-fold two-block cascade at
4178    /// that call site (pinned by the paired
4179    /// `validate_deps_folds_per_entry_arm_matches_gate` /
4180    /// `validate_deps_folds_self_edge_arm_matches_gate` equivalence
4181    /// pins and the `validate_deps_per_entry_arm_fires_before_self_edge_arm`
4182    /// ordering pin). Self-contained on `&self` — resolves its three
4183    /// inputs ([`Self::deps`], [`Self::deps_dev`], [`Self::nome`])
4184    /// through the substrate primitives' own accessor family, the same
4185    /// posture every peer per-slot compound gate
4186    /// ([`crate::AplicacaoSpec::validate_contratos`],
4187    /// [`crate::MeshPolicy::validate`],
4188    /// [`crate::SupervisorSpec::validate_children`],
4189    /// [`Self::validate_upgrade_from`]) already carries.
4190    ///
4191    /// Prior to this lift [`crate::dep::validate_no_self_dep`] lived
4192    /// only open-coded at the layout wire-up site
4193    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs)
4194    /// as a standalone two-arg dispatch immediately after this method's
4195    /// per-entry + cross-entry walk, both wrapped through the same
4196    /// [`crate::LayoutError::DepsViolation`] envelope: every future
4197    /// consumer that wanted to gate the dep-graph as a whole — the
4198    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4199    /// per-CR admission webhook re-checking `:deps` / `:deps-dev` after
4200    /// a per-entry patch, a future `feira validate --deps` per-caixa
4201    /// admission verb, a per-`:deps` overlay resolver a per-cluster
4202    /// overlay lift would materialize (each the deferred consumer this
4203    /// method's peer [`Self::deps`] / [`Self::deps_dev`] accessors'
4204    /// docstrings already name) — was structurally forced to either
4205    /// re-inline the two-dispatch cascade in lockstep with the layout
4206    /// wire-up (the duplication the PRIME DIRECTIVE names as a bug) or
4207    /// call the whole [`crate::layout::StandardLayout::verify`] pipeline
4208    /// and pay every peer per-Caixa gate to re-check one slot. Post-fold
4209    /// each such consumer reaches the two-arm compound gate through one
4210    /// call on the substrate primitive.
4211    pub fn validate_deps(&self) -> Result<(), DepError> {
4212        for &list in crate::dep::DepList::ALL {
4213            let mut seen = std::collections::HashSet::new();
4214            for dep in self.deps_of(list) {
4215                dep.validate()?;
4216                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4217                    DepError::DuplicateNome {
4218                        nome: dep.nome().to_string(),
4219                        list: list.as_str(),
4220                    }
4221                })?;
4222            }
4223        }
4224        crate::dep::validate_no_self_dep(self.deps(), self.deps_dev(), self.nome())?;
4225        Ok(())
4226    }
4227
4228    /// Reject `:nome` values the K8s apiserver would refuse at admission
4229    /// time. The top-level Caixa identity flows directly into every
4230    /// substrate-side artifact's `metadata.name` axis: the
4231    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4232    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4233    /// aggregator keys ComputeUnit derivation off
4234    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4235    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4236    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4237    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4238    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4239    /// ([`caixa-mesh::lib::cilium_network_policies`],
4240    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4241    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4242    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4243    /// schema enforces the DNS-1123 label rule on admission; a
4244    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4245    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4246    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4247    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4248    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4249    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4250    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4251    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4252    /// Invalid value` rejection on whichever derived artifact admitted
4253    /// first, far from the source `caixa.lisp` and without any field
4254    /// naming the offending `:nome`.
4255    ///
4256    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4257    /// substrate-side predicate the per-axis name gates already share:
4258    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4259    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4260    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4261    /// diagnostic is self-locating (the offending `:nome` is named
4262    /// verbatim) and the author can grep their `caixa.lisp` for
4263    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4264    /// every per-axis sibling gate already exposes
4265    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4266    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4267    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4268    ///
4269    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4270    /// derive macro stores the raw String) is gated by the narrower
4271    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4272    /// consulted, mirroring the empty-first cascade every per-axis
4273    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4274    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4275    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4276        // Routes through the shared
4277        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4278        // name axes each land on so drift between the eight axes'
4279        // accepted DNS-1123-label sets is structurally impossible.
4280        let nome = self.nome();
4281        crate::render::require_valid_dns_1123_label(
4282            nome,
4283            || ManifestError::NomeEmpty,
4284            |reason| ManifestError::NomeInvalid {
4285                nome: nome.to_string(),
4286                reason,
4287            },
4288        )
4289    }
4290
4291    /// Reject `:nome` values whose joint length with the canonical
4292    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4293    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4294    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4295    /// substrate carries materializes the caixa's `:nome` through the
4296    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4297    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4298    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4299    /// `ChartDir.name` + `Chart.yaml::name`
4300    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4301    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4302    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4303    /// `oci://<registry>/lareira-<nome>` chart ref
4304    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4305    /// admission rule strict-parses against DNS-1123-label, the Helm
4306    /// operator's tracking-secret name is derived from `release_name`
4307    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4308    /// K8s object `metadata.name` axes embed the chart name as a
4309    /// prefix — every one fails admission on a > 63-byte chart name.
4310    ///
4311    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4312    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4313    /// `:nome` of 56–63 bytes silently passed validate (the inner
4314    /// DNS-1123 check accepts the bare `:nome`) but produced a
4315    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4316    /// rejected at admission — far from the source `caixa.lisp`, with
4317    /// no field naming the overflow root cause. The
4318    /// [`lareira_chart_name`] helper's own doc comment
4319    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4320    /// "the M4 admission webhook will pin the joint-length invariant
4321    /// when it lands". This gate lands the invariant at the
4322    /// manifest-validate layer rather than waiting for the apiserver
4323    /// — the same fail-at-the-source posture every peer per-axis
4324    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4325    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4326    /// `:edicao`, etc.) takes.
4327    ///
4328    /// Thin wrapper around
4329    /// [`crate::render::is_lareira_chart_name_shape`] (the
4330    /// substrate-side predicate that composes [`lareira_chart_name`] +
4331    /// [`is_dns_1123_label`] via the lifted
4332    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4333    /// shared parser-shaped reason into the
4334    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4335    /// diagnostic is self-locating (the offending `:nome` is named
4336    /// verbatim alongside the rendered chart name and the budget) and
4337    /// the author can shorten in one edit. The gate runs across every
4338    /// `:kind` — `:nome` is the substrate-wide identity axis any
4339    /// future renderer the substrate adds can derive a
4340    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4341    /// the drift footgun where a future kind grows a chart-emitting
4342    /// render path while the validate cascade doesn't catch it.
4343    ///
4344    /// Runs *after* [`Self::validate_nome`] so the narrower
4345    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4346    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4347    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4348    /// specific shape error rather than the chart-name-budget error,
4349    /// preserving the legitimate "well-shaped `:nome` that happens to
4350    /// overflow the joint cap" arm for this gate.
4351    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4352        let nome = self.nome();
4353        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4354            ManifestError::NomeChartNameBudgetExceeded {
4355                nome: nome.to_string(),
4356                reason,
4357            }
4358        })
4359    }
4360
4361    /// Reject `:versao` values that don't parse as [`semver::Version`].
4362    /// The top-level Caixa version flows directly into every
4363    /// substrate-side artifact that carries a "this is which version of
4364    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4365    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4366    /// SemVer-2-strict at `helm template` / `helm install` time per
4367    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4368    /// `feira publish` Zig-style `v<versao>` git tag
4369    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4370    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4371    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4372    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4373    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4374    /// `skopeo push`, the lacre closure's pinned versions
4375    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4376    /// `:upgrade-from :from` references peers in this exact `versao`
4377    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4378    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4379    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4380    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4381    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4382    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4383    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4384    /// into the version field a peer `:deps :versao` accepts;
4385    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4386    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4387    /// derive macro stores the raw String) and the failure surfaced at
4388    /// the *first* downstream consumer that strict-parses it: at
4389    /// `helm install` time as a chart-version rejection, at
4390    /// `feira publish` time as a malformed git tag, at lacre-resolve
4391    /// time as a `semver::Error` not naming the offending caixa, at
4392    /// `feira upgrade --to <versao>` time as an unresolvable
4393    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4394    /// and without any field naming the offending `:versao`.
4395    ///
4396    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4397    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4398    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4399    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4400    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4401    /// variant, carrying the offending `:versao` verbatim + a
4402    /// parser-shaped reason naming the specific violation, so the
4403    /// diagnostic is self-locating (the author can grep their
4404    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4405    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4406    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4407    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4408    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4409    /// now structurally equivalent (every value past validate is
4410    /// round-trippable through [`semver::Version::parse`] without
4411    /// re-checking at the renderer, resolver, or operator hot-upgrade
4412    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4413    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4414    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4415    ///
4416    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4417    /// the derive macro stores the raw String) is gated by the
4418    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4419    /// consulted, mirroring the empty-first cascade every per-axis
4420    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4421    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4422    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4423    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4424        let versao = self.versao();
4425        if versao.is_empty() {
4426            return Err(ManifestError::VersaoEmpty);
4427        }
4428        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4429            versao: versao.to_string(),
4430            reason: e.to_string(),
4431        })?;
4432        Ok(())
4433    }
4434
4435    /// Compound per-`Caixa` entry gate on the M2 `:upgrade-from` slot:
4436    /// folds the three [`crate::upgrade`] top-level validators — the
4437    /// per-entry shape + cross-entry duplicate-`:from` gate
4438    /// ([`crate::upgrade::validate_upgrade_from`]), the cross-slot
4439    /// `:from < :versao` SemVer-2 precedence gate
4440    /// ([`crate::upgrade::validate_upgrade_from_against_versao`]), and the
4441    /// cross-slot `:state-change` ↔ `:on-state-change` composition gate
4442    /// ([`crate::upgrade::validate_upgrade_from_against_behavior`]) — onto
4443    /// one substrate primitive on [`Caixa`]. The three dispatches run in
4444    /// the same order the layout pipeline
4445    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4446    /// author-time gate) has always sequenced them, so the fold is
4447    /// byte-for-byte equivalent to the pre-fold three-block cascade at
4448    /// that call site (pinned by the per-arm
4449    /// `validate_upgrade_from_folds_per_entry_arm_matches_gate` /
4450    /// `_folds_versao_arm_matches_gate` / `_folds_behavior_arm_matches_gate`
4451    /// equivalence pins and by the cross-arm
4452    /// `validate_upgrade_from_per_entry_arm_fires_before_versao_arm` /
4453    /// `_versao_arm_fires_before_behavior_arm` ordering pins).
4454    ///
4455    /// Prior to this lift the three [`crate::upgrade`] top-level validators
4456    /// lived only open-coded at the layout wire-up site
4457    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4458    /// each threaded through the same `self.upgrade_from()` slice and each
4459    /// paired with the same [`crate::LayoutError::UpgradeViolation`]-wrap
4460    /// envelope: every future consumer that wanted to gate `:upgrade-from`
4461    /// as a whole — the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
4462    /// materializer's per-CR admission webhook re-checking `:upgrade-from`
4463    /// after a per-`(:from … :instructions …)` patch, a future `feira
4464    /// validate --upgrade` per-caixa admission verb, a per-`:upgrade-from`
4465    /// overlay resolver a per-cluster overlay lift would materialize —
4466    /// was structurally forced to either re-inline the three-dispatch
4467    /// cascade in lockstep with the layout wire-up (the duplication the
4468    /// PRIME DIRECTIVE names as a bug) or call the whole
4469    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4470    /// peer per-Caixa gate to re-check one slot. Post-fold each such
4471    /// consumer reaches the three-arm compound gate through one call on
4472    /// the substrate primitive.
4473    ///
4474    /// The three arms together name one contract with three axes:
4475    ///
4476    ///   - **per-entry + cross-entry graph-edge invariant** — every entry's
4477    ///     `:from` parses as SemVer-2 and every per-instruction / within-
4478    ///     entry ordering / singularity gate on each entry's
4479    ///     `:instructions` list passes, and no two entries share the same
4480    ///     parsed `:from` (the wasm-operator's OTP appup
4481    ///     `release_handler:install_release/1` analog picks at most one
4482    ///     matching block per running version — two entries with the same
4483    ///     parsed semver are an ambiguous edge in the typed upgrade graph).
4484    ///   - **cross-slot reachability invariant** — every entry's `:from`
4485    ///     is strictly less than the caixa's own `:versao` under SemVer-2
4486    ///     precedence. An entry whose `:from >= :versao` is structurally
4487    ///     unreachable by the operator's `:from`-match dispatch (the
4488    ///     operator loads the current `:versao` and matches the *running*
4489    ///     version against each entry's `:from`; an entry whose `:from >=
4490    ///     :versao` is never reached because the operator never runs a
4491    ///     version >= the current one that it could then upgrade *to* the
4492    ///     current one).
4493    ///   - **cross-slot composition invariant** — every entry carrying a
4494    ///     `(:state-change …)` instruction has a `:behavior
4495    ///     :on-state-change` callback declared on the same caixa. The
4496    ///     per-version migration script is the `gen_server:code_change/3`
4497    ///     analog and the runtime hook it is delivered through during hot
4498    ///     upgrade is the `:on-state-change` callback (the upgrade.rs
4499    ///     module doc pins the composition verbatim: "Composes with the
4500    ///     `:behavior :on-state-change` callback to deliver state migration
4501    ///     during hot upgrades").
4502    ///
4503    /// All three axes must hold together — every consumer's
4504    /// `:upgrade-from` accept-set past this compound gate is the same
4505    /// set the `feira build` author-time gate admits.
4506    ///
4507    /// The per-slot compound entry gate discipline lifted here onto the
4508    /// M2 `:upgrade-from` axis is the sibling of the peer per-kind
4509    /// compound entry gates ([`crate::render::require_supervisor_view`]
4510    /// / [`crate::render::require_aplicacao_view`] /
4511    /// [`crate::render::require_v0_servico_shape`]) that fold every
4512    /// per-kind cascade at the per-kind altitude, and of the peer
4513    /// per-slot compound gates ([`crate::AplicacaoSpec::validate_contratos`],
4514    /// [`crate::MeshPolicy::validate`],
4515    /// [`crate::SupervisorSpec::validate_children`]) that fold every
4516    /// structural axis on their slot onto one substrate primitive.
4517    /// Extended here to the last unlifted compound-cascade wire-up at
4518    /// the layout-pipeline altitude — the three-dispatch M2
4519    /// `:upgrade-from` cascade that lived only open-coded at the layout
4520    /// wire-up site.
4521    ///
4522    /// The per-instruction script-path on-disk existence-probe walk that
4523    /// [`crate::layout::StandardLayout::verify`] runs immediately after
4524    /// this gate (which resolves each entry's `:instructions
4525    /// (:state-change :script)` against the layout root) stays open-coded
4526    /// at the layout wire-up site — that arm needs the filesystem oracle
4527    /// on the [`crate::LayoutInvariants`] trait, not the pure per-Caixa
4528    /// typed-shape surface this compound gate folds. Same posture the
4529    /// peer [`Self::validate_code_paths`] takes on the sibling code-path
4530    /// axes: the typed-shape gate fires on the per-Caixa surface, the
4531    /// on-disk existence check fires on the [`crate::StandardLayout`]
4532    /// surface.
4533    ///
4534    /// # Errors
4535    ///
4536    /// Returns [`crate::UpgradeError::FromInvalid`] /
4537    /// [`crate::UpgradeError::ModuleEmpty`] /
4538    /// [`crate::UpgradeError::ModuleInvalid`] /
4539    /// [`crate::UpgradeError::EmptyScript`] /
4540    /// [`crate::UpgradeError::AbsoluteScript`] /
4541    /// [`crate::UpgradeError::ParentEscapeScript`] /
4542    /// [`crate::UpgradeError::NonLispExtensionScript`] /
4543    /// [`crate::UpgradeError::RestartNotExclusive`] /
4544    /// [`crate::UpgradeError::StateChangeWithoutPriorLoad`] /
4545    /// [`crate::UpgradeError::PurgeWithoutPriorLoad`] /
4546    /// [`crate::UpgradeError::StateChangeAfterCleanup`] /
4547    /// [`crate::UpgradeError::DuplicateLoadModule`] /
4548    /// [`crate::UpgradeError::DuplicateStateChange`] /
4549    /// [`crate::UpgradeError::DuplicateCleanup`] /
4550    /// [`crate::UpgradeError::DuplicateFrom`] on the per-entry +
4551    /// cross-entry axis; [`crate::UpgradeError::FromNotBeforeVersao`] on
4552    /// the cross-slot `:from ↔ :versao` axis;
4553    /// [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
4554    /// on the cross-slot `:state-change ↔ :on-state-change` axis.
4555    pub fn validate_upgrade_from(&self) -> Result<(), crate::UpgradeError> {
4556        crate::upgrade::validate_upgrade_from(self.upgrade_from())?;
4557        crate::upgrade::validate_upgrade_from_against_versao(self.upgrade_from(), self.versao())?;
4558        crate::upgrade::validate_upgrade_from_against_behavior(
4559            self.upgrade_from(),
4560            self.behavior(),
4561        )?;
4562        Ok(())
4563    }
4564
4565    /// Compound per-`Caixa` entry gate on the M2 `:limits` slot — folds
4566    /// the [`crate::LimitsSpec::validate`] four-axis cascade (`:memory`
4567    /// wasm32 zero-floor / below-page / above-cap / non-page-multiple;
4568    /// `:fuel` zero-floor / cap; `:wall-clock` zero-floor / cap; `:cpu`
4569    /// zero-floor / cap) onto one substrate primitive on [`Caixa`]. The
4570    /// `#[serde(default)]` absent-slot arm (`limits: None`, the
4571    /// canonical "no bound declared — engine-default applies" author
4572    /// shape [`crate::LimitsSpec::is_empty`]'s per-axis `None` cascade
4573    /// reads) is the fold's identity element and passes trivially; the
4574    /// present-slot arm (`limits: Some(l)`) dispatches to
4575    /// [`crate::LimitsSpec::validate`] verbatim, threading its per-axis
4576    /// [`crate::LimitsError`] Display through untouched.
4577    ///
4578    /// Prior to this lift the M2 `:limits` slot lived only wired
4579    /// open-coded at the layout wire-up site
4580    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4581    /// through the `if let Some(l) = caixa.limits() { l.validate() … }`
4582    /// three-line `Option::None → Ok(()) | Some(_) → …` unwrap-and-
4583    /// dispatch pattern paired with the same
4584    /// [`crate::LayoutError::LimitsViolation`]-wrap envelope: every
4585    /// future consumer that wanted to gate `:limits` as a whole — the
4586    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4587    /// per-CR admission webhook re-checking `:limits` after a per-
4588    /// `{:memory, :fuel, :wall-clock, :cpu}` patch (the exact case the
4589    /// [`Self::limits`] accessor docstring names as the second
4590    /// consumer of the slot), a future `feira validate --limits` per-
4591    /// caixa admission verb, a per-`:limits` overlay resolver a per-
4592    /// cluster `:limits-overrides` overlay lift would materialize — was
4593    /// structurally forced to either re-inline the two-line
4594    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4595    /// pattern in lockstep with the layout wire-up (the duplication the
4596    /// PRIME DIRECTIVE names as a bug) or call the whole
4597    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4598    /// peer per-Caixa gate ([`Self::validate_nome`],
4599    /// [`Self::validate_versao`], [`Self::validate_deps`],
4600    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4601    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4602    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4603    /// [`Self::validate_upgrade_from`], [`Self::validate_code_paths`],
4604    /// plus the per-kind `require_supervisor_view` /
4605    /// `require_aplicacao_view` gates, plus the on-disk existence
4606    /// walks) to re-check one slot. Post-lift each such consumer
4607    /// reaches the [`crate::LimitsSpec::validate`] four-axis cascade
4608    /// (and its identity-element on the absent slot) through one call
4609    /// on the substrate primitive.
4610    ///
4611    /// The per-slot compound entry-gate discipline lifted here onto the
4612    /// M2 `:limits` axis is the sibling of the peer per-slot compound
4613    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4614    /// [`crate::MeshPolicy::validate`],
4615    /// [`crate::SupervisorSpec::validate_children`],
4616    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`]) that
4617    /// fold every structural + cross-slot axis on their slot onto one
4618    /// substrate primitive. Extended here to the M2 `:limits` slot, the
4619    /// first of the two M2 typed slots (`:limits`, `:behavior`) whose
4620    /// per-Caixa compound-gate wire-up still lived open-coded at the
4621    /// layout altitude after the [`Self::validate_upgrade_from`] lift
4622    /// (d6801df) closed the sibling M2 slot's cascade.
4623    ///
4624    /// # Errors
4625    ///
4626    /// Returns every [`crate::LimitsError`] variant on the present-slot
4627    /// arm — verbatim from [`crate::LimitsSpec::validate`]. Passes
4628    /// trivially on the absent-slot arm (`limits: None`, the fold's
4629    /// identity element).
4630    pub fn validate_limits(&self) -> Result<(), crate::LimitsError> {
4631        match self.limits() {
4632            Some(l) => l.validate(),
4633            None => Ok(()),
4634        }
4635    }
4636
4637    /// Compound per-`Caixa` entry gate on the M2 `:behavior` slot's
4638    /// pure typed-shape surface — folds the
4639    /// [`crate::BehaviorSpec::validate`] six-slot value-shape cascade
4640    /// (each declared `:on-init` / `:on-call` / `:on-cast` / `:on-info`
4641    /// / `:on-state-change` / `:on-terminate` callback-path is
4642    /// non-empty / relative / no-`..`-parent-escape / terminating-
4643    /// `.lisp`-extension, routed through the shared
4644    /// [`crate::render::require_sandboxed_lisp_path`] arm-set) onto one
4645    /// substrate primitive on [`Caixa`]. The `#[serde(default)]`
4646    /// absent-slot arm (`behavior: None`, the canonical "no callback
4647    /// declared — the runtime falls back to the wasm-engine's default
4648    /// callback per arm" author shape [`crate::BehaviorSpec::is_empty`]'s
4649    /// per-slot `None` cascade reads) is the fold's identity element
4650    /// and passes trivially; the present-slot arm (`behavior: Some(b)`)
4651    /// dispatches to [`crate::BehaviorSpec::validate`] verbatim,
4652    /// threading its per-slot [`crate::BehaviorError`] Display through
4653    /// untouched.
4654    ///
4655    /// Scope note — the on-disk callback-path existence walk paired
4656    /// with the value-shape gate at
4657    /// [`crate::layout::StandardLayout::verify`] stays open-coded at
4658    /// the layout altitude, because it needs the
4659    /// [`crate::layout::LayoutInvariants`] filesystem oracle
4660    /// ([`crate::layout::LayoutInvariants::exists`]) that the pure
4661    /// per-Caixa typed-shape surface this compound gate folds onto has
4662    /// no reference to. Same posture the peer M2 `:upgrade-from`
4663    /// per-Caixa compound gate ([`Self::validate_upgrade_from`]
4664    /// d6801df) already carries: the pure typed-shape surface folds
4665    /// onto the substrate primitive; the per-instruction script-path
4666    /// existence probe on the paired axis (there `:state-change
4667    /// :script`; here `:on-*`) stays at the layout altitude.
4668    ///
4669    /// Prior to this lift the pure value-shape surface of the M2
4670    /// `:behavior` slot lived only wired open-coded at the layout
4671    /// wire-up site ([`crate::layout::StandardLayout::verify`],
4672    /// caixa-core/src/layout.rs), through the
4673    /// `if let Some(b) = caixa.behavior() { b.validate() … }`
4674    /// unwrap-and-dispatch pattern paired with the same
4675    /// [`crate::LayoutError::BehaviorViolation`]-wrap envelope: every
4676    /// future consumer that wanted to gate the `:behavior` slot's
4677    /// value-shape as a whole — the deferred
4678    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4679    /// admission webhook re-checking `:behavior` after a per-`{:on-init,
4680    /// :on-call, :on-cast, :on-info, :on-state-change, :on-terminate}`
4681    /// patch (the exact case the peer `:on-*` accessor docstrings on
4682    /// [`crate::BehaviorSpec`] already name as deferred consumers of
4683    /// the slot), a future `feira validate --behavior` per-caixa
4684    /// admission verb, a per-`:behavior` overlay resolver a future
4685    /// per-cluster callback-overlay lift would materialize — was
4686    /// structurally forced to either re-inline the two-line
4687    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4688    /// pattern in lockstep with the layout wire-up (the duplication the
4689    /// PRIME DIRECTIVE names as a bug) or call the whole
4690    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4691    /// peer per-Caixa gate ([`Self::validate_nome`],
4692    /// [`Self::validate_versao`], [`Self::validate_deps`],
4693    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4694    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4695    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4696    /// [`Self::validate_limits`], [`Self::validate_upgrade_from`],
4697    /// [`Self::validate_code_paths`], plus the per-kind
4698    /// `require_supervisor_view` / `require_aplicacao_view` gates, plus
4699    /// the on-disk existence walks) to re-check one slot. Post-lift
4700    /// each such consumer reaches the [`crate::BehaviorSpec::validate`]
4701    /// six-slot cascade (and its identity-element on the absent slot)
4702    /// through one call on the substrate primitive.
4703    ///
4704    /// The per-slot compound entry-gate discipline lifted here onto the
4705    /// M2 `:behavior` axis is the sibling of the peer per-slot compound
4706    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4707    /// [`crate::MeshPolicy::validate`],
4708    /// [`crate::SupervisorSpec::validate_children`],
4709    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`],
4710    /// [`Self::validate_limits`]) that fold every structural + cross-
4711    /// slot axis on their slot onto one substrate primitive. Extended
4712    /// here to the M2 `:behavior` slot, the last of the four M2 typed
4713    /// slots (`:limits`, `:behavior`, `:upgrade-from`, plus the
4714    /// supervisor-only `:children` peer) whose per-Caixa compound-gate
4715    /// wire-up still lived open-coded at the layout altitude after the
4716    /// [`Self::validate_limits`] lift (baa4688) closed the sibling M2
4717    /// `:limits` slot's cascade. With this lift the "one named per-slot
4718    /// / per-Caixa compound gate per typed slot folding every structural
4719    /// axis on that slot (plus the `Option::None` identity element for
4720    /// the `Option`-shaped slots) onto one substrate primitive"
4721    /// discipline spans every M2 typed slot uniformly, so a reader who
4722    /// has learned any peer M2 gate reads `:behavior` without a per-
4723    /// slot exception carve-out.
4724    ///
4725    /// # Errors
4726    ///
4727    /// Returns every [`crate::BehaviorError`] variant on the present-
4728    /// slot arm — verbatim from [`crate::BehaviorSpec::validate`].
4729    /// Passes trivially on the absent-slot arm (`behavior: None`, the
4730    /// fold's identity element).
4731    pub fn validate_behavior(&self) -> Result<(), crate::BehaviorError> {
4732        match self.behavior() {
4733            Some(b) => b.validate(),
4734            None => Ok(()),
4735        }
4736    }
4737
4738    /// Reject `:restart-window` values the shared
4739    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4740    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4741    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4742    /// `Option<Duration>` routed through the shared codec via `with =
4743    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4744    /// view-construction path ([`Self::supervisor_view`]) folds the
4745    /// raw string through the same shared codec and soft-swallows the
4746    /// parse error as `None` to keep the view best-effort. Without
4747    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4748    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4749    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4750    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4751    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4752    /// edge case) silently produced a `SupervisorSpec` with
4753    /// `restart_window: None`, indistinguishable from the canonical
4754    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4755    /// `MaxIntensity / Period` invariant turns into a never-reset
4756    /// supervisor far from the source `caixa.lisp`, with no field
4757    /// naming the offending `:restart-window`. Lifting the gate to a
4758    /// Caixa-level validator mirrors the trajectory of the peer
4759    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4760    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4761    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4762    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4763    ///
4764    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4765    /// (the shared codec backing `:supervisor :restart-window` as
4766    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4767    /// `:politicas :circuit-breaker :window` — all three covered by
4768    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4769    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4770    /// variant, carrying the offending raw string + a parser-shaped
4771    /// reason naming the canonical authoring form, so the diagnostic
4772    /// is self-locating (the author can grep their `caixa.lisp` for
4773    /// `:restart-window "<value>"` and fix it in one edit) and
4774    /// uniform with every other manifest-level validate diagnostic.
4775    /// With this gate the four `:restart-window`-shaped surfaces (the
4776    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4777    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4778    /// now structurally equivalent — every value past the codec is in
4779    /// one accepted set, by construction.
4780    ///
4781    /// `None` (the canonical "omit the slot to express no reset"
4782    /// shape) is accepted trivially — the gate is a no-op when the
4783    /// author didn't author a window. The empty string is rejected by
4784    /// the shared codec (its digit-only gate refuses an empty
4785    /// magnitude), surfacing the same `RestartWindowMalformed`
4786    /// diagnostic as every other rejected non-canonical shape.
4787    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4788        let Some(s) = self.restart_window() else {
4789            return Ok(());
4790        };
4791        crate::supervisor::duration_codec::parse(s)
4792            .map(|_| ())
4793            .map_err(|reason| ManifestError::RestartWindowMalformed {
4794                restart_window: s.to_string(),
4795                reason,
4796            })
4797    }
4798
4799    /// Compound per-`Caixa` entry gate on the Aplicacao-kind mesh-slot
4800    /// family — folds the paired [`crate::AplicacaoSpec::validate`]
4801    /// typed-shape cascade (per-slot gates on `:membros`, `:contratos`,
4802    /// `:entrada`, `:placement`, `:politicas`, in that declared order)
4803    /// plus the cross-slot self-edge gate
4804    /// ([`crate::aplicacao::validate_no_self_membership`], the
4805    /// `:membros :caixa` ≠ `:nome` invariant the typed view cannot
4806    /// enforce on its own because it carries the membros but not the
4807    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4808    /// non-Aplicacao kinds the fold is the identity element — the paired
4809    /// [`Self::aplicacao_view`] accessor returns `None` off the
4810    /// Aplicacao arm (peer with the [`Self::validate_limits`] /
4811    /// [`Self::validate_behavior`] M2 `Option`-arm identity element),
4812    /// so the gate passes trivially without touching the mesh slots.
4813    ///
4814    /// Prior to this lift the paired cascade lived only wired open-coded
4815    /// at the layout wire-up site
4816    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4817    /// as the three-line `let view = caixa.aplicacao_view().expect(...);
4818    /// view.validate() … validate_no_self_membership(...) …` pattern
4819    /// paired with two `.map_err(|err| LayoutError::AplicacaoViolation
4820    /// { caixa, issue })` wraps — every future consumer that wanted to
4821    /// gate the Aplicacao-shape cascade as a whole (the deferred
4822    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4823    /// admission webhook re-checking `:membros` / `:contratos` after a
4824    /// per-slot patch, a future `feira validate --aplicacao` per-caixa
4825    /// admission verb, a per-Aplicacao overlay resolver) was structurally
4826    /// forced to either re-inline the two-dispatch cascade in lockstep
4827    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
4828    /// names as a bug) or call the whole
4829    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4830    /// peer per-Caixa gate to re-check one slot family. Post-fold each
4831    /// such consumer reaches the two-arm compound gate through one call
4832    /// on the substrate primitive.
4833    ///
4834    /// Peer to the [`crate::render::require_aplicacao_view`] compound
4835    /// entry gate every per-Aplicacao *renderer* routes through
4836    /// (3aefefb folded `validate_no_self_membership` onto the renderer
4837    /// path) — this gate mirrors the same fold on the *layout* path, so
4838    /// the two consumers of the Aplicacao-shape cascade (the author-time
4839    /// gate and every per-Aplicacao renderer) share one substrate
4840    /// primitive rather than two open-coded cascades kept in lockstep.
4841    /// Same lift discipline the peer per-slot compound gates
4842    /// ([`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
4843    /// b5dd55e, [`Self::validate_limits`] baa4688,
4844    /// [`Self::validate_behavior`] 0d2877a) each carry.
4845    ///
4846    /// # Errors
4847    ///
4848    /// Returns every [`crate::AplicacaoError`] variant on the present-
4849    /// kind arm — the typed-shape cascade's per-slot arms first
4850    /// (matching [`crate::AplicacaoSpec::validate`]'s declared order),
4851    /// then the cross-slot self-edge arm
4852    /// ([`crate::AplicacaoError::MembroIsSelfAplicacao`]). Passes
4853    /// trivially on non-Aplicacao kinds (the fold's identity element).
4854    pub fn validate_aplicacao_shape(&self) -> Result<(), crate::AplicacaoError> {
4855        let Some(view) = self.aplicacao_view() else {
4856            return Ok(());
4857        };
4858        view.validate()?;
4859        crate::aplicacao::validate_no_self_membership(self.membros(), self.nome())?;
4860        Ok(())
4861    }
4862
4863    /// Compound per-`Caixa` entry gate on the Supervisor-kind
4864    /// supervision-tree slot family — folds the paired
4865    /// [`crate::SupervisorSpec::validate`] typed-shape cascade
4866    /// (`:estrategia` ↔ `:children` invariants, `:max-restarts` /
4867    /// `:restart-window` bounds, per-child DNS-1123 `:caixa` names,
4868    /// semver-valid `:versao` constraints, the set-not-multiset
4869    /// duplicate-child gate) plus the cross-slot self-edge gate
4870    /// ([`crate::supervisor::validate_no_self_supervision`], the
4871    /// `:children :caixa` ≠ `:nome` invariant the typed view cannot
4872    /// enforce on its own because it carries the children but not the
4873    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4874    /// non-Supervisor kinds the fold is the identity element — the paired
4875    /// [`Self::supervisor_view`] accessor returns `None` off the
4876    /// Supervisor arm (peer with the [`Self::validate_limits`] /
4877    /// [`Self::validate_behavior`] M2 `Option`-arm identity element and
4878    /// the sibling per-Aplicacao [`Self::validate_aplicacao_shape`]),
4879    /// so the gate passes trivially without touching the supervision-tree
4880    /// slots.
4881    ///
4882    /// Prior to this lift the paired cascade lived only wired open-coded
4883    /// at the layout wire-up site
4884    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4885    /// as the three-line `let view = caixa.supervisor_view().expect(...);
4886    /// view.validate() … validate_no_self_supervision(...) …` pattern
4887    /// paired with two `.map_err(|err| LayoutError::SupervisorViolation
4888    /// { caixa, issue })` wraps — every future consumer that wanted to
4889    /// gate the Supervisor-shape cascade as a whole (the wasm-operator's
4890    /// hierarchical reconciliation scheduler re-checking `:children` /
4891    /// `:estrategia` after a per-slot patch, the M4
4892    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
4893    /// webhook, a future `feira validate --supervisor` per-caixa
4894    /// admission verb, a per-Supervisor overlay resolver) was structurally
4895    /// forced to either re-inline the two-dispatch cascade in lockstep
4896    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
4897    /// names as a bug) or call the whole
4898    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4899    /// peer per-Caixa gate to re-check one slot family. Post-fold each
4900    /// such consumer reaches the two-arm compound gate through one call
4901    /// on the substrate primitive.
4902    ///
4903    /// Peer to the [`crate::render::require_supervisor_view`] compound
4904    /// entry gate every per-Supervisor *renderer* would route through
4905    /// (which already folds the same `spec.validate()` +
4906    /// `validate_no_self_supervision` two-arm cascade behind its
4907    /// `require_kind` + `validate_restart_window` prelude) — this gate
4908    /// mirrors the same fold on the *layout* path, so the two consumers
4909    /// of the Supervisor-shape cascade (the author-time gate and every
4910    /// per-Supervisor renderer) share one substrate primitive rather
4911    /// than two open-coded cascades kept in lockstep. Same lift
4912    /// discipline the peer per-slot compound gates
4913    /// ([`Self::validate_aplicacao_shape`] 949a7a0,
4914    /// [`Self::validate_upgrade_from`] d6801df,
4915    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
4916    /// baa4688, [`Self::validate_behavior`] 0d2877a) each carry.
4917    ///
4918    /// # Errors
4919    ///
4920    /// Returns every [`crate::SupervisorError`] variant on the present-
4921    /// kind arm — the typed-shape cascade's per-slot arms first
4922    /// (matching [`crate::SupervisorSpec::validate`]'s declared order),
4923    /// then the cross-slot self-edge arm
4924    /// ([`crate::SupervisorError::ChildSupervisesSelf`]). Passes
4925    /// trivially on non-Supervisor kinds (the fold's identity element).
4926    pub fn validate_supervisor_shape(&self) -> Result<(), crate::SupervisorError> {
4927        let Some(view) = self.supervisor_view() else {
4928            return Ok(());
4929        };
4930        view.validate()?;
4931        crate::supervisor::validate_no_self_supervision(self.children(), self.nome())?;
4932        Ok(())
4933    }
4934
4935    /// Reject per-entry values on the three Caixa-level code-surface
4936    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
4937    /// layout checker's `root.join(p)` sandbox would silently subvert.
4938    /// Same three structural footguns the peer
4939    /// [`BehaviorSpec::validate`] (b0c8389) and
4940    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
4941    /// (26da2c7) already close on the M2 `:behavior :on-*` and
4942    /// `:upgrade-from :state-change :script` axes, here lifted onto
4943    /// the three top-level code-path axes through the shared
4944    /// [`is_sandboxed_relative_path`] predicate:
4945    ///
4946    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
4947    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
4948    ///     [`Path::join`] as the base itself — `root.join("")` ==
4949    ///     `root`, so the existence check (`self.exists(&root)`)
4950    ///     trivially passes (the project root exists), and the layout
4951    ///     silently treats the project root as a biblioteca / exe /
4952    ///     servico entry. The `:bibliotecas` loop then hands the root
4953    ///     to `tatara_lisp::read` at `feira build` time as if the root
4954    ///     directory itself were a Lisp source file — a parse error
4955    ///     far from the source `caixa.lisp` with no field naming the
4956    ///     offending entry.
4957    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
4958    ///     [`Path::join`] *replaces* the base when the right-hand side
4959    ///     is absolute, so `root.join("/etc/passwd")` resolves to
4960    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
4961    ///     The existence check then silently consults whatever the
4962    ///     escaped path resolves to — for `:bibliotecas`, the layout
4963    ///     has no `starts_with`-fence (only `:exe` is fenced under
4964    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
4965    ///     `:bibliotecas` entry that happens to resolve on disk
4966    ///     silently passes. For `:exe` / `:servicos` the fence catches
4967    ///     the absolute case downstream as `ExeOutsideDir` /
4968    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
4969    ///     doesn't exist), but with a downstream-shaped diagnostic
4970    ///     that names the resolved escape path rather than the
4971    ///     authoring footgun at the source.
4972    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
4973    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
4974    ///     [`std::path::Component::ParentDir`] anywhere round-trips
4975    ///     through [`Path::join`] as a traversal above the caixa root.
4976    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
4977    ///     *component-aware* (not canonical-path-aware), so
4978    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
4979    ///     is **true** even though the canonical resolution
4980    ///     `{parent of root}/escape.lisp` lives outside the caixa root
4981    ///     — the fence silently lets the parent-escape through, and
4982    ///     the existence check passes if that escape-target happens
4983    ///     to exist. Caught regardless of where the `..` sits
4984    ///     (leading, mid-path, trailing) so the gate matches the peer
4985    ///     predicate's full coverage.
4986    ///
4987    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
4988    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
4989    /// same per-slot diagnostic shape every peer per-axis path-gate
4990    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
4991    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
4992    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
4993    /// order [`Caixa::declared_foreign_code_slots`] uses for its
4994    /// canonical foreign-code-slot diagnostic, so a manifest with
4995    /// multiple malformed slots surfaces the lexicographically-earliest
4996    /// slot's diagnostic deterministically.
4997    ///
4998    /// Lifted to the typed surface as a Caixa-level validator (peer
4999    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
5000    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
5001    /// and wired into [`crate::StandardLayout::verify`] before the
5002    /// existence-check loops so the diagnostic names the offending
5003    /// slot at the source caixa.lisp rather than reporting a
5004    /// downstream `MissingEntry` / `ExeOutsideDir` /
5005    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
5006    /// The fourth typed code-path surface — every author-supplied
5007    /// path on the manifest — is now structurally accept-shaped
5008    /// past validate, peer with `:behavior :on-*` and
5009    /// `:upgrade-from :state-change :script`.
5010    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
5011        /// Per-slot file-type contract for the three Caixa-level
5012        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
5013        /// Each variant names the predicate the per-entry file-type
5014        /// gate consults; [`Self::None`] opts the slot out of any
5015        /// file-type contract. Lifted as a typed local enum so the
5016        /// per-slot dispatch is exhaustive at the `match` — adding a
5017        /// future axis to the typed-substrate `:` slot set (the
5018        /// future `:assets` resource axis the M5 roadmap names, the
5019        /// future `:nix-flake` derivation axis the caixa-flake
5020        /// emitter consults) lands as one variant + one `match` arm,
5021        /// not a coordinated rewrite of every per-slot bool flag.
5022        ///
5023        /// Peer of the typed-substrate per-slot variant disciplines
5024        /// already established on this surface
5025        /// ([`crate::supervisor::RestartStrategy`] +
5026        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
5027        /// supervision-tree axis,
5028        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
5029        /// placement axis, [`crate::aplicacao::WitTarget`] on the
5030        /// `:contratos` payload-target axis): the typed `enum` is
5031        /// the substrate's single source of truth for the per-axis
5032        /// dispatch, and every consumer (the per-arm body here, the
5033        /// future feira-lint per-slot diagnostic renderer, the M4
5034        /// per-axis admission webhook) reaches for the same typed
5035        /// surface rather than re-deriving the partition from inline
5036        /// flag combinations.
5037        enum CodePathFileType {
5038            /// `:exe` — nix-build derivation output, no terminating-
5039            /// extension contract (the canonical `"exe/<name>"`
5040            /// fixtures the layout's `ExeOutsideDir` error message
5041            /// documents carry no extension by convention).
5042            None,
5043            /// `:bibliotecas` — tatara-lisp source files the
5044            /// `feira build` loop reads through `tatara_lisp::read`
5045            /// at parse time. Routes to [`is_lisp_extension`].
5046            LispSource,
5047            /// `:servicos` — ComputeUnit-CR YAML files the
5048            /// caixa-helm / caixa-flux renderers consume through
5049            /// `serde_yaml::from_str`. Routes to
5050            /// [`is_computeunit_yaml_extension`].
5051            ComputeUnitYaml,
5052        }
5053
5054        // The per-slot [`CodePathFileType`] selects which axes carry the
5055        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
5056        // source axis (the `feira build` loop at
5057        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
5058        // `tatara_lisp::read` at parse time) — the lifted
5059        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
5060        // `:exe` is the nix-built executable surface (per the canonical
5061        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
5062        // error message documents and every in-tree
5063        // `caixa_with_code_paths` positive control uses) — its file-type
5064        // contract is "nix-build derivation output", not a typed source
5065        // file, so [`CodePathFileType::None`] opts the slot out of any
5066        // file-type gate. `:servicos` is the `.computeunit.yaml`
5067        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
5068        // renderers consume each entry through `serde_yaml::from_str` as
5069        // a typed `ComputeUnit` CR) — the lifted
5070        // [`is_computeunit_yaml_extension`] predicate gates the compound
5071        // `.computeunit.yaml` suffix. All three axes are surfaced through
5072        // the same iteration so the sandbox-shape + duplicate gates
5073        // apply uniformly; the typed file-type dispatch fires per-slot
5074        // exactly where the downstream consumer's accepted set demands
5075        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
5076        // compounding lift on the peer 64772a9 `:bibliotecas`
5077        // `.lisp`-gate trajectory — the second of the three code-path
5078        // axes to land on a typed compound-suffix gate, with the same
5079        // self-locating per-slot diagnostic shape every peer per-axis
5080        // file-type lift uses (`*NonLispExtension { slot, path }` /
5081        // `*NonComputeUnitYamlExtension { slot, path }`).
5082        for (slot, list, file_type) in [
5083            (
5084                ":bibliotecas",
5085                &self.bibliotecas,
5086                CodePathFileType::LispSource,
5087            ),
5088            (":exe", &self.exe, CodePathFileType::None),
5089            (
5090                ":servicos",
5091                &self.servicos,
5092                CodePathFileType::ComputeUnitYaml,
5093            ),
5094        ] {
5095            // Per-slot set-not-multiset gate on the typed code-path axis.
5096            // Every peer Vec-shaped author-supplied list past validate is
5097            // a set, not a multiset: `:membros :caixa`
5098            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
5099            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
5100            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
5101            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
5102            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
5103            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
5104            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
5105            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
5106            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
5107            // the three code-path lists are the last Vec-shaped author-
5108            // supplied slots on the typed Caixa surface still admitting a
5109            // duplicate entry silently. Scope is per-list (`:bibliotecas`
5110            // duplicates are flagged within `:bibliotecas`, not across
5111            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
5112            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
5113            // legitimate dev-vs-runtime shape on the dep axis, fenced
5114            // separately by [`crate::dep::validate_no_self_dep`]). On the
5115            // code-path axis a cross-slot collision is structurally
5116            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
5117            // fence — `:exe` and `:servicos` entries are confined to their
5118            // own directory trees, so the only way a string could appear
5119            // on two code-path lists is the (rare, structurally invalid)
5120            // case where `:bibliotecas` carries an `"exe/<x>"` or
5121            // `"servicos/<x>.yaml"`-shaped path.
5122            //
5123            // Without the gate three authoring footguns silently passed:
5124            //
5125            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
5126            //     canonical copy-paste-the-wrong-file footgun. `feira
5127            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
5128            //     list and re-parses the same file twice, wasting work
5129            //     and silently masking the author's intent to declare a
5130            //     *second* biblioteca.
5131            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
5132            //     Binario surface. The future `caixa-flake` `nix flake`
5133            //     emitter that materializes each `:exe` entry as a flake
5134            //     `packages.<exe-name>` derivation would collide on the
5135            //     duplicate package name and surface a flake-eval error
5136            //     far from the source `caixa.lisp`.
5137            //   - `:servicos ("servicos/x.computeunit.yaml"
5138            //     "servicos/x.computeunit.yaml")` — the same footgun on
5139            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
5140            //     renderers already refuse `:servicos.len() != 1` with
5141            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
5142            //     that diagnostic surfaces "too many servicos" without
5143            //     naming "duplicate entry" — the typed self-locating
5144            //     "which entry is the duplicate" framing only lands at
5145            //     this gate.
5146            //
5147            // Same `seen.insert(entry.as_str())` shape every peer per-list
5148            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
5149            // 86c769b, `:deps` 359fba5) and the same "structural shape
5150            // checks fire before the duplicate check on the same entry"
5151            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
5152            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
5153            // empty entry first, not the duplicate on the later pair).
5154            let mut seen = std::collections::HashSet::new();
5155            for entry in list {
5156                let path = Path::new(entry);
5157                match is_sandboxed_relative_path(path) {
5158                    Ok(()) => {}
5159                    Err(PathShapeViolation::Empty) => {
5160                        return Err(ManifestError::CodePathEmpty { slot });
5161                    }
5162                    Err(PathShapeViolation::Absolute) => {
5163                        return Err(ManifestError::CodePathAbsolute {
5164                            slot,
5165                            path: path.to_path_buf(),
5166                        });
5167                    }
5168                    Err(PathShapeViolation::ParentEscape) => {
5169                        return Err(ManifestError::CodePathParentEscape {
5170                            slot,
5171                            path: path.to_path_buf(),
5172                        });
5173                    }
5174                }
5175                // The per-slot file-type gate dispatched through the
5176                // typed [`CodePathFileType`] selector above. Each variant
5177                // routes to the lifted predicate the downstream consumer
5178                // demands:
5179                //
5180                //   - [`LispSource`] → [`is_lisp_extension`] for
5181                //     `:bibliotecas` (the `feira build` loop's
5182                //     `tatara_lisp::read` consumer);
5183                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
5184                //     for `:servicos` (the caixa-helm / caixa-flux
5185                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
5186                //     accepted set);
5187                //   - [`None`] for `:exe` — the nix-build derivation-
5188                //     output axis has no terminating-extension contract.
5189                //
5190                // Fires after the sandbox-shape arms so a path that is
5191                // *both* sandbox-escaping and wrong-extension surfaces
5192                // the more fundamental sandbox-shape diagnostic first
5193                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
5194                // `ParentEscape` → `NonLispExtension` arm-ordering on
5195                // `:behavior :on-*` c97815a, and `EmptyScript` →
5196                // `AbsoluteScript` → `ParentEscapeScript` →
5197                // `NonLispExtensionScript` on
5198                // `:upgrade-from :state-change :script` 33cc830), and
5199                // before the duplicate gate so the narrower per-entry
5200                // file-type shape dominates the cross-entry uniqueness
5201                // diagnostic (a
5202                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
5203                // `:servicos` surfaces
5204                // `CodePathNonComputeUnitYamlExtension` on the first
5205                // entry rather than `CodePathDuplicate` on the pair —
5206                // peer with the 64772a9 `:bibliotecas`
5207                // `("lib/x.txt" "lib/x.txt")` ordering).
5208                match file_type {
5209                    CodePathFileType::None => {}
5210                    CodePathFileType::LispSource => {
5211                        if !is_lisp_extension(path) {
5212                            return Err(ManifestError::CodePathNonLispExtension {
5213                                slot,
5214                                path: path.to_path_buf(),
5215                            });
5216                        }
5217                    }
5218                    CodePathFileType::ComputeUnitYaml => {
5219                        if !is_computeunit_yaml_extension(path) {
5220                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
5221                                slot,
5222                                path: path.to_path_buf(),
5223                            });
5224                        }
5225                    }
5226                }
5227                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
5228                    ManifestError::CodePathDuplicate {
5229                        slot,
5230                        path: path.to_path_buf(),
5231                    }
5232                })?;
5233            }
5234        }
5235        Ok(())
5236    }
5237
5238    /// Reject `:etiquetas` lists with an empty entry or with two entries
5239    /// agreeing on the same string. `:etiquetas` is the universal
5240    /// registry-search-tag axis on [`Caixa`] (every kind carries the
5241    /// `Vec<String>` slot) and lands verbatim as the Helm chart
5242    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
5243    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
5244    /// a [`std::collections::BTreeSet`] alongside the four substrate-
5245    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
5246    /// Two authoring footguns silently passed validate without this gate:
5247    ///
5248    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
5249    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
5250    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
5251    ///     `chart.metadata.keywords` admits the value without a strict
5252    ///     parser-side gate, but the empty keyword has no operational
5253    ///     meaning — it indexes nothing in the future caixa-registry
5254    ///     search axis and clutters the rendered chart with a no-op tag.
5255    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
5256    ///     copy-paste-the-wrong-tag footgun) silently passed validate
5257    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
5258    ///     at chart render — a "second wins / one silently disappears"
5259    ///     shape divergent from every peer typed-graph set gate
5260    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
5261    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
5262    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
5263    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
5264    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
5265    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
5266    ///     on `:upgrade-from`, the per-instruction-class singularity
5267    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
5268    ///     [`crate::UpgradeError::DuplicateStateChange`] /
5269    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
5270    ///     discipline is uniform: every Vec-shaped author-supplied list
5271    ///     past validate is set-not-multiset, by construction.
5272    ///
5273    /// Past the empty arm the gate enforces the chart-keyword shape
5274    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
5275    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
5276    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
5277    /// continuation. Closes the canonical paste-from-doc footguns the
5278    /// bare empty + duplicate arms left open: paste-from-aligned-doc
5279    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
5280    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
5281    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
5282    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
5283    /// — the author meant three separate list entries), path-separator
5284    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
5285    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
5286    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
5287    /// control bytes that would silently land as malformed search tags
5288    /// in the rendered Chart.yaml `keywords:` array and break the
5289    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
5290    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
5291    /// established on the sibling universal-axis `Vec<String>` surface
5292    /// — the second universal-axis Vec<String> surface to land the
5293    /// empty-first-then-shape-then-duplicate per-entry cascade.
5294    ///
5295    /// Same empty-first cascade discipline every peer per-axis gate
5296    /// uses: the per-entry empty arm fires before the per-entry shape
5297    /// arm fires before the cross-entry duplicate arm, so an
5298    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
5299    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
5300    /// has no value" defect) before either the shape or the duplicate
5301    /// diagnostic. Walks the list in declaration order so the
5302    /// first-collision diagnostic surfaces the lexicographically-
5303    /// earliest offending position, peer with every other duplicate
5304    /// gate on this surface.
5305    ///
5306    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
5307    /// caixa-build gate alongside the peer universal gates
5308    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5309    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
5310    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
5311    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5312    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5313    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
5314    /// slot sets. The future caixa-registry search axis can reach for
5315    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
5316    /// chart-keyword-shaped string without re-deriving the precondition.
5317    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
5318        let mut seen = std::collections::HashSet::new();
5319        for etiqueta in self.etiquetas() {
5320            if etiqueta.is_empty() {
5321                return Err(ManifestError::EtiquetaEmpty);
5322            }
5323            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
5324                ManifestError::EtiquetaInvalid {
5325                    etiqueta: etiqueta.clone(),
5326                    reason,
5327                }
5328            })?;
5329            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
5330                ManifestError::EtiquetaDuplicate {
5331                    etiqueta: etiqueta.clone(),
5332                }
5333            })?;
5334        }
5335        Ok(())
5336    }
5337
5338    /// Reject `:autores` lists with an empty entry or with two entries
5339    /// agreeing on the same string. `:autores` is the universal
5340    /// maintainer-axis on [`Caixa`] (every kind carries the
5341    /// `Vec<String>` slot) and lands verbatim as the Helm chart
5342    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
5343    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
5344    /// to a `Maintainer { name, email: None }` without dedup). Two
5345    /// authoring footguns silently passed validate without this gate:
5346    ///
5347    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
5348    ///     blank-doc footgun) rendered as
5349    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
5350    ///     empty maintainer name has no operational meaning — it
5351    ///     identifies no one in the substrate's authorship index and
5352    ///     clutters the rendered chart with a no-op maintainer.
5353    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
5354    ///     the copy-paste-the-wrong-author footgun) silently passed
5355    ///     validate and rendered as two identical maintainer entries.
5356    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
5357    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
5358    ///     rendered `keywords:` array at chart-render time), the
5359    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
5360    ///     entries stack verbatim in the chart, divergent from every
5361    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
5362    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
5363    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
5364    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
5365    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
5366    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
5367    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
5368    ///     `:etiquetas`).
5369    ///
5370    /// Past the empty arm the gate enforces the chart-maintainer-name
5371    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
5372    /// the structural single-line printable-UTF-8 floor every realistic
5373    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
5374    /// or trailing whitespace, no ASCII control characters anywhere,
5375    /// Unicode bytes accepted. Closes the canonical paste-from-doc
5376    /// footguns the bare empty + duplicate arms left open:
5377    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
5378    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
5379    /// pasted a multi-line block of author records into one `:autores`
5380    /// entry instead of splitting into one entry per author),
5381    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
5382    /// and the paste-from-binary-blob control bytes that would silently
5383    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
5384    /// `maintainers:` array. Mirrors the shape-predicate cascade
5385    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5386    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
5387    /// establish past their own empty arms on the sibling universal-axis
5388    /// `Option<String>` surfaces — the first universal-axis Vec<String>
5389    /// surface to land the empty-first-then-shape-then-duplicate per-entry
5390    /// cascade.
5391    ///
5392    /// Same empty-first cascade discipline every peer per-axis gate
5393    /// uses: the per-entry empty arm fires before the per-entry shape
5394    /// arm before the cross-entry duplicate arm. Walks the list in
5395    /// declaration order so the first-collision diagnostic surfaces the
5396    /// lexicographically-earliest offending position, peer with every
5397    /// other duplicate gate on this surface.
5398    ///
5399    /// Universal-axis (every kind carries `:autores`), so wired at the
5400    /// caixa-build gate alongside the peer universal gates
5401    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5402    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5403    /// [`Self::validate_code_paths`] — before the kind-coherence gates
5404    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5405    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5406    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5407    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
5408    /// slot sets.
5409    pub fn validate_autores(&self) -> Result<(), ManifestError> {
5410        let mut seen = std::collections::HashSet::new();
5411        for autor in self.autores() {
5412            if autor.is_empty() {
5413                return Err(ManifestError::AutorEmpty);
5414            }
5415            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
5416                ManifestError::AutorInvalid {
5417                    autor: autor.clone(),
5418                    reason,
5419                }
5420            })?;
5421            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
5422                ManifestError::AutorDuplicate {
5423                    autor: autor.clone(),
5424                }
5425            })?;
5426        }
5427        Ok(())
5428    }
5429
5430    /// Reject `:repositorio` values whose shape the shared
5431    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
5432    /// `repositorio: Option<String>` slot on [`Caixa`] is the
5433    /// universal git-shaped homepage axis every kind carries — the
5434    /// substrate routes the same string through two load-bearing
5435    /// consumers:
5436    ///
5437    ///   - [`caixa-helm`] folds it verbatim into the rendered
5438    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
5439    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
5440    ///     the chart `README.md` `repo = …` interpolation
5441    ///     (`caixa-helm/src/lib.rs:359`).
5442    ///   - [`caixa-flux`] folds it verbatim into the standalone
5443    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
5444    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
5445    ///     `GitRepository.spec.url` the cluster's source-controller
5446    ///     polls — the load-bearing deploy-time axis.
5447    ///
5448    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
5449    /// substitute a placeholder when the slot is absent (`None` → the
5450    /// fallback fires); a `Some("")` *skips the fallback* and silently
5451    /// passes the empty string through to `Chart.yaml home: ""` /
5452    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
5453    /// controller both reject the empty URL far from the source
5454    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
5455    /// Similarly a malformed `:repositorio` (whitespace, control char,
5456    /// missing `:` separator, leading `-`) silently lands in the
5457    /// rendered artifacts and breaks at `git clone` / `helm template`
5458    /// / `flux reconcile` time.
5459    ///
5460    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
5461    /// same shared predicate the peer [`crate::DepSource::validate`]
5462    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
5463    /// gate the two `git URL`-shaped surfaces on the typed Caixa
5464    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
5465    /// structurally equivalent: every value past validate is
5466    /// guaranteed-acceptable by the predicate's union of constraints
5467    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
5468    /// control chars, ASCII only, no leading `:`, contains a `:`
5469    /// separator). The predicate accepts every documented authoring
5470    /// shape — `github:org/repo` shorthand, `https://host/path`,
5471    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
5472    /// scp-style SSH, `file:///path` — and refuses the canonical
5473    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
5474    /// injection footguns at validate time. Maps the predicate's
5475    /// `String` reason verbatim into the
5476    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
5477    /// offending value + parser-shaped reason so the diagnostic is
5478    /// self-locating (the author can grep their `caixa.lisp` for
5479    /// `:repositorio "<value>"` and fix it in one edit).
5480    ///
5481    /// `None` (the canonical "omit the slot to express no published
5482    /// homepage" shape) is accepted trivially — the gate is a no-op
5483    /// when the author didn't declare a value. `Some("")` is gated by
5484    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
5485    /// shape predicate is consulted, mirroring the empty-first cascade
5486    /// every peer per-axis identity gate uses
5487    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
5488    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
5489    /// [`crate::DepError::FonteRepoEmpty`] →
5490    /// [`crate::DepError::FonteRepoInvalid`]).
5491    ///
5492    /// Universal-axis (every kind carries `:repositorio`), so wired at
5493    /// the caixa-build gate alongside the peer universal gates
5494    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5495    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5496    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
5497    /// before the kind-coherence gates
5498    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5499    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5500    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5501    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5502    /// specific slot sets.
5503    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
5504        let Some(s) = self.repositorio() else {
5505            return Ok(());
5506        };
5507        if s.is_empty() {
5508            return Err(ManifestError::RepositorioEmpty);
5509        }
5510        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
5511            repositorio: s.to_string(),
5512            reason,
5513        })
5514    }
5515
5516    /// Reject `:descricao` values that are the empty string. The flat
5517    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
5518    /// free-form-prose homepage axis every kind carries — the
5519    /// substrate routes the same string through two load-bearing
5520    /// consumers in the [`caixa-helm`] renderer:
5521    ///
5522    ///   - `build_chart_yaml` folds it verbatim into the rendered
5523    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
5524    ///     field (`caixa-helm/src/lib.rs:232-235`).
5525    ///   - `build_readme` folds it verbatim into the rendered chart
5526    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
5527    ///
5528    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
5529    /// substitute a `caixa.nome`-derived placeholder when the slot is
5530    /// absent (`None` → the fallback fires); a `Some("")` *skips the
5531    /// fallback* and silently passes the empty string through to
5532    /// `Chart.yaml description: ""` / a blank chart `README.md`
5533    /// header. Helm's chart spec requires a non-empty `description:`
5534    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
5535    /// `WARNING [chart.metadata.description]: description is required`),
5536    /// so the empty `Some("")` silently lands in the rendered
5537    /// artifacts and breaks at `helm lint` / `helm install` time far
5538    /// from the source `caixa.lisp`, with no field naming the
5539    /// offending `:descricao`.
5540    ///
5541    /// `None` (the canonical "omit the slot to defer to the renderer's
5542    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
5543    /// the gate is a no-op when the author didn't declare a value.
5544    /// `Some("")` is gated by the narrower
5545    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
5546    /// shape every peer per-axis empty gate uses
5547    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5548    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5549    /// [`ManifestError::RepositorioEmpty`]).
5550    ///
5551    /// Universal-axis (every kind carries `:descricao`), so wired at
5552    /// the caixa-build gate alongside the peer universal gates
5553    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5554    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5555    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5556    /// [`Self::validate_code_paths`] — before the kind-coherence
5557    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5558    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5559    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5560    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5561    /// specific slot sets.
5562    ///
5563    /// Past the empty arm the gate enforces the chart-description
5564    /// shape predicate via [`crate::render::is_chart_description_shape`]:
5565    /// the structural single-line UTF-8 floor every realistic chart
5566    /// description in the wild matches — 1..=512 bytes, no leading
5567    /// or trailing whitespace, no ASCII control characters anywhere
5568    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
5569    /// carriage return, and every other control byte), Unicode
5570    /// continuation bytes accepted (the canonical fixtures carry
5571    /// `→` and `—`). Closes the canonical paste-from-doc footguns
5572    /// the bare empty-arm gate left open: paste-from-aligned-doc
5573    /// leading / trailing whitespace (`" Checkout flow."`,
5574    /// `"Checkout flow. "`), paste-from-multiline-doc newline
5575    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
5576    /// (`"Checkout\rflow."`), tab-from-aligned-doc
5577    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
5578    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
5579    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
5580    /// [`Self::validate_edicao`] establish past their own empty arms
5581    /// on the sibling universal-axis `Option<String>` Caixa-level
5582    /// value-shape surfaces.
5583    ///
5584    /// The empty-first cascade discipline mirrors every peer per-axis
5585    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
5586    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
5587    /// diagnostic surfaces on `Some("")` rather than the broader
5588    /// shape-predicate diagnostic — peer with how
5589    /// [`ManifestError::LicencaEmpty`] runs before
5590    /// [`ManifestError::LicencaInvalid`],
5591    /// [`ManifestError::EdicaoEmpty`] runs before
5592    /// [`ManifestError::EdicaoInvalid`],
5593    /// [`ManifestError::RepositorioEmpty`] runs before
5594    /// [`ManifestError::RepositorioInvalid`].
5595    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
5596        let Some(s) = self.descricao() else {
5597            return Ok(());
5598        };
5599        if s.is_empty() {
5600            return Err(ManifestError::DescricaoEmpty);
5601        }
5602        crate::render::is_chart_description_shape(s).map_err(|reason| {
5603            ManifestError::DescricaoInvalid {
5604                descricao: s.to_string(),
5605                reason,
5606            }
5607        })?;
5608        Ok(())
5609    }
5610
5611    /// Reject `:licenca` values that are the empty string. The flat
5612    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
5613    /// SPDX-shaped license-expression axis every kind carries — the
5614    /// substrate routes the same string through the [`caixa-helm`]
5615    /// renderer's `build_readme` which folds it verbatim into the
5616    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
5617    /// section (`caixa-helm/src/lib.rs:361`) via
5618    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
5619    /// fallback only fires on `None`; a `Some("")` *skips the
5620    /// fallback* and silently passes the empty string through to a
5621    /// chart `README.md` whose `License` section renders as the bare
5622    /// trailing period (`.\n`) — peer footgun with the
5623    /// `Some("")`-skips-`unwrap_or_else` shape the
5624    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
5625    /// gates close on the sibling free-form-prose and git-URL axes.
5626    ///
5627    /// `None` (the canonical "omit the slot to defer to the
5628    /// renderer's `MIT` fallback" shape every existing fixture
5629    /// carries) is accepted trivially — the gate is a no-op when the
5630    /// author didn't declare a value. `Some("")` is gated by the
5631    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
5632    /// empty-arm shape every peer per-axis empty gate uses
5633    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5634    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5635    /// [`ManifestError::RepositorioEmpty`],
5636    /// [`ManifestError::DescricaoEmpty`]).
5637    ///
5638    /// Universal-axis (every kind carries `:licenca`), so wired at
5639    /// the caixa-build gate alongside the peer universal gates
5640    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5641    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5642    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5643    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
5644    /// — before the kind-coherence gates
5645    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5646    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5647    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5648    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5649    /// specific slot sets.
5650    ///
5651    /// Past the empty arm the gate enforces the SPDX-expression shape
5652    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
5653    /// structural alphabet floor every realistic SPDX expression in
5654    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
5655    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
5656    /// single ASCII space (token separator). Closes the canonical
5657    /// paste-from-doc footguns the bare empty-arm gate left open:
5658    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
5659    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
5660    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
5661    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
5662    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
5663    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
5664    /// Apache-2.0"`), and semicolon-list-separator confusion
5665    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
5666    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
5667    /// establish past their own empty arms.
5668    ///
5669    /// The empty-first cascade discipline mirrors every peer per-axis
5670    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
5671    /// [`ManifestError::LicencaInvalid`], so the narrower empty
5672    /// diagnostic surfaces on `Some("")` rather than the broader
5673    /// shape-predicate diagnostic — peer with how
5674    /// [`ManifestError::EdicaoEmpty`] runs before
5675    /// [`ManifestError::EdicaoInvalid`],
5676    /// [`ManifestError::RepositorioEmpty`] runs before
5677    /// [`ManifestError::RepositorioInvalid`].
5678    ///
5679    /// A future tightening on this axis can extend the alphabet
5680    /// floor into a full SPDX expression parser + license-id
5681    /// allowlist (rejecting alphabet-valid values that don't name a
5682    /// real SPDX license identifier — e.g., `"NotAReal"` is
5683    /// alphabet-valid but no `NotAReal` license-id exists). That
5684    /// parser only becomes meaningful past a real SPDX-spec
5685    /// dependency; this gate establishes the structural floor by
5686    /// refusing every non-SPDX-alphabet value at validate time.
5687    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
5688        let Some(s) = self.licenca() else {
5689            return Ok(());
5690        };
5691        if s.is_empty() {
5692            return Err(ManifestError::LicencaEmpty);
5693        }
5694        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
5695            ManifestError::LicencaInvalid {
5696                licenca: s.to_string(),
5697                reason,
5698            }
5699        })?;
5700        Ok(())
5701    }
5702
5703    /// Reject `:edicao` values that are the empty string. The flat
5704    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
5705    /// language-edition axis every kind carries — it determines the
5706    /// tatara-lisp macro surface + compatibility flags the substrate
5707    /// applies when building a caixa, and lands verbatim in the
5708    /// `Caixa::template` author-time scaffold (the canonical
5709    /// `:edicao "2026"` line every `feira init` emits via
5710    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
5711    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
5712    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
5713    /// `caixa-core/src/render.rs:2510`) via
5714    /// `edicao: Some("2026".into())`.
5715    ///
5716    /// `None` (the canonical "omit the slot to defer to the
5717    /// substrate's default edition" shape every existing
5718    /// [`caixa-resolver`] integration test fixture carries via
5719    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5720    /// is accepted trivially — the gate is a no-op when the author
5721    /// didn't declare a value. `Some("")` is gated by the narrower
5722    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
5723    /// shape every peer per-axis empty gate uses
5724    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
5725    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
5726    /// [`ManifestError::RepositorioEmpty`],
5727    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
5728    ///
5729    /// Universal-axis (every kind carries `:edicao`), so wired at
5730    /// the caixa-build gate alongside the peer universal gates
5731    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
5732    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
5733    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
5734    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
5735    /// [`Self::validate_code_paths`] — before the kind-coherence
5736    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5737    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5738    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
5739    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
5740    /// specific slot sets.
5741    ///
5742    /// Past the empty arm the gate enforces the canonical year-shape
5743    /// predicate: every documented tatara-lisp edition is a 4-digit
5744    /// ASCII decimal year (`"2026"` is the only edition currently
5745    /// minted; future-introduced siblings will follow the same
5746    /// shape, peer with Cargo's `[package] edition` grammar which
5747    /// every value Cargo has ever accepted matches — `"2015"`,
5748    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
5749    /// 4 ASCII decimal bytes is rejected with the narrower
5750    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
5751    /// shape-predicate cascade [`Self::validate_repositorio`]
5752    /// establishes past its own empty arm
5753    /// ([`ManifestError::RepositorioEmpty`] →
5754    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
5755    /// paste-from-doc footguns the bare empty-arm gate left open:
5756    ///
5757    ///   - leading / trailing whitespace from a paste-from-doc
5758    ///     (`"2026 "`, `" 2026"`)
5759    ///   - control characters / CRLF from a paste-from-multiline-doc
5760    ///     (`"2026\n"`)
5761    ///   - non-ASCII look-alikes from a fullwidth keyboard
5762    ///     (`"2026"`) which would silently land as a non-ASCII
5763    ///     string in the rendered caixa.lisp
5764    ///   - free-form non-year values (`"x"`, `"latest"`,
5765    ///     `"nightly"`) that have no operational meaning on the
5766    ///     substrate's build-time edition selector
5767    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
5768    ///     `"r2026"`) — common version-tag idioms that don't apply
5769    ///     to the year-shaped edition axis
5770    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
5771    ///     edition is a year, not a fractional version
5772    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
5773    ///     `"00026"`) that don't name a year
5774    ///
5775    /// `None` (the canonical "omit the slot to defer to the
5776    /// substrate's default edition" shape every existing
5777    /// [`caixa-resolver`] integration test fixture carries via
5778    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
5779    /// is accepted trivially — the gate is a no-op when the author
5780    /// didn't declare a value. The empty-first cascade discipline
5781    /// mirrors every peer per-axis identity gate:
5782    /// [`ManifestError::EdicaoEmpty`] runs before
5783    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
5784    /// diagnostic surfaces on `Some("")` rather than the broader
5785    /// shape-predicate diagnostic — peer with how
5786    /// [`ManifestError::NomeEmpty`] runs before
5787    /// [`ManifestError::NomeInvalid`],
5788    /// [`ManifestError::VersaoEmpty`] runs before
5789    /// [`ManifestError::VersaoInvalid`],
5790    /// [`ManifestError::RepositorioEmpty`] runs before
5791    /// [`ManifestError::RepositorioInvalid`].
5792    ///
5793    /// A future tightening on this axis can extend the shape
5794    /// predicate into a known-edition allowlist (rejecting
5795    /// year-shaped values that don't name a tatara-lisp edition
5796    /// the substrate actually understands — e.g., `"1999"` is
5797    /// year-shaped but no `1999` edition exists). That allowlist
5798    /// only becomes meaningful past the introduction of a sibling
5799    /// edition to `"2026"`; this gate establishes the structural
5800    /// floor by refusing every non-year-shaped value at validate
5801    /// time.
5802    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
5803        let Some(s) = self.edicao() else {
5804            return Ok(());
5805        };
5806        if s.is_empty() {
5807            return Err(ManifestError::EdicaoEmpty);
5808        }
5809        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
5810            return Err(ManifestError::EdicaoInvalid {
5811                edicao: s.to_string(),
5812                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
5813            });
5814        }
5815        Ok(())
5816    }
5817
5818    /// Compose the supervisor-related flat slots into a single
5819    /// [`SupervisorSpec`] for validation. Returns `None` when the
5820    /// caixa isn't a `:kind Supervisor`.
5821    ///
5822    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
5823    /// simple (one form, no nested `:supervisor (…)` block); this view
5824    /// is the "typed shape" the operator + supervisor reconciler
5825    /// consume.
5826    #[must_use]
5827    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
5828        if !self.kind().is_supervisor() {
5829            return None;
5830        }
5831        // Fold through the shared `supervisor::duration_codec::parse`
5832        // — the same parser the serde-routed `with = "duration_codec"`
5833        // on `SupervisorSpec::restart_window`, the `:politicas
5834        // :timeout` codec, and the `:politicas :circuit-breaker
5835        // :window` codec all consume. The prior inline f64-shaped
5836        // duplicate (`parse_window_inline`) admitted every magnitude
5837        // the integer-magnitude gate (1c55a2a) rejects on the three
5838        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
5839        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
5840        // `None` (i.e. "no reset"), divergent from the shared codec's
5841        // integer-magnitude discipline by construction. The fold
5842        // closes the divergence: every value the typed
5843        // `SupervisorSpec` carries past `supervisor_view` is in the
5844        // shared codec's accepted set. The `.ok()` here preserves the
5845        // existing soft-swallow shape on this view-construction path;
5846        // the new [`Caixa::validate_restart_window`] (sibling of
5847        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
5848        // the offending raw string at build time so authoring tools
5849        // (`feira lint`, the future layout-side wire-up) surface a
5850        // self-locating diagnostic instead of a silently dropped
5851        // window.
5852        let restart_window = self
5853            .restart_window()
5854            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
5855        Some(SupervisorSpec {
5856            // Route the author-omitted `:estrategia` arm through the
5857            // substrate-canonical
5858            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
5859            // `pub const` rather than the transitively-derived
5860            // [`RestartStrategy::default`] route the prior
5861            // `.unwrap_or_default()` fold reached for — one source of
5862            // truth for the Erlang/OTP `one_for_one` half of Learn You
5863            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
5864            // supervisor canonical default that also backs the
5865            // [`crate::supervisor::Default for RestartStrategy`] impl
5866            // and the [`crate::supervisor::Default for SupervisorSpec`]
5867            // impl's struct-literal `estrategia` field, all now routed
5868            // through the same lifted constant. Prior to the lift the
5869            // composition site carried `.unwrap_or_default()` with no
5870            // compile-time link back to the shared OTP-canonical
5871            // default that the peer paired
5872            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
5873            // arm on the sibling `:max-restarts` axis routes through —
5874            // so a future rebrand of the OTP-canonical strategy default
5875            // (a widening to `rest_for_one` once the substrate
5876            // discovers startup-order-coupled child cohorts as the more
5877            // common shape, a per-cluster overlay the operator pins
5878            // through the MESH-COMPOSITION §III.2 supervision-canary
5879            // `:estrategia-overrides` roadmap slot) would have had to
5880            // migrate the paired `MaxIntensity` + `Period` halves
5881            // through the lifted constants and the `one_for_one` half
5882            // through a `RestartStrategy::default()` route in lockstep
5883            // or the three halves of the same OTP-canonical default
5884            // would silently drift out of pairing. Byte-parity against
5885            // the lifted constant closes the split. Pinned by
5886            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
5887            // in the tests module.
5888            estrategia: self
5889                .estrategia()
5890                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
5891            // Route the author-omitted `:max-restarts` arm through the
5892            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
5893            // typed `pub const` rather than the raw `5` literal — one
5894            // source of truth for the Erlang/OTP-canonical
5895            // `{intensity, 5, 60}` `MaxIntensity` default that also
5896            // backs the serde-side wire-format author-omitted arm on
5897            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
5898            // `#[serde(default = "default_max_restarts")]` and the
5899            // [`Default for SupervisorSpec`] impl's struct-literal
5900            // default field. Prior to the lift the composition site
5901            // carried a raw `5` with no compile-time link back to the
5902            // serde-side default, so a future rebrand of the OTP-
5903            // canonical default (a tightening to Elixir's `3`, a
5904            // widening to a per-cluster overlay the operator pins
5905            // through the MESH-COMPOSITION §III.2 supervision-canary
5906            // `:supervisor :max-restarts-overrides` roadmap slot)
5907            // would have had to be threaded through both open-coded
5908            // copies in lockstep or the wire-format author-omitted arm
5909            // and this view-construction author-omitted arm would
5910            // silently disagree on which restart-budget an omitted
5911            // `:max-restarts` resolves to. Pinned by
5912            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
5913            // in the tests module.
5914            max_restarts: self
5915                .max_restarts()
5916                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
5917            restart_window,
5918            children: self.children().to_vec(),
5919        })
5920    }
5921
5922    /// A minimal starter manifest emitted by `feira init`.
5923    #[must_use]
5924    pub fn template(nome: &str) -> String {
5925        format!(
5926            "(defcaixa\n  \
5927               :nome        {nome:?}\n  \
5928               :versao      \"0.1.0\"\n  \
5929               :kind        Biblioteca\n  \
5930               :edicao      \"2026\"\n  \
5931               :descricao   \"FIXME — describe this caixa\"\n  \
5932               :autores     ()\n  \
5933               :etiquetas   ()\n  \
5934               :deps        ()\n  \
5935               :deps-dev    ()\n  \
5936               :bibliotecas (\"lib/{nome}.lisp\"))\n"
5937        )
5938    }
5939
5940    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
5941    /// back after mutation (e.g. `feira add`).
5942    ///
5943    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
5944    /// The derive-macro `compile_from_sexp` path is the inverse, so any
5945    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
5946    #[must_use]
5947    pub fn to_lisp(&self) -> String {
5948        let json = serde_json::to_value(self).expect("Caixa serialize");
5949        let sexp = tatara_lisp::domain::json_to_sexp(&json);
5950        let tatara_lisp::Sexp::List(items) = sexp else {
5951            return format!("(defcaixa {sexp})\n");
5952        };
5953        let mut out = String::from("(defcaixa");
5954        let mut i = 0;
5955        while i + 1 < items.len() {
5956            out.push_str("\n  ");
5957            out.push_str(&items[i].to_string());
5958            out.push(' ');
5959            out.push_str(&items[i + 1].to_string());
5960            i += 2;
5961        }
5962        out.push_str(")\n");
5963        out
5964    }
5965}
5966
5967/// Errors raised by top-level [`Caixa`] validators that don't fit
5968/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
5969/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
5970/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
5971/// through every substrate-side artifact's `metadata.name` /
5972/// version derivation.
5973///
5974/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
5975/// doc-comment anticipates) can hold one of each per-axis error
5976/// family without reshaping individual diagnostics; this enum is
5977/// the first such per-Caixa-identity family.
5978#[derive(Debug, Error, PartialEq, Eq)]
5979pub enum ManifestError {
5980    #[error(
5981        ":nome is empty (every caixa must name itself; the value flows \
5982         into every K8s artifact's `metadata.name` derivation and into \
5983         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
5984    )]
5985    NomeEmpty,
5986    #[error(
5987        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
5988         apiserver enforces this rule on every `metadata.name` the \
5989         caixa's substrate-side renderers derive from `:nome` — the \
5990         `lareira-<nome>` Helm chart name, the programs.yaml entry \
5991         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
5992         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
5993         name; use a lowercase alphanumeric + hyphen identifier like \
5994         `\"checkout\"` or `\"cart-v2\"`)"
5995    )]
5996    NomeInvalid { nome: String, reason: String },
5997    #[error(
5998        ":nome {nome:?} overflows the joint-length budget on the canonical \
5999         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
6000         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
6001         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
6002         `chart:` slot, `caixa-tatara`'s `release_name` + \
6003         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
6004         joint name through the canonical `lareira_chart_name` helper, and \
6005         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
6006         DNS-1123 label cap on every chart-name-derived `metadata.name` \
6007         reject any joint name exceeding 63 bytes; the narrower \
6008         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
6009         arm gates the chart-name budget downstream renderers inherit)"
6010    )]
6011    NomeChartNameBudgetExceeded { nome: String, reason: String },
6012    #[error(
6013        ":versao is empty (every caixa must pin its own version; the value flows \
6014         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
6015         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
6016         `:latest` tags, the lacre closure's `concrete_versao`, and the \
6017         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
6018    )]
6019    VersaoEmpty,
6020    #[error(
6021        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
6022         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
6023         with optional `-prerelease` and `+build` — across every artifact derived \
6024         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
6025         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
6026         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
6027         and the `:upgrade-from :from` peers that match against this exact shape; \
6028         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
6029         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
6030         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
6031    )]
6032    VersaoInvalid { versao: String, reason: String },
6033    #[error(
6034        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
6035         substrate consumes this string through the shared \
6036         `supervisor::duration_codec` — the same parser routed via `with = \
6037         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
6038         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
6039         the canonical authoring form is `<integer><unit>` where the unit is one \
6040         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
6041         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
6042         Without this gate a malformed `:restart-window` silently produced a \
6043         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
6044         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
6045         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
6046         layer with the offending value named verbatim. Omit the slot entirely to \
6047         express \"no reset\"; carry a positive integer duration to express the \
6048         sliding window)"
6049    )]
6050    RestartWindowMalformed {
6051        restart_window: String,
6052        reason: String,
6053    },
6054    #[error(
6055        "{slot} entry is an empty path string — every {slot} entry must name \
6056         a file relative to the caixa root; omit the entry to omit the file \
6057         (the layout checker's `root.join(\"\")` resolves to the caixa root \
6058         itself, so an empty entry silently aliases the project root as a \
6059         declared {slot} file, then fails downstream at parse / existence \
6060         time with a diagnostic that names the root rather than the offending \
6061         entry)"
6062    )]
6063    CodePathEmpty { slot: &'static str },
6064    #[error(
6065        "{slot} entry {} is an absolute path — entries must be relative to \
6066         the caixa root, since `Path::join` replaces the base with an absolute \
6067         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
6068         outside the caixa root sandbox; rewrite the entry as a relative path \
6069         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
6070         `\"servicos/<name>.computeunit.yaml\"`)",
6071        path.display()
6072    )]
6073    CodePathAbsolute { slot: &'static str, path: PathBuf },
6074    #[error(
6075        "{slot} entry {} contains a `..` component — entries must not traverse \
6076         above the caixa root (the layout's `starts_with(<dir>)` fence on \
6077         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
6078         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
6079         has no such fence, so a leading `..` escapes unconditionally if the \
6080         resolved target happens to exist)",
6081        path.display()
6082    )]
6083    CodePathParentEscape { slot: &'static str, path: PathBuf },
6084    #[error(
6085        "{slot} entry {} does not terminate in the `.lisp` extension — every \
6086         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
6087         loop reads through `tatara_lisp::read` at parse time, so any other \
6088         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
6089         structurally a parser error far from the source caixa.lisp, with \
6090         no field naming the offending `:bibliotecas` entry. Pin a relative \
6091         path under the caixa root whose terminating extension is \
6092         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
6093         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
6094         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
6095         (33cc830) axes already carry through the same lifted \
6096         `is_lisp_extension` predicate",
6097        path.display()
6098    )]
6099    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
6100    #[error(
6101        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
6102         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
6103         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
6104         through `serde_yaml::from_str` at chart / FluxCD bundle render \
6105         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
6106         off-by-one-segment `.computeunit-yaml`, the editor-backup \
6107         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
6108         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
6109         source caixa.lisp, with no field naming the offending `:servicos` \
6110         entry. Pin a relative path under the caixa root whose terminating \
6111         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
6112         `\"servicos/<name>.computeunit.yaml\"`, \
6113         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
6114         contract the sibling `:bibliotecas` axis (64772a9) already carries \
6115         on the tatara-lisp-source axis through the peer lifted \
6116         `is_lisp_extension` predicate, here on the compound-suffix axis \
6117         `Path::extension` can't express on its own through the lifted \
6118         `is_computeunit_yaml_extension` predicate",
6119        path.display()
6120    )]
6121    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
6122    #[error(
6123        "{slot} entry {} appears more than once (the code-path list is \
6124         a set, not a multiset; every peer Vec-shaped author-supplied \
6125         list past validate is set-not-multiset — `:membros :caixa`, \
6126         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6127         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
6128         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
6129         code-path lists are the last Vec-shaped author-supplied slots on \
6130         the typed Caixa surface still admitting a duplicate entry. \
6131         `:bibliotecas` duplicates re-parse the same file at \
6132         `feira build` time and silently mask the author's intent to \
6133         declare a *second* biblioteca; `:exe` duplicates collide on the \
6134         flake `packages.<name>` derivation key at the future \
6135         `caixa-flake` materializer; `:servicos` duplicates surface as the \
6136         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
6137         rejection far from the source `caixa.lisp`. Drop the duplicate \
6138         or rename it to the actual second file intended)",
6139        path.display()
6140    )]
6141    CodePathDuplicate { slot: &'static str, path: PathBuf },
6142    #[error(
6143        ":etiquetas entry is empty (every tag must carry a non-empty \
6144         registry-search identifier; the empty entry has no operational \
6145         meaning — it indexes nothing in the future caixa-registry search \
6146         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
6147         with a no-op tag; omit the entry to express \"no tag on this \
6148         position\")"
6149    )]
6150    EtiquetaEmpty,
6151    #[error(
6152        ":etiquetas entry {etiqueta:?} appears more than once (the \
6153         registry-search tag set is a set, not a multiset; duplicate \
6154         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
6155         at chart render — a \"second wins / one silently disappears\" \
6156         shape divergent from every peer typed-graph set gate \
6157         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
6158         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
6159         duplicate or rename it to the actual tag intended)"
6160    )]
6161    EtiquetaDuplicate { etiqueta: String },
6162    #[error(
6163        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
6164         {reason} (the substrate consumes this string through the shared \
6165         `crate::render::is_chart_keyword_shape` predicate — the same \
6166         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
6167         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
6168         continuation. The canonical authoring shapes are short kebab-case \
6169         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
6170         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
6171         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
6172         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
6173         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
6174         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
6175         `\"mesh,http,grpc\"` — the author meant to author three separate \
6176         list entries; path-separator confusion `\"caixa/servico\"`; \
6177         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
6178         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
6179         `\"café\"` — every legitimate search tag is strict ASCII; \
6180         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
6181         passed `from_lisp` + `validate_etiquetas` + \
6182         `StandardLayout::verify` and landed in the rendered \
6183         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
6184         malformed search tag — Artifact Hub's keyword index + the future \
6185         caixa-registry's keyword index would either silently drop the \
6186         tag or fail to index it far from the source caixa.lisp; the gate \
6187         moves the diagnostic to the manifest layer with the offending \
6188         value named verbatim)"
6189    )]
6190    EtiquetaInvalid { etiqueta: String, reason: String },
6191    #[error(
6192        ":autores entry is empty (every maintainer must carry a non-empty \
6193         identifier; the empty entry has no operational meaning — it \
6194         identifies no one in the substrate's authorship index and renders \
6195         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
6196         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
6197         omit the entry to express \"no maintainer on this position\")"
6198    )]
6199    AutorEmpty,
6200    #[error(
6201        ":autores entry {autor:?} appears more than once (the maintainer \
6202         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
6203         `maintainers:` rendering does *no* dedup — duplicate entries \
6204         stack verbatim in `Chart.yaml` as two identical \
6205         `Maintainer {{ name, email: None }}` records, divergent from every \
6206         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
6207         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6208         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
6209         rename it to the actual author intended)"
6210    )]
6211    AutorDuplicate { autor: String },
6212    #[error(
6213        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
6214         {reason} (the substrate consumes this string through the shared \
6215         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
6216         single-line-UTF-8 floor every realistic chart maintainer name carries: \
6217         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
6218         characters anywhere, Unicode bytes accepted. The canonical authoring \
6219         shapes are short single-line identifiers like `\"pleme-io\"`, \
6220         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
6221         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
6222         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
6223         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
6224         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
6225         records into one entry instead of splitting into one entry per author; \
6226         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
6227         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
6228         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
6229         `validate_autores` + `StandardLayout::verify` and landed in the \
6230         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
6231         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
6232         round-trip — every chart-aware UI (`helm list`, `helm search`, \
6233         Artifact Hub maintainer index) would render the maintainer name in a \
6234         single-line column far from the source caixa.lisp; the gate moves the \
6235         diagnostic to the manifest layer with the offending value named \
6236         verbatim)"
6237    )]
6238    AutorInvalid { autor: String, reason: String },
6239    #[error(
6240        ":repositorio is the empty string (every published caixa names its \
6241         git source via a non-empty `:repositorio` locator — the value \
6242         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
6243         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
6244         `GitRepository.spec.url` via `caixa-flux`'s \
6245         `ClusterBundleOpts::for_caixa`; both consumers' \
6246         `Option::unwrap_or_else` fallbacks only fire when the slot is \
6247         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
6248         `url: \"\"` in the rendered artifacts and breaks at `helm \
6249         template` / FluxCD source-controller reconcile time far from the \
6250         source caixa.lisp; omit the slot entirely to defer to the \
6251         renderer's `https://github.com/pleme-io/<nome>` / \
6252         `caixa.nome`-derived fallback, or carry a canonical authoring \
6253         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
6254         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
6255         `\"file:///path\"`)"
6256    )]
6257    RepositorioEmpty,
6258    #[error(
6259        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
6260         (the substrate consumes this string through the shared \
6261         `crate::render::is_git_repo_url` predicate — the same parser the \
6262         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
6263         value through via `DepSource::validate`; the canonical authoring \
6264         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
6265         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
6266         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
6267         scp-style SSH form. Without this gate a malformed `:repositorio` \
6268         (whitespace from a paste-from-doc; control characters / CRLF \
6269         from a paste-from-multiline-doc; a leading `-` from a \
6270         CLI-argument-injection footgun; a missing `:` separator from a \
6271         bare `org/repo` shape git treats as a relative filesystem path) \
6272         silently landed in the rendered `Chart.yaml home:` and the \
6273         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
6274         FluxCD reconcile time far from the source caixa.lisp; the gate \
6275         moves the diagnostic to the manifest layer with the offending \
6276         value named verbatim)"
6277    )]
6278    RepositorioInvalid { repositorio: String, reason: String },
6279    #[error(
6280        ":descricao is the empty string (every published caixa names \
6281         its purpose via a non-empty `:descricao` summary — the value \
6282         flows verbatim into the rendered `lareira-<nome>` Helm \
6283         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
6284         `build_chart_yaml` and into the chart `README.md` header via \
6285         `build_readme`; both consumers' `Option::unwrap_or_else` \
6286         `caixa.nome`-derived fallbacks only fire when the slot is \
6287         `None`, so an empty `Some(\"\")` silently lands as \
6288         `description: \"\"` / a blank `README.md` header in the \
6289         rendered artifacts and breaks at `helm lint` time \
6290         (`WARNING [chart.metadata.description]: description is \
6291         required` on `apiVersion: v2` charts) far from the source \
6292         caixa.lisp; omit the slot entirely to defer to the \
6293         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
6294         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
6295         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
6296         Servico.\"`)"
6297    )]
6298    DescricaoEmpty,
6299    #[error(
6300        ":descricao {descricao:?} is not a valid chart-description shape: \
6301         {reason} (the substrate consumes this string through the shared \
6302         `crate::render::is_chart_description_shape` predicate — the same \
6303         single-line-UTF-8 floor every realistic chart description carries: \
6304         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
6305         characters anywhere, Unicode prose bytes accepted. The canonical \
6306         authoring shapes are short single-line summaries like `\"Canonical \
6307         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
6308         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
6309         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
6310         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
6311         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
6312         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
6313         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
6314         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
6315         `validate_descricao` + `StandardLayout::verify` and landed in the \
6316         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
6317         field + `README.md` header paragraph as a YAML-illegal multi-line \
6318         scalar or a silently-trimmed whitespace round-trip — every \
6319         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
6320         render the description in a single-line column far from the source \
6321         caixa.lisp; the gate moves the diagnostic to the manifest layer \
6322         with the offending value named verbatim)"
6323    )]
6324    DescricaoInvalid { descricao: String, reason: String },
6325    #[error(
6326        ":licenca is the empty string (every published caixa names \
6327         its license via a non-empty `:licenca` SPDX expression — the \
6328         value flows verbatim into the rendered `lareira-<nome>` Helm \
6329         chart's `README.md` `## License` section via `caixa-helm`'s \
6330         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
6331         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
6332         only fires when the slot is `None`, so an empty `Some(\"\")` \
6333         silently lands as a bare trailing period in the rendered \
6334         chart `README.md` `License` section far from the source \
6335         caixa.lisp; omit the slot entirely to defer to the \
6336         renderer's `MIT` fallback, or carry a canonical SPDX \
6337         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
6338         `\"Apache-2.0 OR MIT\"`)"
6339    )]
6340    LicencaEmpty,
6341    #[error(
6342        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
6343         (the substrate consumes this string through the shared \
6344         `crate::render::is_spdx_expression_shape` predicate — the same \
6345         alphabet-floor parser every peer per-axis value-shape gate routes \
6346         its value through; the canonical authoring shapes are single \
6347         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
6348         compound expressions like `\"Apache-2.0 OR MIT\"`, \
6349         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
6350         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
6351         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
6352         like `\"LicenseRef-MyLicense\"` / \
6353         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
6354         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
6355         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
6356         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
6357         a smart-quote paste; underscore-instead-of-hyphen typo \
6358         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
6359         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
6360         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
6361         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
6362         `README.md` `## License` section + a future SPDX-aware \
6363         `Chart.yaml license:` emitter would refuse the value at \
6364         `helm lint` time far from the source caixa.lisp; the gate moves \
6365         the diagnostic to the manifest layer with the offending value \
6366         named verbatim)"
6367    )]
6368    LicencaInvalid { licenca: String, reason: String },
6369    #[error(
6370        ":edicao is the empty string (every published caixa names \
6371         its language edition via a non-empty `:edicao` value — the \
6372         edition determines the tatara-lisp macro surface + \
6373         compatibility flags the substrate applies when building \
6374         the caixa; the canonical `Caixa::template` scaffold every \
6375         `feira init` emits carries `:edicao \"2026\"` verbatim and \
6376         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
6377         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
6378         construction, so an empty `Some(\"\")` silently lands as a \
6379         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
6380         a future renderer-side consumer that folds it through \
6381         `Option::unwrap_or_else` will skip the fallback and pass the \
6382         empty edition through to the substrate's build-time edition \
6383         selector far from the source caixa.lisp; omit the slot \
6384         entirely to defer to the substrate's default edition, or \
6385         carry a canonical edition like `\"2026\"`)"
6386    )]
6387    EdicaoEmpty,
6388    #[error(
6389        ":edicao {edicao:?} is not a valid edition: {reason} (every \
6390         documented tatara-lisp edition is a 4-digit ASCII decimal \
6391         year — `\"2026\"` is the only edition currently minted; \
6392         future-introduced siblings will follow the same shape, peer \
6393         with Cargo's `[package] edition` grammar which every value \
6394         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
6395         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
6396         paste-from-doc footguns silently passed: a trailing space \
6397         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
6398         from a paste-from-multiline-doc, a fullwidth-keyboard \
6399         look-alike (`\"2026\"`), a free-form non-year value \
6400         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
6401         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
6402         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
6403         wrong-length numeric value (`\"26\"`, `\"202\"`, \
6404         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
6405         rendered caixa.lisp and broke at the substrate's \
6406         build-time edition selector far from the source caixa.lisp; \
6407         omit the slot entirely to defer to the substrate's default \
6408         edition, or carry a canonical 4-digit ASCII decimal year \
6409         like `\"2026\"`)"
6410    )]
6411    EdicaoInvalid { edicao: String, reason: String },
6412}
6413
6414#[cfg(test)]
6415mod tests {
6416    use super::*;
6417
6418    #[test]
6419    fn template_round_trips() {
6420        let src = Caixa::template("demo");
6421        let c = Caixa::from_lisp(&src).expect("template must parse");
6422        assert_eq!(c.nome, "demo");
6423        assert_eq!(c.versao, "0.1.0");
6424        assert_eq!(c.kind, CaixaKind::Biblioteca);
6425        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
6426        assert!(c.deps.is_empty());
6427        assert!(c.deps_dev.is_empty());
6428    }
6429
6430    #[test]
6431    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
6432        // Fail-before-pass-after pin on [`Caixa::nome`] +
6433        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
6434        // accessor projects the top-level manifest's per-`:nome` /
6435        // per-`:versao` [`String`] storage through the `pub const fn`
6436        // [`String::as_str`] (const-stable since Rust 1.87, well within
6437        // the workspace MSRV) — any future accidental downgrade to
6438        // non-`const` fails the corresponding `<name>_via_const_fn`
6439        // wrapper at caixa-core build time with E0015 (`cannot call
6440        // non-const method`), strictly stronger than a runtime
6441        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
6442        // scalar-accessor family pins on the sibling `const`-eval-
6443        // surface passes ([`crate::CaixaVersion::as_str`] at the
6444        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
6445        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
6446        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
6447        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
6448        // axis, [`crate::supervisor::ChildSpec::nome`] /
6449        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
6450        // M2 supervisor-tree axis,
6451        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
6452        // upgrade axis, [`crate::dep::Dep::nome`] /
6453        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
6454        // axis, and the per-`:contratos`
6455        // [`crate::aplicacao::WitContract::source`] /
6456        // [`crate::aplicacao::WitContract::destination`] /
6457        // [`crate::aplicacao::WitContract::world_ref`] trio the
6458        // sibling pin at 279823b already anchors).
6459        const fn nome_via_const_fn(c: &Caixa) -> &str {
6460            c.nome()
6461        }
6462        const fn versao_via_const_fn(c: &Caixa) -> &str {
6463            c.versao()
6464        }
6465        let src = Caixa::template("demo");
6466        let c = Caixa::from_lisp(&src).expect("template must parse");
6467        assert_eq!(nome_via_const_fn(&c), c.nome());
6468        assert_eq!(versao_via_const_fn(&c), c.versao());
6469        assert_eq!(c.nome(), "demo");
6470        assert_eq!(c.versao(), "0.1.0");
6471    }
6472
6473    #[test]
6474    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
6475        // Fail-before-pass-after pin on the five per-`Caixa`
6476        // `Option<String> → Option<&str>` scalar accessors
6477        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
6478        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
6479        // manifest's optional universal-axis surface, plus
6480        // [`Caixa::restart_window`] on the M2 supervisor-tree
6481        // per-`SupervisorSpec` peer raw-window-string projection axis).
6482        // Each accessor destructures the typed slot's `Option<String>`
6483        // storage through the `match &self.<field> { Some(s) =>
6484        // Some(s.as_str()), None => None }` shape — routing through
6485        // [`String::as_str`] (const-stable since Rust 1.87, well within
6486        // the workspace MSRV) rather than the non-const
6487        // [`Option::as_deref`] the pre-lift bodies carried — and any
6488        // future accidental downgrade to non-`const` fails the
6489        // corresponding `<name>_via_const_fn` wrapper at caixa-core
6490        // build time with E0015 (`cannot call non-const method`),
6491        // strictly stronger than a runtime `assert!` and strictly
6492        // stronger than a module-scope `const _: () = assert!(…)` pin
6493        // (which cannot be formed on a `&Caixa` fixture because the
6494        // type's `String` / `Option<String>` carriers rule out
6495        // `const`-context value construction; the `const fn` wrapper
6496        // is the load-bearing shape that side-steps the destructor-in-
6497        // const restriction on the value axis while still pinning the
6498        // `const`-fn posture on the callee — mirror of the sibling
6499        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
6500        // pin's discipline verbatim on the peer non-`Option`
6501        // `String → &str` axis at the same struct).
6502        //
6503        // Peer of the sibling per-M2/M3-slot `Option<String> →
6504        // Option<&str>` accessor family pin
6505        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
6506        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
6507        // [`WitContract::subject`] / [`WitContract::slot`] on the
6508        // per-`:contratos` payload-carrier trio,
6509        // [`Placement::shard_key`] / [`Placement::affinity`] on the
6510        // per-`:placement` optional-scalar pair).
6511        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
6512            c.licenca()
6513        }
6514        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
6515            c.repositorio()
6516        }
6517        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
6518            c.descricao()
6519        }
6520        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
6521            c.edicao()
6522        }
6523        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
6524            c.restart_window()
6525        }
6526        // Sweep both the `Some`-carrying arm (author-declared slot,
6527        // the byte-string projection payload) and the `None`-carrying
6528        // arm (author-omitted slot, the default-path projection) on
6529        // every accessor so the `const fn` wrapper family pins each
6530        // axis's canonical two-arm partition through the same const
6531        // dispatch as the runtime path.
6532        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6533        c1.licenca = Some("MIT".to_string());
6534        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
6535        c1.descricao = Some("demo caixa".to_string());
6536        c1.edicao = Some("2024".to_string());
6537        c1.restart_window = Some("60s".to_string());
6538        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
6539        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
6540        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
6541        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
6542        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
6543        assert_eq!(c1.licenca(), Some("MIT"));
6544        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
6545        assert_eq!(c1.descricao(), Some("demo caixa"));
6546        assert_eq!(c1.edicao(), Some("2024"));
6547        assert_eq!(c1.restart_window(), Some("60s"));
6548        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6549        c2.licenca = None;
6550        c2.repositorio = None;
6551        c2.descricao = None;
6552        c2.edicao = None;
6553        c2.restart_window = None;
6554        assert_eq!(licenca_via_const_fn(&c2), None);
6555        assert_eq!(repositorio_via_const_fn(&c2), None);
6556        assert_eq!(descricao_via_const_fn(&c2), None);
6557        assert_eq!(edicao_via_const_fn(&c2), None);
6558        assert_eq!(restart_window_via_const_fn(&c2), None);
6559    }
6560
6561    #[test]
6562    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
6563        // Fail-before-pass-after pin on the two outer-[`Caixa`]
6564        // `Copy`-return accessors — [`Caixa::kind`] on the required
6565        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
6566        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
6567        // axis. Both accessors project a `Copy`-carrier field
6568        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
6569        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
6570        // `Option<RestartStrategy>: Copy`) by value through a bare
6571        // `self.<field>` field-access — no dispatch, no destructor, no
6572        // heap. Any future accidental downgrade to non-`const` fails
6573        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
6574        // build time with E0015 (`cannot call non-const method`),
6575        // strictly stronger than a runtime `assert!` and strictly
6576        // stronger than a module-scope `const _: () = assert!(…)` pin
6577        // (which cannot be formed on a `&Caixa` fixture because the
6578        // type's `String` / `Vec` / `Option<Composite>` carriers rule
6579        // out `const`-context value construction; the `const fn`
6580        // wrapper is the load-bearing shape that side-steps the
6581        // destructor-in-const restriction on the value axis while still
6582        // pinning the `const`-fn posture on the callee — mirror of the
6583        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
6584        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
6585        // pins' discipline verbatim on the peer outer-`Caixa`
6586        // `String → &str` + `Option<String> → Option<&str>` axes at the
6587        // same struct).
6588        //
6589        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
6590        // family on the inner-altitude nested-spec typed-slot
6591        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
6592        // + [`crate::supervisor::ChildSpec::restart`] on the M2
6593        // supervisor-tree axis (pinned at 152c868), and
6594        // [`crate::aplicacao::Placement::estrategia`] +
6595        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
6596        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
6597        // unlifted altitude for the `Copy`-return-accessor family.
6598        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
6599            c.kind()
6600        }
6601        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
6602            c.estrategia()
6603        }
6604        // Sweep every arm of both discriminant partitions the accessors
6605        // fan on — every [`CaixaKind`] variant the six-arm required
6606        // discriminant carries (Biblioteca / Binario / Servico /
6607        // Supervisor / Aplicacao / Acao) and both arms of the
6608        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
6609        // (`Some(<strategy>)` on an author-declared supervisor and
6610        // `None` on the author-omitted default arm every non-Supervisor
6611        // caixa carries by `#[serde(default)]`) — so the `const fn`
6612        // wrapper family pins the closed-set partition through the
6613        // same const dispatch as the runtime path.
6614        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6615        c1.kind = CaixaKind::Servico;
6616        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
6617        assert_eq!(kind_via_const_fn(&c1), c1.kind());
6618        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
6619        assert_eq!(c1.kind(), CaixaKind::Servico);
6620        assert_eq!(
6621            c1.estrategia(),
6622            Some(crate::supervisor::RestartStrategy::OneForAll)
6623        );
6624        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6625        c2.kind = CaixaKind::Aplicacao;
6626        c2.estrategia = None;
6627        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
6628        assert_eq!(estrategia_via_const_fn(&c2), None);
6629        // Anchor the remaining discriminant arms so any future
6630        // reordering of [`CaixaKind`]'s six-variant enum surfaces
6631        // through the wrapper dispatch, not just through the direct
6632        // method call.
6633        for kind in [
6634            CaixaKind::Biblioteca,
6635            CaixaKind::Binario,
6636            CaixaKind::Servico,
6637            CaixaKind::Supervisor,
6638            CaixaKind::Aplicacao,
6639            CaixaKind::Acao,
6640        ] {
6641            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6642            c.kind = kind;
6643            assert_eq!(kind_via_const_fn(&c), kind);
6644        }
6645    }
6646
6647    #[test]
6648    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
6649        // Fail-before-pass-after pin on the five outer-[`Caixa`]
6650        // `Vec<String> → &[String]` slice-return accessors on the
6651        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
6652        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
6653        // Each body is a bare `self.<field>.as_slice()` dispatch through
6654        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
6655        // the workspace MSRV). Any future accidental downgrade to
6656        // non-`const` fails the corresponding `<name>_via_const_fn`
6657        // wrapper at caixa-core build time with E0015 (`cannot call
6658        // non-const method`) — mirror of the sibling
6659        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
6660        // discipline on the peer outer-`Caixa` `Copy`-return accessor
6661        // axis, and peer of the sibling composite-carrier slice-return
6662        // pin below on the peer outer-`Caixa` composite-slice axis.
6663        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
6664            c.autores()
6665        }
6666        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
6667            c.etiquetas()
6668        }
6669        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
6670            c.bibliotecas()
6671        }
6672        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
6673            c.exe()
6674        }
6675        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
6676            c.servicos()
6677        }
6678        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
6679        // `servicos` — the template's `Vec::new()` default) and the
6680        // populated arm (mutated below) on every accessor so the
6681        // `const fn` wrapper family pins each axis's two-arm partition
6682        // through the same const dispatch as the runtime path.
6683        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
6684        // so that arm's "empty" fixture is the populated arm the
6685        // mutation sweep covers.
6686        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6687        assert!(autores_via_const_fn(&c_empty).is_empty());
6688        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
6689        assert!(exe_via_const_fn(&c_empty).is_empty());
6690        assert!(servicos_via_const_fn(&c_empty).is_empty());
6691        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6692        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
6693        c_full.etiquetas = vec!["compounding".to_string()];
6694        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
6695        c_full.exe = vec!["exe/cli.lisp".to_string()];
6696        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
6697        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
6698        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
6699        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
6700        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
6701        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
6702        assert_eq!(
6703            bibliotecas_via_const_fn(&c_full),
6704            &["lib/one.lisp", "lib/two.lisp"]
6705        );
6706        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
6707        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
6708        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
6709        assert_eq!(
6710            servicos_via_const_fn(&c_full),
6711            &["servicos/one.computeunit.yaml"]
6712        );
6713    }
6714
6715    #[test]
6716    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
6717        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
6718        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
6719        // / [`Caixa::deps_dev`] on the dep-graph axis,
6720        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
6721        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
6722        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
6723        // bare `self.<field>.as_slice()` dispatch through
6724        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
6725        // the workspace MSRV) — peer of the sibling `String`-payload
6726        // slice-return pin above on the peer outer-`Caixa` universal-
6727        // axis surface, and peer of the sibling inner-composite-
6728        // altitude reference-return pin family
6729        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
6730        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
6731        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
6732        // (all pinned at 0b23e0f).
6733        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
6734            c.deps()
6735        }
6736        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
6737            c.deps_dev()
6738        }
6739        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
6740            c.upgrade_from()
6741        }
6742        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
6743            c.children()
6744        }
6745        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
6746            c.membros()
6747        }
6748        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
6749            c.contratos()
6750        }
6751        // Empty-arm sweep on all six composite-carrier axes — every
6752        // `Caixa::template` starts with `Vec::new()` on each.
6753        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6754        assert!(deps_via_const_fn(&c_empty).is_empty());
6755        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
6756        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
6757        assert!(children_via_const_fn(&c_empty).is_empty());
6758        assert!(membros_via_const_fn(&c_empty).is_empty());
6759        assert!(contratos_via_const_fn(&c_empty).is_empty());
6760        // Populate `:membros` / `:contratos` directly via struct literals
6761        // — the parser-side validation path fans on `:kind`-gated cross-
6762        // slot invariants irrelevant to the accessor dispatch under test.
6763        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6764        c_full.membros = vec![
6765            crate::aplicacao::Membro {
6766                caixa: "demo-a".to_string(),
6767                versao: "^0.1.0".to_string(),
6768            },
6769            crate::aplicacao::Membro {
6770                caixa: "demo-b".to_string(),
6771                versao: "^0.2.0".to_string(),
6772            },
6773        ];
6774        c_full.contratos = vec![crate::aplicacao::WitContract {
6775            de: "demo-a".to_string(),
6776            para: "demo-b".to_string(),
6777            wit: "wasi:http/proxy".to_string(),
6778            endpoint: Some("/edge".to_string()),
6779            subject: None,
6780            slot: None,
6781        }];
6782        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
6783        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
6784        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
6785        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
6786        // Alias-borrow check on the four remaining composite-carrier
6787        // slice-return arms — the wrapper's return borrow must alias the
6788        // caller's borrow so any future accessor re-routing that skips
6789        // the storage field surfaces through the assertion.
6790        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
6791        assert!(std::ptr::eq(
6792            deps_dev_via_const_fn(&c_full),
6793            c_full.deps_dev()
6794        ));
6795        assert!(std::ptr::eq(
6796            upgrade_from_via_const_fn(&c_full),
6797            c_full.upgrade_from()
6798        ));
6799        assert!(std::ptr::eq(
6800            children_via_const_fn(&c_full),
6801            c_full.children()
6802        ));
6803    }
6804
6805    #[test]
6806    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
6807        // Fail-before-pass-after pin on the six outer-[`Caixa`]
6808        // `Option<Composite> → Option<&Composite>` reference-return
6809        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
6810        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
6811        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
6812        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
6813        // Each body is a bare `self.<field>.as_ref()` dispatch through
6814        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
6815        // the workspace MSRV of 1.89). Any future accidental downgrade
6816        // to non-`const` fails the corresponding `<name>_via_const_fn`
6817        // wrapper at caixa-core build time with E0015 (`cannot call
6818        // non-const method`), strictly stronger than a runtime `assert!`
6819        // and strictly stronger than a module-scope `const _: () =
6820        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
6821        // because the type's `String` / `Vec` / `Option<Composite>`
6822        // carriers rule out `const`-context value construction; the
6823        // `const fn` wrapper is the load-bearing shape that side-steps
6824        // the destructor-in-const restriction on the value axis while
6825        // still pinning the `const`-fn posture on the callee — mirror
6826        // of the sibling
6827        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
6828        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
6829        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
6830        // pins' discipline verbatim on the peer outer-`Caixa` axes at
6831        // the same struct).
6832        //
6833        // Closes the outer-`Caixa` `Option<&Composite>` composite-
6834        // reference-return sub-family — the last unlifted altitude on
6835        // the outer-`Caixa` accessor-family const-eval surface after
6836        // the sibling `Copy`-return / universal-axis-`&str` /
6837        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
6838        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
6839        // 231a968 (the last of these pins the `Vec<T> → &[T]`
6840        // composite-slice arm the six accessors here close as their
6841        // `Option<Composite> → Option<&Composite>` peer). Peer of the
6842        // sibling inner-altitude nested-spec composite-reference-return
6843        // pin family — [`crate::AplicacaoSpec::politicas`] /
6844        // [`crate::AplicacaoSpec::placement`] /
6845        // [`crate::AplicacaoSpec::entrada`] on the inner
6846        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
6847        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
6848        // the same shape so both altitudes of the reference-return
6849        // discipline (per-`Caixa` outer-slot presence + per-
6850        // `AplicacaoSpec` inner-slot presence) route through one typed
6851        // const dispatch on the substrate primitive.
6852        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
6853            c.limits()
6854        }
6855        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
6856            c.behavior()
6857        }
6858        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
6859            c.politicas()
6860        }
6861        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
6862            c.placement()
6863        }
6864        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
6865            c.entrada()
6866        }
6867        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
6868            c.ci()
6869        }
6870        // Both-arm sweep on every accessor: the `None` author-omitted
6871        // arm (template default — no M2/M3/CI slot declared) and the
6872        // `Some(<composite>)` authored arm (mutated below via struct-
6873        // literal seeds, side-stepping the parser-side `:kind`-gated
6874        // cross-slot invariants irrelevant to the accessor dispatch
6875        // under test). Both arms route through the `const fn` wrapper
6876        // family so the two-arm `Option` partition is pinned through
6877        // the same const dispatch as the runtime path.
6878        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6879        assert!(limits_via_const_fn(&c_empty).is_none());
6880        assert!(behavior_via_const_fn(&c_empty).is_none());
6881        assert!(politicas_via_const_fn(&c_empty).is_none());
6882        assert!(placement_via_const_fn(&c_empty).is_none());
6883        assert!(entrada_via_const_fn(&c_empty).is_none());
6884        assert!(ci_via_const_fn(&c_empty).is_none());
6885        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
6886        c_full.limits = Some(LimitsSpec::default());
6887        c_full.behavior = Some(crate::BehaviorSpec::default());
6888        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
6889        c_full.placement = Some(crate::aplicacao::Placement::default());
6890        c_full.entrada = Some(crate::aplicacao::Entrada {
6891            host: "demo.quero.cloud".to_string(),
6892            para: "demo".to_string(),
6893            paths: Vec::new(),
6894            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
6895        });
6896        c_full.ci = Some(canteiro_types::CiRun {
6897            workspace: "pleme-io".into(),
6898            repo: "caixa".into(),
6899            nodes: vec![],
6900        });
6901        assert!(limits_via_const_fn(&c_full).is_some());
6902        assert!(behavior_via_const_fn(&c_full).is_some());
6903        assert!(politicas_via_const_fn(&c_full).is_some());
6904        assert!(placement_via_const_fn(&c_full).is_some());
6905        assert!(entrada_via_const_fn(&c_full).is_some());
6906        assert!(ci_via_const_fn(&c_full).is_some());
6907        // Alias-borrow check on every arm: the wrapper's inner-`Option`
6908        // reference must alias the caller's borrow so any future accessor
6909        // re-routing that skips the storage field surfaces through the
6910        // assertion.
6911        assert!(std::ptr::eq(
6912            limits_via_const_fn(&c_full).unwrap(),
6913            c_full.limits().unwrap()
6914        ));
6915        assert!(std::ptr::eq(
6916            behavior_via_const_fn(&c_full).unwrap(),
6917            c_full.behavior().unwrap()
6918        ));
6919        assert!(std::ptr::eq(
6920            politicas_via_const_fn(&c_full).unwrap(),
6921            c_full.politicas().unwrap()
6922        ));
6923        assert!(std::ptr::eq(
6924            placement_via_const_fn(&c_full).unwrap(),
6925            c_full.placement().unwrap()
6926        ));
6927        assert!(std::ptr::eq(
6928            entrada_via_const_fn(&c_full).unwrap(),
6929            c_full.entrada().unwrap()
6930        ));
6931        assert!(std::ptr::eq(
6932            ci_via_const_fn(&c_full).unwrap(),
6933            c_full.ci().unwrap()
6934        ));
6935    }
6936
6937    #[test]
6938    fn register_populates_registry() {
6939        Caixa::register().expect("first register call in this test process must succeed");
6940        let kws = tatara_lisp::domain::registered_keywords();
6941        assert!(kws.contains(&"defcaixa"));
6942    }
6943
6944    #[test]
6945    fn to_lisp_round_trips() {
6946        let src = Caixa::template("demo");
6947        let c1 = Caixa::from_lisp(&src).unwrap();
6948        let emitted = c1.to_lisp();
6949        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
6950        assert_eq!(c1, c2);
6951    }
6952
6953    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
6954    //
6955    // The compounding pin: the variant stores only the typed
6956    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
6957    // (canonical keyword, description, consumer) routes through the enum's
6958    // own accessors at Display time. Prior to that closure the variant
6959    // carried each accessor's return value as a stored `&'static str`
6960    // snapshot alongside `dialeto`; a caller could construct the variant
6961    // with a snapshot that drifted from what `dialeto`'s accessors would
6962    // return, and every downstream user-facing projection would silently
6963    // disagree with the classification. Storing only the axis makes the
6964    // drift structurally impossible.
6965
6966    #[test]
6967    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
6968        // Single-field construction is the whole compounding shape — a
6969        // future re-introduction of a snapshot field (a `palavra_canonica:
6970        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
6971        // would re-open the drift surface and this construction would fail
6972        // to compile with "missing field" until every snapshot was seeded
6973        // at the call site again. The compile-time guarantee is the
6974        // invariant; the assertion below only witnesses that the
6975        // construction is well-formed after the closure.
6976        let err = LeituraError::DialetoEstrangeiro {
6977            dialeto: crate::dialeto::CaixaDialeto::Molde,
6978        };
6979        assert!(matches!(
6980            err,
6981            LeituraError::DialetoEstrangeiro {
6982                dialeto: crate::dialeto::CaixaDialeto::Molde,
6983            }
6984        ));
6985    }
6986
6987    #[test]
6988    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
6989        // For every foreign-dialect classification the variant surfaces —
6990        // [`crate::dialeto::CaixaDialeto::Molde`] and
6991        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
6992        // variants [`Caixa::from_lisp`] raises this error for — the
6993        // rendered [`std::fmt::Display`] byte-string must interpolate each
6994        // typed accessor's return verbatim. A future re-introduction of a
6995        // stored `&'static str` snapshot alongside `dialeto` that Display
6996        // read instead of the accessor would fail this pin as soon as the
6997        // two disagreed; a future accessor rebrand (a per-dialect
6998        // consumer rename, a canonical-keyword shift once the substrate
6999        // migration named in [`crate::dialeto`] completes) reaches every
7000        // consumer through one typed dispatch and this pin verifies the
7001        // display path is one of them.
7002        for d in [
7003            crate::dialeto::CaixaDialeto::Molde,
7004            crate::dialeto::CaixaDialeto::MoldePosicional,
7005        ] {
7006            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
7007            assert!(
7008                rendered.contains(d.palavra_canonica()),
7009                "Display must interpolate `dialeto.palavra_canonica()` \
7010                 verbatim — a stored snapshot would silently drift from \
7011                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
7012            );
7013            assert!(
7014                rendered.contains(d.descricao()),
7015                "Display must interpolate `dialeto.descricao()` verbatim. \
7016                 dialect: {d}, rendered: {rendered:?}"
7017            );
7018            assert!(
7019                rendered.contains(d.consumidor()),
7020                "Display must interpolate `dialeto.consumidor()` verbatim. \
7021                 dialect: {d}, rendered: {rendered:?}"
7022            );
7023        }
7024    }
7025
7026    #[test]
7027    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
7028        // The end-to-end pin the compounding closure defends: a
7029        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
7030        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
7031        // rendered Display byte-string names the Molde accessors'
7032        // returns verbatim. Any future path that constructed the variant
7033        // with a mismatched snapshot (a stored `palavra_canonica:
7034        // "defcaixa"` on a `Molde` classification) would land Display
7035        // pointing at `defcaixa` while the typed axis said `Molde` — the
7036        // exact drift the closure removes.
7037        let src = r#"
7038          (defcaixa
7039            :name "x"
7040            :kind :Biblioteca
7041            :ecosystem :rust-single-crate
7042            :package {:name "x" :version "0.1.0"})
7043        "#;
7044        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
7045        match err {
7046            LeituraError::DialetoEstrangeiro { dialeto } => {
7047                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
7048                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
7049                assert!(rendered.contains(dialeto.palavra_canonica()));
7050                assert!(rendered.contains(dialeto.consumidor()));
7051                assert!(rendered.contains(dialeto.descricao()));
7052            }
7053            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
7054        }
7055    }
7056
7057    #[test]
7058    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
7059        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
7060        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
7061        // positional-arity `defmolde` form written under a `(defcaixa …)`
7062        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
7063        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
7064        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
7065        // so no test exercised the positional-arity path through
7066        // `Caixa::from_lisp` specifically; the sibling
7067        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
7068        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
7069        // two arms route through the lifted
7070        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
7071        // typed predicate — the same predicate the pre-lift `foreign =>`
7072        // wildcard resolved to today — and this pin makes the
7073        // positional-arity arm's byte-shape at the gate explicit rather
7074        // than implied by wildcard-absorption. A future regression that
7075        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
7076        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
7077        // from the two-arity closure) would fail this pin at caixa-core
7078        // test time rather than surfacing far from the change as a
7079        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
7080        // …)` silently parsing past the derive.
7081        let src = r#"
7082          (defcaixa todoku-go
7083            :kind :Biblioteca
7084            :ecosystem :go
7085            :package {:name "todoku-go" :version "0.3.0"})
7086        "#;
7087        let err =
7088            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
7089        match err {
7090            LeituraError::DialetoEstrangeiro { dialeto } => {
7091                assert_eq!(
7092                    dialeto,
7093                    crate::dialeto::CaixaDialeto::MoldePosicional,
7094                    "DialetoEstrangeiro must carry the MoldePosicional \
7095                     variant verbatim — the positional-arity `defmolde` \
7096                     form under a `(defcaixa …)` head is the \
7097                     `MoldePosicional` arm's canonical byte-shape"
7098                );
7099                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
7100                assert!(
7101                    rendered.contains(dialeto.palavra_canonica()),
7102                    "Display must interpolate `dialeto.palavra_canonica()` \
7103                     verbatim on the MoldePosicional arm; rendered: \
7104                     {rendered:?}"
7105                );
7106                assert!(
7107                    rendered.contains(dialeto.consumidor()),
7108                    "Display must interpolate `dialeto.consumidor()` \
7109                     verbatim on the MoldePosicional arm; rendered: \
7110                     {rendered:?}"
7111                );
7112                assert!(
7113                    rendered.contains(dialeto.descricao()),
7114                    "Display must interpolate `dialeto.descricao()` \
7115                     verbatim on the MoldePosicional arm; rendered: \
7116                     {rendered:?}"
7117                );
7118            }
7119            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
7120        }
7121    }
7122
7123    #[test]
7124    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
7125        // Load-bearing byte-parity pin: for every arm in
7126        // [`crate::dialeto::CaixaDialeto::ALL`], the
7127        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
7128        // partition must agree with the lifted
7129        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
7130        // typed predicate — i.e. from_lisp raises
7131        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
7132        // `d.is_molde_family()` returns `true`, and does NOT raise
7133        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
7134        // predicate returns `false` (the arm's source falls through to
7135        // the derive — parses cleanly on
7136        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
7137        // [`LeituraError::Leitura`] on
7138        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
7139        //
7140        // Pre-lift the gate hand-rolled a three-arm match
7141        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
7142        // whose `foreign =>` wildcard expressed no compile-time link
7143        // back to the substrate primitive's arm-family; a future fifth
7144        // dialect the [`crate::dialeto`] module doc's "third dialect"
7145        // hazard actualises would fall silently onto the wildcard
7146        // regardless of whether it belonged to the `defmolde` family or
7147        // to a distinct `defcaixa`-family. Post-lift the partition
7148        // resolves through
7149        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
7150        // typed dispatch, and this pin refuses any future regression
7151        // that silently split the from_lisp partition from the typed
7152        // predicate — the two paths now migrate as one on any future
7153        // arm addition.
7154        //
7155        // Sibling in shape to the peer
7156        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
7157        // (e9d2315) that pins the same byte-parity between
7158        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
7159        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
7160        // `== "defmolde"` classifier — extends the discipline from the
7161        // two paths within the [`crate::dialeto`] primitive onto the
7162        // third external consumer of the `defmolde`-family partition
7163        // (the [`Caixa::from_lisp`] gate that raises
7164        // [`LeituraError::DialetoEstrangeiro`]).
7165        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
7166            (
7167                crate::dialeto::CaixaDialeto::Pacote,
7168                r#"
7169                  (defcaixa
7170                    :nome   "checkout"
7171                    :versao "0.1.0"
7172                    :kind   Biblioteca
7173                    :edicao "2026"
7174                    :descricao "canonical Pacote source"
7175                    :autores ()
7176                    :etiquetas ()
7177                    :deps ()
7178                    :deps-dev ()
7179                    :bibliotecas ("lib/checkout.lisp"))
7180                "#,
7181            ),
7182            (
7183                crate::dialeto::CaixaDialeto::Molde,
7184                r#"
7185                  (defcaixa
7186                    :name "base64"
7187                    :kind :Biblioteca
7188                    :ecosystem :rust-single-crate
7189                    :package {:name "base64" :version "0.22.1"}
7190                    :workflows [:auto-release])
7191                "#,
7192            ),
7193            (
7194                crate::dialeto::CaixaDialeto::MoldePosicional,
7195                r#"
7196                  (defcaixa todoku-go
7197                    :kind :Biblioteca
7198                    :ecosystem :go
7199                    :package {:name "todoku-go" :version "0.3.0"})
7200                "#,
7201            ),
7202            (
7203                crate::dialeto::CaixaDialeto::Desconhecido,
7204                r#"(defcaixa :licenca "MIT")"#,
7205            ),
7206        ];
7207
7208        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
7209        // must appear in the fixture table so the pin's arm-set stays
7210        // synchronised with the enum's arm-set. Fails at test time if a
7211        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
7212        // (with a corresponding `is_molde_family` return) forgot to
7213        // extend this fixture table with a canonical source for the new
7214        // arm — the pin cannot cover an arm it has no source for.
7215        for &expected in crate::dialeto::CaixaDialeto::ALL {
7216            assert!(
7217                fixtures.iter().any(|(d, _)| *d == expected),
7218                "fixture table must carry a canonical source for every \
7219                 CaixaDialeto arm; missing: {expected:?}"
7220            );
7221        }
7222
7223        for &(expected_dialect, src) in fixtures {
7224            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
7225                panic!(
7226                    "fixture source for {expected_dialect:?} must classify \
7227                     cleanly, got err: {err:?}"
7228                )
7229            });
7230            assert_eq!(
7231                classified, expected_dialect,
7232                "fixture source for {expected_dialect:?} must classify as \
7233                 {expected_dialect:?} (drift here defeats the byte-parity \
7234                 pin below — a source labelled for one arm but classifying \
7235                 as another would silently satisfy or violate the pin for \
7236                 the wrong reason)"
7237            );
7238
7239            let outcome = Caixa::from_lisp(src);
7240            match (expected_dialect.is_molde_family(), &outcome) {
7241                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
7242                    assert_eq!(
7243                        *dialeto, expected_dialect,
7244                        "DialetoEstrangeiro must carry the same typed arm \
7245                         the classifier returned — a drift here would let \
7246                         from_lisp raise the error while pointing at the \
7247                         wrong dialect (e.g. rejecting a \
7248                         MoldePosicional source as Molde). arm: \
7249                         {expected_dialect:?}"
7250                    );
7251                }
7252                (true, other) => panic!(
7253                    "arm {expected_dialect:?} has is_molde_family() = true \
7254                     so from_lisp must raise DialetoEstrangeiro carrying \
7255                     {expected_dialect:?}; got: {other:?}"
7256                ),
7257                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
7258                    "arm {expected_dialect:?} has is_molde_family() = false \
7259                     so from_lisp must NOT raise DialetoEstrangeiro; got \
7260                     one carrying: {dialeto:?}. This means the typed \
7261                     predicate and the from_lisp partition disagree on \
7262                     this arm — exactly the drift this pin refuses."
7263                ),
7264                (false, _) => {
7265                    // A non-molde arm's source falls through to the
7266                    // derive: Pacote sources parse to Ok(_); Desconhecido
7267                    // sources surface as LeituraError::Leitura from the
7268                    // derive's own unknown-keyword rejection. Either
7269                    // shape is acceptable here — the pin's promise is
7270                    // narrower: "no DialetoEstrangeiro on
7271                    // is_molde_family() == false".
7272                }
7273            }
7274        }
7275    }
7276
7277    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
7278
7279    #[test]
7280    fn limits_round_trip_via_json() {
7281        use crate::LimitsSpec;
7282        use std::time::Duration;
7283        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7284        c.limits = Some(LimitsSpec {
7285            memory: Some(64 * 1024 * 1024),
7286            fuel: Some(1_000_000),
7287            wall_clock: Some(Duration::from_secs(30)),
7288            cpu: Some(500),
7289        });
7290        let json = serde_json::to_string(&c).unwrap();
7291        assert!(json.contains("\"limits\""));
7292        assert!(json.contains("\"64MiB\""));
7293        assert!(json.contains("\"30s\""));
7294        assert!(json.contains("\"500m\""));
7295        let back: Caixa = serde_json::from_str(&json).unwrap();
7296        assert_eq!(c.limits, back.limits);
7297    }
7298
7299    #[test]
7300    fn behavior_round_trip_via_json() {
7301        use crate::BehaviorSpec;
7302        use std::path::PathBuf;
7303        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7304        c.behavior = Some(BehaviorSpec {
7305            on_init: Some(PathBuf::from("lib/init.lisp")),
7306            on_call: Some(PathBuf::from("lib/handlers.lisp")),
7307            ..Default::default()
7308        });
7309        let json = serde_json::to_string(&c).unwrap();
7310        let back: Caixa = serde_json::from_str(&json).unwrap();
7311        assert_eq!(c.behavior, back.behavior);
7312    }
7313
7314    #[test]
7315    fn upgrade_from_round_trip_via_json() {
7316        use crate::{UpgradeFromEntry, UpgradeInstruction};
7317        use std::path::PathBuf;
7318        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7319        c.upgrade_from = vec![UpgradeFromEntry {
7320            from: "0.1.0".into(),
7321            instructions: vec![
7322                UpgradeInstruction::LoadModule {
7323                    module: "demo".into(),
7324                },
7325                UpgradeInstruction::StateChange {
7326                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
7327                },
7328                UpgradeInstruction::SoftPurge {
7329                    module: "demo-old".into(),
7330                },
7331            ],
7332        }];
7333        let json = serde_json::to_string(&c).unwrap();
7334        let back: Caixa = serde_json::from_str(&json).unwrap();
7335        assert_eq!(c.upgrade_from, back.upgrade_from);
7336    }
7337
7338    #[test]
7339    fn supervisor_view_returns_typed_shape() {
7340        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
7341        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
7342        c.kind = CaixaKind::Supervisor;
7343        c.bibliotecas.clear();
7344        c.estrategia = Some(RestartStrategy::OneForOne);
7345        c.max_restarts = Some(5);
7346        c.restart_window = Some("60s".into());
7347        c.children = vec![ChildSpec {
7348            caixa: "worker".into(),
7349            versao: "^0.1".into(),
7350            restart: RestartPolicy::Permanent,
7351        }];
7352        let view = c.supervisor_view().expect("Supervisor kind has a view");
7353        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
7354        assert_eq!(view.max_restarts, 5);
7355        assert_eq!(
7356            view.restart_window,
7357            Some(std::time::Duration::from_secs(60))
7358        );
7359        assert_eq!(view.children.len(), 1);
7360        view.validate().unwrap();
7361    }
7362
7363    #[test]
7364    fn supervisor_view_none_for_non_supervisor_kinds() {
7365        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7366        assert!(c.supervisor_view().is_none());
7367    }
7368
7369    #[test]
7370    fn declared_mesh_slots_empty_for_bare_caixa() {
7371        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7372        assert!(c.declared_mesh_slots().is_empty());
7373    }
7374
7375    #[test]
7376    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
7377        use crate::{Entrada, Membro};
7378        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7379        // Set a non-adjacent pair (:membros + :entrada) to pin that the
7380        // canonical declaration order is preserved regardless of which
7381        // subset is populated.
7382        c.membros = vec![Membro {
7383            caixa: "a".into(),
7384            versao: "^0.1".into(),
7385        }];
7386        c.entrada = Some(Entrada {
7387            host: "x.example.com".into(),
7388            para: "a".into(),
7389            paths: vec![],
7390            port: 8080,
7391        });
7392        assert_eq!(
7393            c.declared_mesh_slots(),
7394            vec![
7395                crate::render::M3_AUTHOR_KEY_MEMBROS,
7396                crate::render::M3_AUTHOR_KEY_ENTRADA,
7397            ]
7398        );
7399    }
7400
7401    #[test]
7402    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7403        // Scalar-value pin: the five author-facing kebab-case labels the
7404        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
7405        // mesh slot axis, one arm per typed slot. Mirrors the peer
7406        // scalar-value pin the sibling
7407        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
7408        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
7409        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
7410        // carry (f49c8b0), so both altitudes of the typed-slot algebra
7411        // (per-Servico M2 + per-Aplicacao M3) share the same
7412        // "one canonical byte-string per arm" discipline. A future
7413        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
7414        // `:politicas` → `:policies`, `:placement` → `:distribution`,
7415        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
7416        // and every consumer that reaches for the label picks it up at
7417        // build time rather than at runtime as a downstream mismatch.
7418        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
7419        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
7420        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
7421        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
7422        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
7423    }
7424
7425    #[test]
7426    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
7427        // Production-through-const pin: the five per-arm labels the
7428        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
7429        // `Vec` route through the lifted
7430        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
7431        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
7432        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
7433        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
7434        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
7435        // declaration order. A future re-order or drift at the tagger
7436        // (a rename that reaches the tagger but not the const, or vice
7437        // versa) surfaces here at build time rather than at runtime as
7438        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
7439        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7440        // commit. Mirror of the peer
7441        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
7442        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
7443        // axis.
7444        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
7445        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7446        c.membros = vec![Membro {
7447            caixa: "a".into(),
7448            versao: "^0.1".into(),
7449        }];
7450        c.contratos = vec![WitContract {
7451            de: "a".into(),
7452            para: "a".into(),
7453            wit: "wasi:http/proxy".into(),
7454            endpoint: Some("/x".into()),
7455            subject: None,
7456            slot: None,
7457        }];
7458        c.politicas = Some(MeshPolicy::default());
7459        c.placement = Some(Placement {
7460            estrategia: PlacementStrategy::Replicated,
7461            clusters: vec!["rio".into()],
7462            affinity: None,
7463            shard_key: None,
7464        });
7465        c.entrada = Some(Entrada {
7466            host: "x.example.com".into(),
7467            para: "a".into(),
7468            paths: vec![],
7469            port: 8080,
7470        });
7471        assert_eq!(
7472            c.declared_mesh_slots(),
7473            vec![
7474                crate::render::M3_AUTHOR_KEY_MEMBROS,
7475                crate::render::M3_AUTHOR_KEY_CONTRATOS,
7476                crate::render::M3_AUTHOR_KEY_POLITICAS,
7477                crate::render::M3_AUTHOR_KEY_PLACEMENT,
7478                crate::render::M3_AUTHOR_KEY_ENTRADA,
7479            ]
7480        );
7481    }
7482
7483    #[test]
7484    fn declared_supervisor_slots_empty_for_bare_caixa() {
7485        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7486        assert!(c.declared_supervisor_slots().is_empty());
7487    }
7488
7489    #[test]
7490    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
7491        use crate::RestartStrategy;
7492        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7493        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
7494        // that the canonical declaration order is preserved regardless
7495        // of which subset is populated.
7496        c.estrategia = Some(RestartStrategy::OneForOne);
7497        c.restart_window = Some("60s".into());
7498        assert_eq!(
7499            c.declared_supervisor_slots(),
7500            vec![
7501                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7502                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7503            ]
7504        );
7505    }
7506
7507    #[test]
7508    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7509        // Scalar-value pin: the four author-facing kebab-case labels the
7510        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
7511        // supervision-tree slot axis, one arm per typed slot. Mirrors the
7512        // peer scalar-value pins the sibling
7513        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
7514        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
7515        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
7516        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
7517        // top-level M3 slot consts carry, so all three kind-scoped
7518        // typed-slot-family author-facing-label axes route through one
7519        // canonical per-arm declaration. A future rebrand
7520        // (`:estrategia` → `:strategy` for English uniformity,
7521        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
7522        // `MaxIntensity` name, `:restart-window` → `:period` matching
7523        // OTP's `Period` name, `:children` → `:workers` matching Elixir
7524        // idiom) lands as an edit to exactly one const, and every
7525        // consumer that reaches for the label picks it up at build time
7526        // rather than at runtime as a downstream mismatch.
7527        assert_eq!(
7528            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7529            ":estrategia"
7530        );
7531        assert_eq!(
7532            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
7533            ":max-restarts"
7534        );
7535        assert_eq!(
7536            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7537            ":restart-window"
7538        );
7539        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
7540    }
7541
7542    #[test]
7543    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
7544        // Production-through-const pin: the four per-arm labels the
7545        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
7546        // return `Vec` route through the lifted
7547        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
7548        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
7549        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
7550        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
7551        // canonical declaration order. A future re-order or drift at the
7552        // tagger (a rename that reaches the tagger but not the const, or
7553        // vice versa) surfaces here at build time rather than at runtime
7554        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
7555        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7556        // commit. Mirror of the peer
7557        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
7558        // (f49c8b0) and
7559        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
7560        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
7561        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
7562        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7563        c.estrategia = Some(RestartStrategy::OneForOne);
7564        c.max_restarts = Some(5);
7565        c.restart_window = Some("60s".into());
7566        c.children = vec![ChildSpec {
7567            caixa: "worker".into(),
7568            versao: "^0.1".into(),
7569            restart: RestartPolicy::Permanent,
7570        }];
7571        assert_eq!(
7572            c.declared_supervisor_slots(),
7573            vec![
7574                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
7575                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
7576                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
7577                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
7578            ]
7579        );
7580    }
7581
7582    #[test]
7583    fn declared_servico_slots_empty_for_bare_caixa() {
7584        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7585        assert!(c.declared_servico_slots().is_empty());
7586    }
7587
7588    #[test]
7589    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
7590        use crate::{UpgradeFromEntry, UpgradeInstruction};
7591        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7592        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
7593        // the canonical declaration order is preserved regardless of
7594        // which subset is populated.
7595        c.limits = Some(crate::LimitsSpec {
7596            fuel: Some(1_000_000),
7597            ..Default::default()
7598        });
7599        c.upgrade_from = vec![UpgradeFromEntry {
7600            from: "0.1.0".into(),
7601            instructions: vec![UpgradeInstruction::Restart],
7602        }];
7603        assert_eq!(
7604            c.declared_servico_slots(),
7605            vec![
7606                crate::render::M2_AUTHOR_KEY_LIMITS,
7607                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
7608            ]
7609        );
7610    }
7611
7612    #[test]
7613    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
7614        // Scalar-value pin: the three author-facing kebab-case labels
7615        // the `(defcaixa … :<slot> (…))` surface admits on the M2
7616        // top-level slot axis, one arm per typed slot. Mirrors the peer
7617        // scalar-value pin the sibling renderer-side
7618        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
7619        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
7620        // consts carry, so both halves of the M2 top-level slot dual
7621        // axis (author-facing kebab-case label + renderer-side
7622        // camelCase overlay-container wire key) route through one
7623        // canonical per-arm declaration. A future rebrand
7624        // (`:limits` → `:sandbox` matching Lunatic per-process
7625        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
7626        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
7627        // matching Erlang's verbatim appup name) lands as an edit to
7628        // exactly one const, and every consumer that reaches for the
7629        // label picks it up at build time rather than at runtime as a
7630        // downstream mismatch.
7631        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
7632        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
7633        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
7634    }
7635
7636    #[test]
7637    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
7638        // Production-through-const pin: the three per-arm labels the
7639        // [`Caixa::declared_servico_slots`] tagger pushes onto its
7640        // return `Vec` route through the lifted
7641        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
7642        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
7643        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
7644        // declaration order. A future re-order or drift at the tagger
7645        // (a rename that reaches the tagger but not the const, or vice
7646        // versa) surfaces here at build time rather than at runtime as
7647        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
7648        // `slots: <stale-kebab-case>` diagnostic far from the rename's
7649        // commit. Mirror of the peer
7650        // [`crate::behavior::BehaviorSpec::declared_slots`] production
7651        // tagger pin (889dc18) on the sibling per-callback axis.
7652        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
7653        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7654        c.limits = Some(crate::LimitsSpec {
7655            fuel: Some(1_000_000),
7656            ..Default::default()
7657        });
7658        c.behavior = Some(BehaviorSpec {
7659            on_init: Some(PathBuf::from("lib/init.lisp")),
7660            ..Default::default()
7661        });
7662        c.upgrade_from = vec![UpgradeFromEntry {
7663            from: "0.1.0".into(),
7664            instructions: vec![UpgradeInstruction::Restart],
7665        }];
7666        assert_eq!(
7667            c.declared_servico_slots(),
7668            vec![
7669                crate::render::M2_AUTHOR_KEY_LIMITS,
7670                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
7671                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
7672            ]
7673        );
7674    }
7675
7676    #[test]
7677    fn existing_manifests_unaffected_by_new_optional_slots() {
7678        // Regression test: a caixa.lisp authored before M2 typed slots
7679        // should still parse + serialize cleanly. The bare `defcaixa`
7680        // emitted by `Caixa::template` has none of the new fields.
7681        let src = Caixa::template("legacy");
7682        let c = Caixa::from_lisp(&src).unwrap();
7683        assert!(c.limits.is_none());
7684        assert!(c.behavior.is_none());
7685        assert!(c.upgrade_from.is_empty());
7686        assert!(c.estrategia.is_none());
7687        assert!(c.children.is_empty());
7688
7689        // And to_lisp emits a manifest with the new slots in the
7690        // empty/default state — round-trippable.
7691        let emitted = c.to_lisp();
7692        let back = Caixa::from_lisp(&emitted).unwrap();
7693        assert_eq!(c, back);
7694    }
7695
7696    #[test]
7697    fn validate_deps_accepts_canonical_caixa() {
7698        // Positive control: the bare template — zero deps, zero
7699        // deps_dev — passes the gate trivially. A future axis added to
7700        // `Dep::validate` mustn't regress an empty-deps caixa to a
7701        // build error.
7702        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7703        c.validate_deps().unwrap();
7704    }
7705
7706    #[test]
7707    fn validate_deps_rejects_invalid_versao_in_deps() {
7708        // Fail-before-pass-after pin: a malformed `:deps :versao`
7709        // surfaces at validate_deps() time, not at lacre-resolve time.
7710        // Mirrors `rejects_invalid_membro_versao_requirement` and
7711        // `validate_rejects_invalid_child_versao_requirement` on the
7712        // other two `:versao` axes.
7713        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7714        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
7715        let err = c.validate_deps().unwrap_err();
7716        assert!(
7717            matches!(
7718                err,
7719                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7720                    if nome == "caixa-teia" && versao == "^bad-version"
7721            ),
7722            "got {err:?}"
7723        );
7724    }
7725
7726    #[test]
7727    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
7728        // Parity pin: `:deps-dev` must run through the same per-entry
7729        // validator as `:deps` — a typo in either axis surfaces the
7730        // same diagnostic. Without this leg, `:deps-dev` would be a
7731        // second-class citizen of the typed surface and an author
7732        // could land a build that passes validate_deps but fails at
7733        // `feira lock`-time when the dev-dep is resolved for a test
7734        // build.
7735        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7736        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
7737        let err = c.validate_deps().unwrap_err();
7738        assert!(
7739            matches!(
7740                err,
7741                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7742                    if nome == "tatara-check" && versao == "^^0.1"
7743            ),
7744            "got {err:?}"
7745        );
7746    }
7747
7748    #[test]
7749    fn validate_deps_runs_deps_before_deps_dev() {
7750        // Order pin: when both lists carry typos, the `:deps`
7751        // diagnostic surfaces first. The author's mental model is
7752        // "runtime deps are load-bearing; dev deps are scaffolding";
7753        // surfacing the runtime axis first matches that hierarchy.
7754        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7755        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
7756        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
7757        let err = c.validate_deps().unwrap_err();
7758        assert!(
7759            matches!(
7760                err,
7761                crate::dep::DepError::VersaoInvalid { ref nome, .. }
7762                    if nome == "runtime-dep"
7763            ),
7764            "expected `:deps` typo to surface first, got {err:?}"
7765        );
7766    }
7767
7768    #[test]
7769    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
7770        // Positive control sweep across both lists. Pin every
7771        // canonical Cargo-shaped form so a future tightening of the
7772        // accepted set surfaces here as a test failure (parity with
7773        // `accepts_canonical_membro_versao_forms` and
7774        // `validate_accepts_canonical_child_versao_forms`).
7775        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7776        c.deps = vec![
7777            Dep::simple("caret", "^0.1"),
7778            Dep::simple("tilde", "~0.1.2"),
7779            Dep::simple("exact", "0.1.0"),
7780            Dep::simple("wildcard", "*"),
7781            Dep::simple("multi-range", ">=0.1, <2"),
7782        ];
7783        c.deps_dev = vec![
7784            Dep::simple("dev-caret", "^0.1"),
7785            Dep::simple("dev-wildcard", "*"),
7786        ];
7787        c.validate_deps().unwrap();
7788    }
7789
7790    #[test]
7791    fn validate_deps_diagnostic_carries_offending_dep() {
7792        // Diagnostic-shape pin: the error names the offending entry's
7793        // `:nome` + `:versao` verbatim and carries a non-empty
7794        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
7795        // run can render the diagnostic without re-parsing.
7796        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7797        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
7798        let err = c.validate_deps().unwrap_err();
7799        let crate::dep::DepError::VersaoInvalid {
7800            nome,
7801            versao,
7802            reason,
7803        } = err
7804        else {
7805            panic!("expected VersaoInvalid, got other variant");
7806        };
7807        assert_eq!(nome, "caixa-teia");
7808        assert_eq!(versao, "not-a-req");
7809        assert!(
7810            !reason.is_empty(),
7811            "VersaoInvalid `reason` must carry the parser's wording verbatim"
7812        );
7813    }
7814
7815    #[test]
7816    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
7817        // Cross-axis pin: `validate_deps` walks both :deps and
7818        // :deps-dev through `Dep::validate`, and the new fonte gate
7819        // (`:tag` + `:branch` both set — the canonical "pin drift"
7820        // footgun) must surface from the :deps-dev arm with the
7821        // offending entry's :nome named. Pin the :deps-dev arm
7822        // explicitly so a future shortcut that only walks :deps
7823        // surfaces here as a regression.
7824        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7825        c.deps_dev = vec![Dep {
7826            nome: "dev-only".into(),
7827            versao: "^0.1".into(),
7828            fonte: Some(crate::DepSource::Git {
7829                repo: "github:p/x".into(),
7830                tag: Some("v1".into()),
7831                rev: None,
7832                branch: Some("main".into()),
7833            }),
7834            opcional: false,
7835            caracteristicas: vec![],
7836        }];
7837        let err = c.validate_deps().unwrap_err();
7838        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
7839            panic!("expected FontePinAmbiguous from :deps-dev walk");
7840        };
7841        assert_eq!(nome, "dev-only");
7842        assert!(pins.contains(":tag") && pins.contains(":branch"));
7843    }
7844
7845    #[test]
7846    fn validate_deps_rejects_empty_repo_in_deps() {
7847        // Parity pin on the :deps arm: an empty :repo on the runtime
7848        // deps list surfaces the same FonteRepoEmpty diagnostic the
7849        // dep.rs per-entry tests pin, naming the offending entry.
7850        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7851        c.deps = vec![Dep {
7852            nome: "runtime".into(),
7853            versao: "^0.1".into(),
7854            fonte: Some(crate::DepSource::Git {
7855                repo: String::new(),
7856                tag: Some("v1".into()),
7857                rev: None,
7858                branch: None,
7859            }),
7860            opcional: false,
7861            caracteristicas: vec![],
7862        }];
7863        let err = c.validate_deps().unwrap_err();
7864        assert!(
7865            matches!(
7866                err,
7867                crate::dep::DepError::FonteRepoEmpty { ref nome }
7868                    if nome == "runtime"
7869            ),
7870            "got {err:?}"
7871        );
7872    }
7873
7874    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
7875
7876    #[test]
7877    fn validate_deps_rejects_duplicate_nome_in_deps() {
7878        // Fail-before-pass-after pin: two `:deps` entries naming the same
7879        // caixa carry two `:versao` / `:fonte` / feature triples that the
7880        // caixa-resolver's lacre pipeline collapses (the second silently
7881        // overwrites the first at `concrete_versao`-resolve time). The
7882        // gate surfaces the duplicate at validate-time, naming the
7883        // offending caixa + the list, before the resolver-side silent
7884        // drop. Mirrors the peer typed-graph duplicate gates
7885        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
7886        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7887        c.deps = vec![
7888            Dep::simple("caixa-teia", "^0.1"),
7889            Dep::simple("caixa-teia", "^0.2"),
7890        ];
7891        let err = c.validate_deps().unwrap_err();
7892        assert!(
7893            matches!(
7894                err,
7895                crate::dep::DepError::DuplicateNome { ref nome, list }
7896                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
7897            ),
7898            "got {err:?}"
7899        );
7900    }
7901
7902    #[test]
7903    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
7904        // Parity pin: `:deps-dev` runs through the same per-list
7905        // duplicate check as `:deps` — neither axis is a second-class
7906        // citizen of the set-not-multiset discipline.
7907        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7908        c.deps_dev = vec![
7909            Dep::simple("tatara-check", "*"),
7910            Dep::simple("tatara-check", "^0.1"),
7911        ];
7912        let err = c.validate_deps().unwrap_err();
7913        assert!(
7914            matches!(
7915                err,
7916                crate::dep::DepError::DuplicateNome { ref nome, list }
7917                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
7918            ),
7919            "got {err:?}"
7920        );
7921    }
7922
7923    #[test]
7924    fn validate_deps_accepts_cross_list_same_nome() {
7925        // The Cargo `[dependencies]` + `[dev-dependencies]` override
7926        // convention is preserved: a name appearing in *both* lists is
7927        // valid (the dev-pin overrides at test/dev time). Only
7928        // within-list duplicates are structurally incoherent — pin the
7929        // permissive cross-list semantics so a future shortcut that
7930        // collapses the two seen-sets into one surfaces here as a test
7931        // failure.
7932        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7933        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
7934        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
7935        c.validate_deps().unwrap();
7936    }
7937
7938    #[test]
7939    fn validate_deps_accepts_distinct_nome_in_both_lists() {
7940        // Positive control: distinct names within each list pass — the
7941        // gate's identity element on the canonical authoring shape.
7942        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7943        c.deps = vec![
7944            Dep::simple("caixa-teia", "^0.1"),
7945            Dep::simple("pleme-mesh", "*"),
7946        ];
7947        c.deps_dev = vec![
7948            Dep::simple("tatara-check", "*"),
7949            Dep::simple("dev-shim", "^0.1"),
7950        ];
7951        c.validate_deps().unwrap();
7952    }
7953
7954    #[test]
7955    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
7956        // Diagnostic-precedence pin: a malformed `:versao` on the
7957        // duplicating entry surfaces its narrower `VersaoInvalid`
7958        // diagnostic first, before the cross-entry duplicate gate fires
7959        // — the canonical "per-entry shape before cross-entry uniqueness"
7960        // precedence every peer set-not-multiset gate establishes
7961        // (`*_invalid_fires_before_duplicate_check` pins on
7962        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
7963        // `validate_upgrade_from`).
7964        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7965        c.deps = vec![
7966            Dep::simple("caixa-teia", "^0.1"),
7967            Dep::simple("caixa-teia", "^bad-version"),
7968        ];
7969        let err = c.validate_deps().unwrap_err();
7970        assert!(
7971            matches!(
7972                err,
7973                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
7974                    if nome == "caixa-teia" && versao == "^bad-version"
7975            ),
7976            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
7977        );
7978    }
7979
7980    #[test]
7981    fn validate_deps_duplicate_diagnostic_names_first_collision() {
7982        // First-collision determinism pin: with three entries naming the
7983        // same caixa, the first colliding pair surfaces — not the last.
7984        // Mirrors the peer first-collision posture on every
7985        // duplicate-target gate
7986        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
7987        // — the second entry is the first collision; this gate uses the
7988        // same shape: the second entry's `:nome` lands in the diagnostic
7989        // because `seen.insert(first.nome)` already populated the set).
7990        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
7991        c.deps = vec![
7992            Dep::simple("caixa-teia", "^0.1"),
7993            Dep::simple("caixa-teia", "^0.2"),
7994            Dep::simple("caixa-teia", "^0.3"),
7995        ];
7996        let err = c.validate_deps().unwrap_err();
7997        // The diagnostic carries the offending caixa name; the
7998        // implementation surfaces on the *second* entry (the first
7999        // collision), so the test pins the `:nome` value.
8000        assert!(
8001            matches!(
8002                err,
8003                crate::dep::DepError::DuplicateNome { ref nome, list }
8004                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8005            ),
8006            "got {err:?}"
8007        );
8008    }
8009
8010    #[test]
8011    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
8012        // Cross-list precedence pin: when both lists carry duplicates,
8013        // the `:deps` diagnostic surfaces first — same author-mental-
8014        // model ordering the `validate_deps_runs_deps_before_deps_dev`
8015        // pin establishes for malformed `:versao` (runtime axis before
8016        // dev axis).
8017        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8018        c.deps = vec![
8019            Dep::simple("runtime-dep", "^0.1"),
8020            Dep::simple("runtime-dep", "^0.2"),
8021        ];
8022        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
8023        let err = c.validate_deps().unwrap_err();
8024        assert!(
8025            matches!(
8026                err,
8027                crate::dep::DepError::DuplicateNome { ref nome, list }
8028                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8029            ),
8030            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
8031        );
8032    }
8033
8034    #[test]
8035    fn validate_deps_empty_lists_pass_duplicate_gate() {
8036        // Empty-set identity pin: the bare template (zero deps, zero
8037        // deps_dev) passes the duplicate gate as the gate's identity
8038        // element. A future tighten that conflates "empty" with
8039        // "missing" would regress this baseline.
8040        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8041        c.validate_deps().unwrap();
8042    }
8043
8044    #[test]
8045    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
8046        // Diagnostic-shape pin: the `list:` field tags which list the
8047        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
8048        // `feira lint` run can route the author to the right block in
8049        // their caixa.lisp without re-deriving the list from context.
8050        // Same self-locating shape every peer per-axis diagnostic
8051        // already exposes.
8052        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8053        c.deps_dev = vec![
8054            Dep::simple("dev-thing", "*"),
8055            Dep::simple("dev-thing", "^0.1"),
8056        ];
8057        let err = c.validate_deps().unwrap_err();
8058        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
8059            panic!("expected DuplicateNome from :deps-dev walk");
8060        };
8061        assert_eq!(nome, "dev-thing");
8062        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
8063    }
8064
8065    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
8066
8067    #[test]
8068    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
8069        // Thread-through pin on `:deps`: the per-entry
8070        // `Dep::validate_caracteristicas` gate fires inside
8071        // `Caixa::validate_deps`'s linear walk, so a malformed feature
8072        // list on any `:deps` entry surfaces as a `DepError` from
8073        // `validate_deps` — the same reachability shape every per-entry
8074        // `Dep::validate` arm threads through. Without this pin a future
8075        // shortcut that skips the per-entry `Dep::validate` call on the
8076        // cross-entry-uniqueness path would mask the within-entry
8077        // `:caracteristicas` gates.
8078        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8079        c.deps = vec![Dep {
8080            nome: "caixa-teia".into(),
8081            versao: "^0.1".into(),
8082            fonte: None,
8083            opcional: false,
8084            caracteristicas: vec!["http".into(), "http".into()],
8085        }];
8086        let err = c.validate_deps().unwrap_err();
8087        let crate::dep::DepError::CaracteristicaDuplicate {
8088            nome,
8089            caracteristica,
8090        } = err
8091        else {
8092            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
8093        };
8094        assert_eq!(nome, "caixa-teia");
8095        assert_eq!(caracteristica, "http");
8096    }
8097
8098    #[test]
8099    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
8100        // Peer thread-through pin on `:deps-dev`: same reachability as
8101        // the `:deps` arm above, on the dev-only authoring axis. Pins
8102        // that the `validate_deps` walk visits both lists' per-entry
8103        // gates uniformly. The empty-feature arm carries here so both
8104        // new `:caracteristicas` arms are surfaced via at least one
8105        // `validate_deps` thread-through.
8106        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8107        c.deps_dev = vec![Dep {
8108            nome: "caixa-teia".into(),
8109            versao: "^0.1".into(),
8110            fonte: None,
8111            opcional: false,
8112            caracteristicas: vec![String::new()],
8113        }];
8114        let err = c.validate_deps().unwrap_err();
8115        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
8116            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
8117        };
8118        assert_eq!(nome, "caixa-teia");
8119    }
8120
8121    #[test]
8122    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
8123        // Thread-through pin on `:deps`: the per-entry
8124        // `Dep::validate_caracteristicas` value-shape gate (lifted via
8125        // `crate::render::is_cargo_feature_name`) fires inside
8126        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
8127        // a structurally invalid feature name on any `:deps` entry
8128        // surfaces as `DepError::CaracteristicaInvalid` from
8129        // `validate_deps` — the same reachability shape every per-entry
8130        // `Dep::validate` arm threads through. Without this pin a
8131        // future shortcut that skips the per-entry `Dep::validate` call
8132        // on the cross-entry-uniqueness path would mask the within-
8133        // entry `:caracteristicas` value-shape gate.
8134        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8135        c.deps = vec![Dep {
8136            nome: "caixa-teia".into(),
8137            versao: "^0.1".into(),
8138            fonte: None,
8139            opcional: false,
8140            caracteristicas: vec!["+http".into()],
8141        }];
8142        let err = c.validate_deps().unwrap_err();
8143        let crate::dep::DepError::CaracteristicaInvalid {
8144            nome,
8145            caracteristica,
8146            ..
8147        } = err
8148        else {
8149            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
8150        };
8151        assert_eq!(nome, "caixa-teia");
8152        assert_eq!(caracteristica, "+http");
8153    }
8154
8155    #[test]
8156    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
8157        // Peer thread-through pin on `:deps-dev`: same reachability as
8158        // the `:deps` arm above, on the dev-only authoring axis. The
8159        // `http/json` shape carries here so the segment-separator
8160        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
8161        // confusion footgun) is surfaced via the cross-entry walk too —
8162        // pinning that the `:deps-dev` list visits the same per-entry
8163        // value-shape gate as the `:deps` list.
8164        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8165        c.deps_dev = vec![Dep {
8166            nome: "caixa-teia".into(),
8167            versao: "^0.1".into(),
8168            fonte: None,
8169            opcional: false,
8170            caracteristicas: vec!["http/json".into()],
8171        }];
8172        let err = c.validate_deps().unwrap_err();
8173        let crate::dep::DepError::CaracteristicaInvalid {
8174            nome,
8175            caracteristica,
8176            ..
8177        } = err
8178        else {
8179            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
8180        };
8181        assert_eq!(nome, "caixa-teia");
8182        assert_eq!(caracteristica, "http/json");
8183    }
8184
8185    #[test]
8186    fn to_lisp_preserves_deps() {
8187        let src = r#"
8188(defcaixa
8189  :nome "x"
8190  :versao "0.1.0"
8191  :kind Biblioteca
8192  :deps ((:nome "a" :versao "^0.1")
8193         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
8194"#;
8195        let c1 = Caixa::from_lisp(src).unwrap();
8196        let emitted = c1.to_lisp();
8197        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
8198        assert_eq!(c1.deps, c2.deps);
8199    }
8200
8201    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
8202
8203    fn caixa_with_nome(nome: &str) -> Caixa {
8204        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
8205        c.nome = nome.to_string();
8206        c
8207    }
8208
8209    #[test]
8210    fn validate_nome_accepts_canonical_template() {
8211        // Positive control: the bare `feira init`-style template's
8212        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
8213        // not regress this baseline shape. A future tightening of the
8214        // accepted set surfaces here as a test failure first.
8215        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8216        c.validate_nome().unwrap();
8217    }
8218
8219    #[test]
8220    fn validate_nome_accepts_canonical_forms() {
8221        // Positive-set sweep: each realistic caixa-name shape the K8s
8222        // apiserver accepts as a `metadata.name` label must pass —
8223        // single-word, hyphen-joined, version-suffixed, single-char,
8224        // two-char, digit-start (DNS-1123 allows this; the stricter
8225        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
8226        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
8227        // the peer member-name axis.
8228        for nome in [
8229            "checkout",
8230            "cart-v2",
8231            "a",
8232            "db",
8233            "3rd-party-shim",
8234            "payment-retry",
8235            "0",
8236        ] {
8237            caixa_with_nome(nome)
8238                .validate_nome()
8239                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
8240        }
8241    }
8242
8243    #[test]
8244    fn validate_nome_rejects_empty() {
8245        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
8246        // an empty `:nome` (the derive macro stores the raw String);
8247        // the gate's empty arm names the offending axis with a narrower
8248        // diagnostic than the `NomeInvalid` parse arm would emit.
8249        let c = caixa_with_nome("");
8250        let err = c.validate_nome().unwrap_err();
8251        assert_eq!(err, ManifestError::NomeEmpty);
8252    }
8253
8254    #[test]
8255    fn validate_nome_rejects_uppercase() {
8256        // The canonical "I copied the TitleCase display name verbatim"
8257        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
8258        // admission on every derived artifact (Helm chart, ComputeUnit,
8259        // CNP, HTTPRoute, label values); the gate moves the diagnostic
8260        // to the source `caixa.lisp` and the reason suggests the
8261        // lowercased fix verbatim.
8262        let c = caixa_with_nome("MyApp");
8263        let err = c.validate_nome().unwrap_err();
8264        let ManifestError::NomeInvalid { nome, reason } = err else {
8265            panic!("expected NomeInvalid for uppercase :nome");
8266        };
8267        assert_eq!(nome, "MyApp");
8268        assert!(
8269            reason.contains("uppercase") && reason.contains("myapp"),
8270            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
8271        );
8272    }
8273
8274    #[test]
8275    fn validate_nome_rejects_underscore() {
8276        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
8277        // `_`; the apiserver rejects on admission across every derived
8278        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
8279        // and `:children :caixa` (31bfa43).
8280        let c = caixa_with_nome("my_app");
8281        let err = c.validate_nome().unwrap_err();
8282        assert!(
8283            matches!(
8284                err,
8285                ManifestError::NomeInvalid { ref nome, ref reason }
8286                    if nome == "my_app" && reason.contains('_')
8287            ),
8288            "got {err:?}"
8289        );
8290    }
8291
8292    #[test]
8293    fn validate_nome_rejects_dot() {
8294        // A `:nome` is a single DNS-1123 label, not a subdomain. The
8295        // "I want to namespace with `.`" footgun the gate redirects to
8296        // `-` via the shared predicate's reason wording.
8297        let c = caixa_with_nome("team.app");
8298        let err = c.validate_nome().unwrap_err();
8299        assert!(
8300            matches!(
8301                err,
8302                ManifestError::NomeInvalid { ref nome, ref reason }
8303                    if nome == "team.app" && reason.contains('.')
8304            ),
8305            "got {err:?}"
8306        );
8307    }
8308
8309    #[test]
8310    fn validate_nome_rejects_leading_hyphen() {
8311        // DNS-1123 boundary rule: the label must start with an ASCII
8312        // alphanumeric. Pin the leading-`-` arm explicitly.
8313        let c = caixa_with_nome("-app");
8314        let err = c.validate_nome().unwrap_err();
8315        assert!(
8316            matches!(
8317                err,
8318                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
8319            ),
8320            "got {err:?}"
8321        );
8322    }
8323
8324    #[test]
8325    fn validate_nome_rejects_trailing_hyphen() {
8326        // Symmetric arm of the boundary rule, pinned separately so a
8327        // future relaxation that only checks the leading position
8328        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
8329        // and `_with_trailing_hyphen` on the supervisor / aplicacao
8330        // axes.
8331        let c = caixa_with_nome("app-");
8332        let err = c.validate_nome().unwrap_err();
8333        assert!(
8334            matches!(
8335                err,
8336                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
8337            ),
8338            "got {err:?}"
8339        );
8340    }
8341
8342    #[test]
8343    fn validate_nome_rejects_unicode() {
8344        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
8345        // bytes are rejected by the K8s apiserver on every name axis.
8346        let c = caixa_with_nome("café");
8347        let err = c.validate_nome().unwrap_err();
8348        assert!(
8349            matches!(
8350                err,
8351                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
8352            ),
8353            "got {err:?}"
8354        );
8355    }
8356
8357    #[test]
8358    fn validate_nome_rejects_whitespace() {
8359        // The paste-from-sketch / paste-from-spec footgun. Internal
8360        // whitespace is rejected by every K8s name axis.
8361        let c = caixa_with_nome("my app");
8362        let err = c.validate_nome().unwrap_err();
8363        assert!(
8364            matches!(
8365                err,
8366                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
8367            ),
8368            "got {err:?}"
8369        );
8370    }
8371
8372    #[test]
8373    fn validate_nome_rejects_too_long() {
8374        // 64-byte boundary pin: the K8s apiserver rejects any
8375        // `metadata.name` over 63 bytes at admission; the diagnostic
8376        // names both the 63-byte cap and the actual length so the
8377        // author can shorten in one edit. Mirrors `_too_long` on the
8378        // peer member-/cluster-/child-name axes.
8379        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
8380        let c = caixa_with_nome(&over);
8381        let err = c.validate_nome().unwrap_err();
8382        let ManifestError::NomeInvalid { nome, reason } = err else {
8383            panic!("expected NomeInvalid for over-cap :nome");
8384        };
8385        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
8386        assert!(
8387            reason.contains("63") && reason.contains("64"),
8388            "diagnostic must name the cap + actual length, got {reason:?}"
8389        );
8390    }
8391
8392    #[test]
8393    fn nome_max_length_validates() {
8394        // The 63-byte cap exactly — the boundary-accepting case pinned
8395        // alongside `validate_nome_rejects_too_long` so a future cap
8396        // shift surfaces both arms simultaneously. Mirrors
8397        // `membro_caixa_max_length_validates`,
8398        // `placement_cluster_max_length_validates`,
8399        // `child_caixa_max_length_validates`.
8400        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
8401        caixa_with_nome(&at_cap).validate_nome().unwrap();
8402    }
8403
8404    #[test]
8405    fn nome_empty_takes_precedence_over_invalid() {
8406        // Order pin: the empty arm fires before the predicate is
8407        // consulted. Empty < invalid in self-locating-ness — the
8408        // narrower `NomeEmpty` diagnostic doesn't carry a useless
8409        // `nome: ""` reference into the parser-shaped reason. Mirrors
8410        // `membro_caixa_empty_takes_precedence_over_invalid` on the
8411        // peer axis (3f9d7a0).
8412        let c = caixa_with_nome("");
8413        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
8414    }
8415
8416    #[test]
8417    fn nome_invalid_diagnostic_carries_offending_nome() {
8418        // Diagnostic-shape pin: the error names the offending `:nome`
8419        // verbatim with a non-empty parser-shaped reason, so a `feira
8420        // lint` run can render the diagnostic without re-parsing.
8421        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
8422        let c = caixa_with_nome("MyApp");
8423        let err = c.validate_nome().unwrap_err();
8424        let ManifestError::NomeInvalid { nome, reason } = err else {
8425            panic!("expected NomeInvalid variant");
8426        };
8427        assert_eq!(nome, "MyApp");
8428        assert!(
8429            !reason.is_empty(),
8430            "NomeInvalid `reason` must carry the predicate's wording verbatim"
8431        );
8432    }
8433
8434    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
8435    //
8436    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
8437    // via DNS-1123; this second-axis gate caps the joint
8438    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
8439    // canonical [`crate::lareira_chart_name`] helper's doc comment
8440    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
8441    // "the M4 admission webhook will pin the joint-length invariant
8442    // when it lands". These tests pin it at the manifest-validate
8443    // layer instead, fail-before-pass-after on the 56-byte boundary.
8444
8445    #[test]
8446    fn validate_nome_chart_name_budget_accepts_canonical_template() {
8447        // Positive control: the bare `feira init`-style template's
8448        // `:nome` ("demo") sits far below the cap; the gate must not
8449        // regress this baseline. Same shape every peer
8450        // value-shape-gate baseline pin uses.
8451        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8452        c.validate_nome_chart_name_budget().unwrap();
8453    }
8454
8455    #[test]
8456    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
8457        // Positive-set sweep across the canonical author surface every
8458        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
8459        // `worker`, the `checkout-aplicacao` example members, the
8460        // `example-attest` caixa-tatara fixture). Every value sits
8461        // far below the 55-byte per-`:nome` budget. Same shape every
8462        // peer per-axis baseline pin uses.
8463        for nome in [
8464            "hello-rio",
8465            "cart",
8466            "checkout",
8467            "worker",
8468            "example-attest",
8469            "demo",
8470            "a",
8471        ] {
8472            caixa_with_nome(nome)
8473                .validate_nome_chart_name_budget()
8474                .unwrap_or_else(|e| {
8475                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
8476                });
8477        }
8478    }
8479
8480    #[test]
8481    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
8482        // Boundary-accepting case at the 55-byte per-`:nome` budget —
8483        // the joint chart name is exactly 63 bytes, the DNS-1123 label
8484        // cap. Pinned alongside the rejecting-arm test so a future cap
8485        // shift surfaces both arms simultaneously. Mirrors
8486        // `nome_max_length_validates` on the peer bare-`:nome` axis.
8487        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
8488        caixa_with_nome(&at_cap)
8489            .validate_nome_chart_name_budget()
8490            .unwrap();
8491    }
8492
8493    #[test]
8494    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
8495        // Fail-before-pass-after pin on the 56-byte boundary: the
8496        // smallest `:nome` length that overflows the joint chart-name
8497        // cap. The inner [`is_dns_1123_label`] gate
8498        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
8499        // this gate it silently passed the manifest-validate cascade
8500        // and surfaced as a `helm lint` / apiserver rejection on the
8501        // rendered chart name far from the source `caixa.lisp`, with
8502        // no field naming the overflow. With this gate the diagnostic
8503        // names the offending `:nome` verbatim alongside the rendered
8504        // chart name and the budget, so the author can shorten in one
8505        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
8506        // bare-`:nome` axis.
8507        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8508        let c = caixa_with_nome(&over);
8509        let err = c.validate_nome_chart_name_budget().unwrap_err();
8510        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
8511            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
8512        };
8513        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8514        assert_eq!(nome, over);
8515        assert!(
8516            reason.contains("63") && reason.contains("64") && reason.contains("55"),
8517            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
8518             and the per-`:nome` budget (55), got {reason:?}"
8519        );
8520    }
8521
8522    #[test]
8523    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
8524        // The 63-byte `:nome` boundary — passes the bare-`:nome`
8525        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
8526        // joint chart name that overflows the DNS-1123 label cap
8527        // structurally. The most stringent fail-before-pass-after
8528        // surface: every `:nome` in the 56..=63-byte range passed the
8529        // prior cascade and broke at admission.
8530        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
8531        let c = caixa_with_nome(&bare_max);
8532        // The bare-`:nome` gate accepts the 63-byte length.
8533        c.validate_nome().unwrap();
8534        // The new joint-length gate rejects it.
8535        let err = c.validate_nome_chart_name_budget().unwrap_err();
8536        assert!(
8537            matches!(
8538                err,
8539                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
8540                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
8541            ),
8542            "got {err:?}"
8543        );
8544    }
8545
8546    #[test]
8547    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
8548        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
8549        // name appears verbatim in the diagnostic so the author sees
8550        // exactly the string the apiserver / `helm lint` would have
8551        // rejected — no re-derivation required to grep the source.
8552        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
8553        // on the bare-`:nome` axis.
8554        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
8555        let c = caixa_with_nome(&over);
8556        let err = c.validate_nome_chart_name_budget().unwrap_err();
8557        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
8558            panic!("expected NomeChartNameBudgetExceeded variant");
8559        };
8560        assert_eq!(nome, over);
8561        let expected_chart = crate::lareira_chart_name(&over);
8562        assert!(
8563            reason.contains(&expected_chart),
8564            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
8565             got {reason:?}"
8566        );
8567        assert!(
8568            reason.contains("lareira-"),
8569            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
8570        );
8571    }
8572
8573    #[test]
8574    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
8575        // Order pin on the layout cascade: the narrower
8576        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
8577        // joint-length budget. A structurally-malformed `:nome` (here:
8578        // uppercase) surfaces its specific shape error rather than
8579        // the chart-name-budget error, even when the joint length
8580        // would also overflow — the narrower diagnostic is more
8581        // self-locating. Mirrors the cascade-precedence pins peer
8582        // gates already use (e.g. `EntradaParaEmpty` before
8583        // `EntradaParaInvalid`).
8584        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8585        let c = caixa_with_nome(&over);
8586        // The bare-shape gate fires first.
8587        let err = c.validate_nome().unwrap_err();
8588        assert!(
8589            matches!(err, ManifestError::NomeInvalid { .. }),
8590            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
8591        );
8592        // And the layout verify cascade surfaces that diagnostic, not
8593        // the budget arm. Inject a path-exists oracle so the cascade
8594        // gets past the manifest-presence check and into the
8595        // value-shape gates.
8596        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
8597        let err = crate::LayoutInvariants::verify(
8598            &layout,
8599            &c,
8600            std::path::Path::new("/tmp/caixa-test-fake-root"),
8601        )
8602        .unwrap_err();
8603        let issue = err.to_string();
8604        assert!(
8605            issue.contains("DNS-1123") || issue.contains("uppercase"),
8606            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
8607             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
8608        );
8609    }
8610
8611    #[test]
8612    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
8613        // Cross-axis envelope pin: the layout cascade wraps both
8614        // bare-`:nome` and joint-length-`:nome` failures through the
8615        // same [`LayoutError::NomeViolation`] envelope, since both
8616        // arms are on the `:nome` axis. The user's diagnostic stays
8617        // self-locating ("which axis"), and a future consumer that
8618        // dispatches on the layout-error variant (e.g. a `feira lint`
8619        // exit-code mapping) sees a single per-axis envelope. The
8620        // wrapped `issue:` carries the full inner diagnostic.
8621        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
8622        let c = caixa_with_nome(&over);
8623        // The bare-shape gate accepts.
8624        c.validate_nome().unwrap();
8625        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
8626        let err = crate::LayoutInvariants::verify(
8627            &layout,
8628            &c,
8629            std::path::Path::new("/tmp/caixa-test-fake-root"),
8630        )
8631        .unwrap_err();
8632        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
8633            panic!("expected LayoutError::NomeViolation, got {err:?}");
8634        };
8635        assert_eq!(caixa, over);
8636        assert!(
8637            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
8638            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
8639        );
8640    }
8641
8642    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
8643
8644    fn caixa_with_versao(versao: &str) -> Caixa {
8645        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8646        c.versao = versao.to_string();
8647        c
8648    }
8649
8650    #[test]
8651    fn validate_versao_accepts_canonical_template() {
8652        // Positive control: the bare `feira init`-style template's
8653        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
8654        // must not regress this baseline shape. A future tightening of
8655        // the accepted set surfaces here as a test failure first.
8656        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8657        c.validate_versao().unwrap();
8658    }
8659
8660    #[test]
8661    fn validate_versao_accepts_canonical_forms() {
8662        // Positive-set sweep: each realistic SemVer-2 shape the
8663        // substrate's downstream consumers accept must pass — bare
8664        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
8665        // build metadata (`+build.42`), the combined form, and the
8666        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
8667        // the peer `:nome` axis (6c992f8).
8668        for versao in [
8669            "0.1.0",
8670            "0.0.0",
8671            "1.0.0",
8672            "0.2.0-rc.1",
8673            "1.0.0-alpha.0",
8674            "1.0.0+build.42",
8675            "1.0.0-rc.1+build.42",
8676            "10.20.30",
8677        ] {
8678            caixa_with_versao(versao)
8679                .validate_versao()
8680                .unwrap_or_else(|e| {
8681                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
8682                });
8683        }
8684    }
8685
8686    #[test]
8687    fn validate_versao_rejects_empty() {
8688        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
8689        // an empty `:versao` (the derive macro stores the raw String);
8690        // the gate's empty arm names the offending axis with a narrower
8691        // diagnostic than the `VersaoInvalid` parse arm would emit.
8692        // Mirrors `validate_nome_rejects_empty` (6c992f8).
8693        let c = caixa_with_versao("");
8694        let err = c.validate_versao().unwrap_err();
8695        assert_eq!(err, ManifestError::VersaoEmpty);
8696    }
8697
8698    #[test]
8699    fn validate_versao_rejects_git_tag_shape() {
8700        // The canonical "I copied the git tag verbatim" footgun —
8701        // `feira publish` *emits* `v<versao>` git tags, so a leaked
8702        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
8703        // shift every downstream consumer's version axis. `semver`
8704        // rejects the leading `v` at parse time; the gate moves the
8705        // diagnostic to the source `caixa.lisp`.
8706        let c = caixa_with_versao("v0.1.0");
8707        let err = c.validate_versao().unwrap_err();
8708        let ManifestError::VersaoInvalid { versao, reason } = err else {
8709            panic!("expected VersaoInvalid for git-tag-shape :versao");
8710        };
8711        assert_eq!(versao, "v0.1.0");
8712        assert!(
8713            !reason.is_empty(),
8714            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
8715        );
8716    }
8717
8718    #[test]
8719    fn validate_versao_rejects_missing_patch() {
8720        // The canonical "I shortened it" footgun — SemVer-2 requires
8721        // three parts. Cargo's `version =` field accepts the shortened
8722        // form as a requirement, conflating the two leaks across the
8723        // typed `:deps :versao` vs top-level `:versao` axes; the gate
8724        // pins the top-level axis to the strict three-part shape.
8725        let c = caixa_with_versao("0.1");
8726        let err = c.validate_versao().unwrap_err();
8727        assert!(
8728            matches!(
8729                err,
8730                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
8731            ),
8732            "got {err:?}"
8733        );
8734    }
8735
8736    #[test]
8737    fn validate_versao_rejects_requirement_shape() {
8738        // The canonical "I leaked a requirement into a version" footgun —
8739        // the typed `:deps :versao` / `:membros :versao` axes accept
8740        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
8741        // concrete `Version`. Without this gate the two typed surfaces
8742        // would silently overlap, and a top-level `^0.1` would surface
8743        // at `helm install` time as a Chart.yaml version rejection far
8744        // from the source `caixa.lisp`.
8745        let c = caixa_with_versao("^0.1");
8746        let err = c.validate_versao().unwrap_err();
8747        assert!(
8748            matches!(
8749                err,
8750                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
8751            ),
8752            "got {err:?}"
8753        );
8754    }
8755
8756    #[test]
8757    fn validate_versao_rejects_docker_tag_shape() {
8758        // The "I confused it with a docker tag" footgun — `latest`,
8759        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
8760        // SemVer rejects at parse time; the gate moves the diagnostic
8761        // to the source `caixa.lisp`.
8762        for bad in ["latest", "main", "stable"] {
8763            let c = caixa_with_versao(bad);
8764            let err = c.validate_versao().unwrap_err();
8765            assert!(
8766                matches!(
8767                    err,
8768                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
8769                ),
8770                "got {err:?} for {bad:?}"
8771            );
8772        }
8773    }
8774
8775    #[test]
8776    fn validate_versao_rejects_four_part_form() {
8777        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
8778        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
8779        // semver crate rejects the extra `.0` at parse time.
8780        let c = caixa_with_versao("0.1.0.0");
8781        let err = c.validate_versao().unwrap_err();
8782        assert!(
8783            matches!(
8784                err,
8785                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
8786            ),
8787            "got {err:?}"
8788        );
8789    }
8790
8791    #[test]
8792    fn versao_empty_takes_precedence_over_invalid() {
8793        // Order pin: the empty arm fires before the parser is consulted.
8794        // Empty < invalid in self-locating-ness — the narrower
8795        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
8796        // reference into the parser-shaped reason. Mirrors
8797        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
8798        // peer axis.
8799        let c = caixa_with_versao("");
8800        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
8801    }
8802
8803    #[test]
8804    fn versao_invalid_diagnostic_carries_offending_versao() {
8805        // Diagnostic-shape pin: the error names the offending `:versao`
8806        // verbatim with a non-empty parser-shaped reason, so a `feira
8807        // lint` run can render the diagnostic without re-parsing.
8808        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
8809        let c = caixa_with_versao("v0.1.0");
8810        let err = c.validate_versao().unwrap_err();
8811        let ManifestError::VersaoInvalid { versao, reason } = err else {
8812            panic!("expected VersaoInvalid variant");
8813        };
8814        assert_eq!(versao, "v0.1.0");
8815        assert!(
8816            !reason.is_empty(),
8817            "VersaoInvalid `reason` must carry the parser's wording verbatim"
8818        );
8819    }
8820
8821    #[test]
8822    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
8823        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
8824        // for `:upgrade-from :from` must also pass `validate_versao` —
8825        // the two `:versao`-typed surfaces (top-level `:versao`,
8826        // `:upgrade-from :from`) consume the *same* `semver::Version`
8827        // parser, so they must agree on the accepted set. Without this
8828        // pin, a future tightening of one axis could silently diverge
8829        // from the other. Mirrors the `:versao` requirement-axis
8830        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
8831        // commits established.
8832        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
8833            // From the canonical UpgradeFromEntry round-trip fixture
8834            // (`upgrade::tests::round_trip_load_module` peers).
8835            let entry = crate::UpgradeFromEntry {
8836                from: versao.to_string(),
8837                instructions: Vec::new(),
8838            };
8839            entry
8840                .validate()
8841                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
8842            caixa_with_versao(versao)
8843                .validate_versao()
8844                .unwrap_or_else(|e| {
8845                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
8846                });
8847        }
8848    }
8849
8850    // ── Caixa::validate_restart_window — supervisor restart-window
8851    //    folds through the shared `supervisor::duration_codec` ────────
8852
8853    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
8854        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8855        c.kind = CaixaKind::Supervisor;
8856        c.restart_window = window.map(str::to_string);
8857        c
8858    }
8859
8860    #[test]
8861    fn validate_restart_window_accepts_none() {
8862        // The canonical "omit the slot to express no reset" shape — a
8863        // `None` raw string is the absence of the typed
8864        // `:restart-window` slot, which is exactly the SupervisorSpec
8865        // "never reset" semantics. The gate must be a no-op here; a
8866        // future tightening that rejected `None` would force every
8867        // supervisor caixa to authoring-time pin a window even when
8868        // the OTP semantics call for none.
8869        caixa_with_restart_window(None)
8870            .validate_restart_window()
8871            .unwrap();
8872    }
8873
8874    #[test]
8875    fn validate_restart_window_accepts_canonical_forms() {
8876        // Positive-set sweep across the canonical authoring units the
8877        // shared `supervisor::duration_codec::parse` accepts —
8878        // matches the codec-side `parse_accepts_integer_canonical_units`
8879        // pin in supervisor::tests so a future codec-side tightening
8880        // surfaces simultaneously on both axes.
8881        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
8882            caixa_with_restart_window(Some(window))
8883                .validate_restart_window()
8884                .unwrap_or_else(|e| {
8885                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
8886                });
8887        }
8888    }
8889
8890    #[test]
8891    fn validate_restart_window_rejects_fractional_seconds() {
8892        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
8893        // as f64 to 1.5 → renders back as `"1500ms"` on first
8894        // serialize). Prior to the fold + this gate, the inline
8895        // `parse_window_inline` accepted f64 magnitudes and silently
8896        // produced a `Duration::from_secs_f64(1.5)`, divergent from
8897        // the shared codec's integer-magnitude discipline on the
8898        // serde-routed siblings. The gate now surfaces a self-locating
8899        // diagnostic at the manifest layer.
8900        let err = caixa_with_restart_window(Some("1.5s"))
8901            .validate_restart_window()
8902            .unwrap_err();
8903        let ManifestError::RestartWindowMalformed {
8904            restart_window,
8905            reason,
8906        } = err
8907        else {
8908            panic!("expected RestartWindowMalformed for fractional seconds");
8909        };
8910        assert_eq!(restart_window, "1.5s");
8911        assert!(
8912            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
8913            "diagnostic must carry shared-codec wording, got {reason:?}"
8914        );
8915    }
8916
8917    #[test]
8918    fn validate_restart_window_rejects_decimal_shaped_integer() {
8919        // The `"1.0s"` class — numerically `1s` exactly, but the
8920        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
8921        // gets the same canonical-form diagnostic.
8922        let err = caixa_with_restart_window(Some("1.0s"))
8923            .validate_restart_window()
8924            .unwrap_err();
8925        assert!(
8926            matches!(
8927                err,
8928                ManifestError::RestartWindowMalformed { ref restart_window, .. }
8929                    if restart_window == "1.0s"
8930            ),
8931            "got {err:?}"
8932        );
8933    }
8934
8935    #[test]
8936    fn validate_restart_window_rejects_half_unit_minute() {
8937        // `"0.5m"` is the unit-fraction footgun — author writes a
8938        // human-readable half-minute, the prior inline parser silently
8939        // produced `Duration::from_secs_f64(30.0)` and serde
8940        // re-emitted as `"30s"`, rewriting author intent. The gate
8941        // closes the loop at the manifest layer.
8942        let err = caixa_with_restart_window(Some("0.5m"))
8943            .validate_restart_window()
8944            .unwrap_err();
8945        let ManifestError::RestartWindowMalformed {
8946            restart_window,
8947            reason,
8948        } = err
8949        else {
8950            panic!("expected RestartWindowMalformed");
8951        };
8952        assert_eq!(restart_window, "0.5m");
8953        assert!(
8954            reason.contains("\"30s\""),
8955            "diagnostic must point at the canonical-form remediation, got {reason:?}"
8956        );
8957    }
8958
8959    #[test]
8960    fn validate_restart_window_rejects_leading_sign() {
8961        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
8962        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
8963        // and was caught by the `num < 0.0` arm which silently
8964        // returned `None`, dropping the author-supplied window). The
8965        // shared codec's digit-only gate rejects both with a unified
8966        // canonical-form diagnostic; the manifest-layer wrapper names
8967        // the offending value.
8968        for bad in ["+30s", "-30s"] {
8969            let err = caixa_with_restart_window(Some(bad))
8970                .validate_restart_window()
8971                .unwrap_err();
8972            assert!(
8973                matches!(
8974                    err,
8975                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
8976                        if restart_window == bad
8977                ),
8978                "got {err:?} for {bad:?}"
8979            );
8980        }
8981    }
8982
8983    #[test]
8984    fn validate_restart_window_rejects_unknown_unit() {
8985        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
8986        // unit dispatch surfaces an `unknown duration unit` reason;
8987        // the manifest-layer wrapper names the offending value.
8988        let err = caixa_with_restart_window(Some("30x"))
8989            .validate_restart_window()
8990            .unwrap_err();
8991        let ManifestError::RestartWindowMalformed {
8992            restart_window,
8993            reason,
8994        } = err
8995        else {
8996            panic!("expected RestartWindowMalformed for unknown unit");
8997        };
8998        assert_eq!(restart_window, "30x");
8999        assert!(
9000            reason.contains("unknown duration unit"),
9001            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
9002        );
9003    }
9004
9005    #[test]
9006    fn validate_restart_window_rejects_garbage() {
9007        // Pure non-numeric magnitude (`"abc"`) falls through to the
9008        // shared codec's narrower `"bad duration magnitude"` arm. Same
9009        // diagnostic shape as the codec-side
9010        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
9011        let err = caixa_with_restart_window(Some("abc"))
9012            .validate_restart_window()
9013            .unwrap_err();
9014        let ManifestError::RestartWindowMalformed {
9015            restart_window,
9016            reason,
9017        } = err
9018        else {
9019            panic!("expected RestartWindowMalformed for garbage");
9020        };
9021        assert_eq!(restart_window, "abc");
9022        assert!(
9023            reason.contains("bad duration magnitude"),
9024            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
9025        );
9026    }
9027
9028    #[test]
9029    fn validate_restart_window_rejects_empty_string() {
9030        // The empty-after-trim edge case — distinct from the `None`
9031        // canonical "omit the slot" shape. The shared codec's
9032        // digit-only gate refuses an empty magnitude; the manifest
9033        // layer names the offending `""` so the author can grep for
9034        // the literal empty value in their `caixa.lisp` and either
9035        // remove the slot (the canonical "no reset" shape) or pin a
9036        // positive duration.
9037        let err = caixa_with_restart_window(Some(""))
9038            .validate_restart_window()
9039            .unwrap_err();
9040        assert!(
9041            matches!(
9042                err,
9043                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9044                    if restart_window.is_empty()
9045            ),
9046            "got {err:?}"
9047        );
9048    }
9049
9050    #[test]
9051    fn validate_restart_window_diagnostic_carries_offending_value() {
9052        // Diagnostic-shape pin (peer with
9053        // `nome_invalid_diagnostic_carries_offending_nome` /
9054        // `versao_invalid_diagnostic_carries_offending_versao`): the
9055        // error names the offending raw `:restart-window` verbatim
9056        // with a non-empty shared-codec-shaped reason, so a `feira
9057        // lint` run can render the diagnostic without re-parsing.
9058        let err = caixa_with_restart_window(Some("1.5s"))
9059            .validate_restart_window()
9060            .unwrap_err();
9061        let ManifestError::RestartWindowMalformed {
9062            restart_window,
9063            reason,
9064        } = err
9065        else {
9066            panic!("expected RestartWindowMalformed variant");
9067        };
9068        assert_eq!(restart_window, "1.5s");
9069        assert!(
9070            !reason.is_empty(),
9071            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
9072        );
9073    }
9074
9075    #[test]
9076    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
9077        // Behavioral parity pin after the fold (`parse_window_inline`
9078        // deletion): the canonical `"60s"` still produces
9079        // `Duration::from_secs(60)` on the typed view — the fold is
9080        // semantically equivalent to the prior inline parser on the
9081        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
9082        // pin, narrowed to the parser-side contract.
9083        let c = caixa_with_restart_window(Some("60s"));
9084        let view = c.supervisor_view().expect("Supervisor kind has a view");
9085        assert_eq!(
9086            view.restart_window,
9087            Some(std::time::Duration::from_secs(60))
9088        );
9089    }
9090
9091    #[test]
9092    fn supervisor_view_soft_swallows_what_validate_rejects() {
9093        // Parity pin between the view-construction path and the
9094        // manifest-level validator: the same `"1.5s"` that surfaces
9095        // `RestartWindowMalformed` at `validate_restart_window` time
9096        // becomes `restart_window: None` on the typed view (the fold
9097        // preserves the existing best-effort shape of `supervisor_view`).
9098        // The contract is: a layout-verifier / `feira lint` flow that
9099        // cares about the malformed-window axis MUST consult
9100        // `validate_restart_window` — relying solely on the view's
9101        // `None` swallows the diagnostic silently. This pin makes the
9102        // expectation a typed invariant.
9103        let c = caixa_with_restart_window(Some("1.5s"));
9104        let view = c.supervisor_view().expect("Supervisor kind has a view");
9105        assert_eq!(
9106            view.restart_window, None,
9107            "view-construction path soft-swallows the parse error to None"
9108        );
9109        // And the manifest-level validator does NOT soft-swallow:
9110        assert!(
9111            matches!(
9112                c.validate_restart_window().unwrap_err(),
9113                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9114                    if restart_window == "1.5s"
9115            ),
9116            "validator must surface the offending value",
9117        );
9118    }
9119
9120    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
9121
9122    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
9123        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9124        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
9125        c.exe = exe.into_iter().map(String::from).collect();
9126        c.servicos = servicos.into_iter().map(String::from).collect();
9127        c
9128    }
9129
9130    #[test]
9131    fn validate_code_paths_accepts_canonical_template() {
9132        // The bare `Caixa::template` shape is the gate's identity element
9133        // on the canonical authoring shape — `:bibliotecas
9134        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
9135        // that the gate is non-disruptive against every existing caixa.
9136        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9137        c.validate_code_paths().unwrap();
9138    }
9139
9140    #[test]
9141    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
9142        // Positive control sweep: a canonical-shaped path on every slot
9143        // passes. Mirrors the peer
9144        // `behavior::validate_every_slot_relative_is_ok` pin.
9145        let c = caixa_with_code_paths(
9146            vec!["lib/demo.lisp", "lib/helpers.lisp"],
9147            vec!["exe/demo", "exe/tool"],
9148            vec!["servicos/demo.computeunit.yaml"],
9149        );
9150        c.validate_code_paths().unwrap();
9151    }
9152
9153    #[test]
9154    fn validate_code_paths_accepts_all_empty_lists() {
9155        // The empty-list identity element: every Caixa with no declared
9156        // code paths trivially passes (Supervisor / Aplicacao kinds rely
9157        // on this — the OwnCode gate already rejected them before the
9158        // path-shape gate runs in the layout, but the validator itself
9159        // must accept the empty shape).
9160        let c = caixa_with_code_paths(vec![], vec![], vec![]);
9161        c.validate_code_paths().unwrap();
9162    }
9163
9164    #[test]
9165    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
9166        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
9167        let err = c.validate_code_paths().unwrap_err();
9168        assert!(
9169            matches!(
9170                err,
9171                ManifestError::CodePathEmpty {
9172                    slot: ":bibliotecas"
9173                }
9174            ),
9175            "got {err:?}",
9176        );
9177    }
9178
9179    #[test]
9180    fn validate_code_paths_rejects_empty_exe_entry() {
9181        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
9182        let err = c.validate_code_paths().unwrap_err();
9183        assert!(
9184            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
9185            "got {err:?}",
9186        );
9187    }
9188
9189    #[test]
9190    fn validate_code_paths_rejects_empty_servicos_entry() {
9191        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
9192        let err = c.validate_code_paths().unwrap_err();
9193        assert!(
9194            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
9195            "got {err:?}",
9196        );
9197    }
9198
9199    #[test]
9200    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
9201        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
9202        // so an absolute path that resolves on disk silently passes the
9203        // layout's existence check — the canonical sandbox-escape on
9204        // the biblioteca axis.
9205        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
9206        let err = c.validate_code_paths().unwrap_err();
9207        let ManifestError::CodePathAbsolute { slot, path } = err else {
9208            panic!("expected CodePathAbsolute, got {err:?}");
9209        };
9210        assert_eq!(slot, ":bibliotecas");
9211        assert_eq!(path, PathBuf::from("/etc/passwd"));
9212    }
9213
9214    #[test]
9215    fn validate_code_paths_rejects_absolute_exe_entry() {
9216        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
9217        let err = c.validate_code_paths().unwrap_err();
9218        let ManifestError::CodePathAbsolute { slot, path } = err else {
9219            panic!("expected CodePathAbsolute, got {err:?}");
9220        };
9221        assert_eq!(slot, ":exe");
9222        assert_eq!(path, PathBuf::from("/usr/bin/env"));
9223    }
9224
9225    #[test]
9226    fn validate_code_paths_rejects_absolute_servicos_entry() {
9227        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
9228        let err = c.validate_code_paths().unwrap_err();
9229        let ManifestError::CodePathAbsolute { slot, path } = err else {
9230            panic!("expected CodePathAbsolute, got {err:?}");
9231        };
9232        assert_eq!(slot, ":servicos");
9233        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
9234    }
9235
9236    #[test]
9237    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
9238        // Canonical "I want a lib from a sibling caixa" footgun on the
9239        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
9240        // downstream, so a leading `..` traverses to the parent of the
9241        // caixa root with no diagnostic at layout time if the resolved
9242        // target exists.
9243        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
9244        let err = c.validate_code_paths().unwrap_err();
9245        let ManifestError::CodePathParentEscape { slot, path } = err else {
9246            panic!("expected CodePathParentEscape, got {err:?}");
9247        };
9248        assert_eq!(slot, ":bibliotecas");
9249        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
9250    }
9251
9252    #[test]
9253    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
9254        // Mid-path `..` defeats the layout's component-aware
9255        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
9256        // `starts_with(<root>/exe)` is true, but the canonical resolution
9257        // lives outside the caixa root. Caught regardless of where the
9258        // `..` sits — mirrors the peer
9259        // `behavior::validate_rejects_parent_escape_mid_path` pin.
9260        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
9261        let err = c.validate_code_paths().unwrap_err();
9262        let ManifestError::CodePathParentEscape { slot, path } = err else {
9263            panic!("expected CodePathParentEscape, got {err:?}");
9264        };
9265        assert_eq!(slot, ":exe");
9266        assert_eq!(path, PathBuf::from("exe/../../escape"));
9267    }
9268
9269    #[test]
9270    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
9271        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
9272        let err = c.validate_code_paths().unwrap_err();
9273        let ManifestError::CodePathParentEscape { slot, path } = err else {
9274            panic!("expected CodePathParentEscape, got {err:?}");
9275        };
9276        assert_eq!(slot, ":servicos");
9277        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
9278    }
9279
9280    #[test]
9281    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
9282        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
9283        // `:servicos`. A manifest with malformed entries on all three
9284        // surfaces surfaces the `:bibliotecas` defect first, mirroring
9285        // the canonical declaration order
9286        // `Caixa::declared_foreign_code_slots` already establishes for
9287        // the foreign-code-slot diagnostic.
9288        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
9289        let err = c.validate_code_paths().unwrap_err();
9290        assert!(
9291            matches!(
9292                err,
9293                ManifestError::CodePathEmpty {
9294                    slot: ":bibliotecas"
9295                }
9296            ),
9297            "got {err:?}",
9298        );
9299    }
9300
9301    #[test]
9302    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
9303        // Within-slot precedence pin: empty → absolute → parent-escape,
9304        // matching the [`PathShapeViolation`] arm-ordering every peer
9305        // `is_sandboxed_relative_path` caller follows (b0c8389
9306        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
9307        // `:bibliotecas` list whose first entry is empty *and* whose
9308        // later entries are absolute/parent-escape surfaces the empty
9309        // arm first, on the lexicographically-earliest offending entry.
9310        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
9311        let err = c.validate_code_paths().unwrap_err();
9312        assert!(
9313            matches!(
9314                err,
9315                ManifestError::CodePathEmpty {
9316                    slot: ":bibliotecas"
9317                }
9318            ),
9319            "got {err:?}",
9320        );
9321    }
9322
9323    #[test]
9324    fn validate_code_paths_first_offender_per_slot_wins() {
9325        // Within a single slot, the first declaration-order offender
9326        // surfaces — pins that the gate is left-to-right deterministic
9327        // (peer of every `*_first_collision_*` pin on duplicate gates).
9328        let c = caixa_with_code_paths(
9329            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
9330            vec![],
9331            vec![],
9332        );
9333        let err = c.validate_code_paths().unwrap_err();
9334        let ManifestError::CodePathAbsolute { slot, path } = err else {
9335            panic!("expected CodePathAbsolute, got {err:?}");
9336        };
9337        assert_eq!(slot, ":bibliotecas");
9338        assert_eq!(path, PathBuf::from("/etc/escape"));
9339    }
9340
9341    #[test]
9342    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
9343        // Diagnostic-shape pin (peer with
9344        // `nome_invalid_diagnostic_carries_offending_nome` /
9345        // `versao_invalid_diagnostic_carries_offending_versao`): the
9346        // error's Display surfaces both the offending `:slot` tag and
9347        // the offending path verbatim, so a `feira lint` run can render
9348        // the diagnostic without re-parsing.
9349        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
9350        let rendered = c.validate_code_paths().unwrap_err().to_string();
9351        assert!(
9352            rendered.contains(":bibliotecas"),
9353            "diagnostic must name the offending slot: {rendered}",
9354        );
9355        assert!(
9356            rendered.contains("/etc/passwd"),
9357            "diagnostic must quote the offending path: {rendered}",
9358        );
9359    }
9360
9361    #[test]
9362    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
9363        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
9364        // axis. Without the gate `feira build` re-parses the same lib
9365        // twice, wasting work and silently masking the author's intent
9366        // to declare a *second* biblioteca.
9367        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
9368        let err = c.validate_code_paths().unwrap_err();
9369        let ManifestError::CodePathDuplicate { slot, path } = err else {
9370            panic!("expected CodePathDuplicate, got {err:?}");
9371        };
9372        assert_eq!(slot, ":bibliotecas");
9373        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
9374    }
9375
9376    #[test]
9377    fn validate_code_paths_rejects_duplicate_exe_entry() {
9378        // Same footgun on the Binario surface. The future `caixa-flake`
9379        // emitter that materializes each `:exe` entry as a flake
9380        // `packages.<name>` derivation would collide on the duplicate
9381        // package key — surfaced here at the typed-validate layer with a
9382        // self-locating diagnostic instead.
9383        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
9384        let err = c.validate_code_paths().unwrap_err();
9385        let ManifestError::CodePathDuplicate { slot, path } = err else {
9386            panic!("expected CodePathDuplicate, got {err:?}");
9387        };
9388        assert_eq!(slot, ":exe");
9389        assert_eq!(path, PathBuf::from("exe/cli"));
9390    }
9391
9392    #[test]
9393    fn validate_code_paths_rejects_duplicate_servicos_entry() {
9394        // Same footgun on the Servico surface. The peer caixa-helm /
9395        // caixa-flux renderers refuse `:servicos.len() != 1` with the
9396        // narrower `UnsupportedServicoCount` diagnostic, but that
9397        // diagnostic surfaces "too many servicos" without naming
9398        // "duplicate entry" — the typed self-locating framing only lands
9399        // at this gate.
9400        let c = caixa_with_code_paths(
9401            vec![],
9402            vec![],
9403            vec![
9404                "servicos/demo.computeunit.yaml",
9405                "servicos/demo.computeunit.yaml",
9406            ],
9407        );
9408        let err = c.validate_code_paths().unwrap_err();
9409        let ManifestError::CodePathDuplicate { slot, path } = err else {
9410            panic!("expected CodePathDuplicate, got {err:?}");
9411        };
9412        assert_eq!(slot, ":servicos");
9413        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
9414    }
9415
9416    #[test]
9417    fn validate_code_paths_accepts_same_path_across_slots() {
9418        // Per-list scope pin: a `:bibliotecas` entry that happens to
9419        // collide with an `:exe` or `:servicos` entry as a *string* is
9420        // not a duplicate by this gate (each list gets its own HashSet),
9421        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
9422        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
9423        // shape on the dep axis). The structural `starts_with(<exe |
9424        // servicos>_dir)` fence at layout time prevents the realistic
9425        // cross-slot collision case from existing on disk, but the gate's
9426        // per-list scope is correct independent of that downstream fence.
9427        let c = caixa_with_code_paths(
9428            vec!["lib/x.lisp"],
9429            vec!["exe/x"],
9430            vec!["servicos/x.computeunit.yaml"],
9431        );
9432        c.validate_code_paths().unwrap();
9433    }
9434
9435    #[test]
9436    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
9437        // Within-slot ordering pin: structural defects (empty / absolute
9438        // / parent-escape) fire before the duplicate gate on the same
9439        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
9440        // surfaces the narrower `CodePathEmpty` for the empty entry
9441        // first, not the duplicate on the later pair — same arm-ordering
9442        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
9443        // `:autores` 86c769b, `:deps` 359fba5).
9444        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
9445        let err = c.validate_code_paths().unwrap_err();
9446        assert!(
9447            matches!(
9448                err,
9449                ManifestError::CodePathEmpty {
9450                    slot: ":bibliotecas"
9451                }
9452            ),
9453            "got {err:?}",
9454        );
9455    }
9456
9457    #[test]
9458    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
9459        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
9460        // duplicates surface before `:exe` duplicates, matching the
9461        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
9462        // order every peer per-slot diagnostic on this surface follows.
9463        let c = caixa_with_code_paths(
9464            vec!["lib/x.lisp", "lib/x.lisp"],
9465            vec!["exe/y", "exe/y"],
9466            vec![],
9467        );
9468        let err = c.validate_code_paths().unwrap_err();
9469        let ManifestError::CodePathDuplicate { slot, path } = err else {
9470            panic!("expected CodePathDuplicate, got {err:?}");
9471        };
9472        assert_eq!(slot, ":bibliotecas");
9473        assert_eq!(path, PathBuf::from("lib/x.lisp"));
9474    }
9475
9476    #[test]
9477    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
9478        // Diagnostic-shape pin (peer with
9479        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
9480        // on the structural arm): the duplicate-arm Display surfaces both
9481        // the offending `:slot` tag and the offending path verbatim, so a
9482        // `feira lint` run can render the diagnostic without re-parsing.
9483        let c = caixa_with_code_paths(
9484            vec![],
9485            vec![],
9486            vec![
9487                "servicos/demo.computeunit.yaml",
9488                "servicos/demo.computeunit.yaml",
9489            ],
9490        );
9491        let rendered = c.validate_code_paths().unwrap_err().to_string();
9492        assert!(
9493            rendered.contains(":servicos"),
9494            "diagnostic must name the offending slot: {rendered}",
9495        );
9496        assert!(
9497            rendered.contains("servicos/demo.computeunit.yaml"),
9498            "diagnostic must quote the offending path: {rendered}",
9499        );
9500    }
9501
9502    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
9503    //
9504    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
9505    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
9506    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
9507    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
9508    // at parse time — the same downstream consumer the peer `:behavior
9509    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
9510    // `:upgrade-from :state-change :script` (33cc830,
9511    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
9512    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
9513    // nix-built executable surface (`"exe/<name>"` shape per the canonical
9514    // [`crate::LayoutError::ExeOutsideDir`] error message and every
9515    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
9516    // is the `.computeunit.yaml` ComputeUnit-CR axis.
9517
9518    #[test]
9519    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
9520        // Canonical "I dragged the wrong file from the workspace tree"
9521        // footgun on the biblioteca axis. Without the gate `feira build`
9522        // hands the extensionless path to `tatara_lisp::read` and fails
9523        // with a parser-shaped diagnostic far from the source caixa.lisp,
9524        // with no field naming the offending `:bibliotecas` entry.
9525        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
9526            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9527            let err = c.validate_code_paths().unwrap_err();
9528            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9529                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9530            };
9531            assert_eq!(slot, ":bibliotecas");
9532            assert_eq!(path, PathBuf::from(relpath));
9533        }
9534    }
9535
9536    #[test]
9537    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
9538        // Wrong-extension sweep across common authoring footguns. Same
9539        // sweep posture as the peer
9540        // `behavior::validate_rejects_wrong_extension` (c97815a) and
9541        // `upgrade::tests::state_change_rejects_wrong_extension_script`
9542        // (33cc830) cases.
9543        for relpath in [
9544            "lib/demo.rs",
9545            "lib/demo.txt",
9546            "lib/demo.md",
9547            "lib/demo.json",
9548            "lib/demo.yaml",
9549            "lib/demo.toml",
9550            "lib/demo.lisp.bak",
9551            "lib/demo.lispx",
9552            "lib/demo.lis",
9553        ] {
9554            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9555            let err = c.validate_code_paths().unwrap_err();
9556            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9557                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9558            };
9559            assert_eq!(slot, ":bibliotecas");
9560            assert_eq!(path, PathBuf::from(relpath));
9561        }
9562    }
9563
9564    #[test]
9565    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
9566        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
9567        // contract. An uppercase `.LISP` shape that the layout's existence
9568        // check would (case-insensitively, on case-insensitive volumes)
9569        // match the on-disk file still mismatches the canonical form the
9570        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
9571        // contract. Mirrors the peer
9572        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
9573        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
9574        // (33cc830) sweeps.
9575        for relpath in [
9576            "lib/demo.LISP",
9577            "lib/demo.Lisp",
9578            "lib/demo.LiSp",
9579            "lib/demo.lISP",
9580        ] {
9581            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9582            let err = c.validate_code_paths().unwrap_err();
9583            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9584                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
9585            };
9586            assert_eq!(slot, ":bibliotecas");
9587            assert_eq!(path, PathBuf::from(relpath));
9588        }
9589    }
9590
9591    #[test]
9592    fn validate_code_paths_accepts_canonical_lisp_shapes() {
9593        // Positive-control sweep through every canonical authoring shape
9594        // every in-tree fixture and the `Caixa::template` scaffold use.
9595        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
9596        // (c97815a) and the lifted predicate's own
9597        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
9598        // (33cc830).
9599        for relpath in [
9600            "lib/demo.lisp",
9601            "lib/handlers.lisp",
9602            "lib/migrations/v01-to-v02.lisp",
9603            "demo.lisp",
9604            "a.lisp",
9605            "./lib/demo.lisp",
9606            "lib/./handlers.lisp",
9607            "lib/migrations/v.0.1.lisp",
9608        ] {
9609            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
9610            c.validate_code_paths()
9611                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
9612        }
9613    }
9614
9615    #[test]
9616    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
9617        // The file-type gate is per-slot — only `:bibliotecas` carries the
9618        // tatara-lisp-source contract. An extensionless `:exe` entry
9619        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
9620        // canonical shapes every in-tree fixture uses, and must continue
9621        // to pass validate. Pins that a future tightening that broadens
9622        // the `.lisp` gate to either axis surfaces as a test failure
9623        // rather than as a silent breaking change to existing valid
9624        // manifests.
9625        let c = caixa_with_code_paths(
9626            vec![],
9627            vec!["exe/demo", "exe/tool"],
9628            vec!["servicos/demo.computeunit.yaml"],
9629        );
9630        c.validate_code_paths().unwrap();
9631    }
9632
9633    #[test]
9634    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
9635        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
9636        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
9637        // sandbox-shape diagnostic first (the `.lisp` remediation would
9638        // be misleading when the offending path can never resolve under
9639        // the caixa root anyway). Mirrors the peer
9640        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
9641        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
9642        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
9643        // on `:upgrade-from :state-change :script` (33cc830).
9644        //
9645        // Empty wins (the strictly-smaller-scope structural arm).
9646        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
9647        assert!(
9648            matches!(
9649                c.validate_code_paths().unwrap_err(),
9650                ManifestError::CodePathEmpty {
9651                    slot: ":bibliotecas"
9652                }
9653            ),
9654            "empty must win over non-lisp-extension",
9655        );
9656        // Absolute wins (the path can't resolve under the caixa root).
9657        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
9658        let err = c.validate_code_paths().unwrap_err();
9659        let ManifestError::CodePathAbsolute { slot, .. } = err else {
9660            panic!("absolute must win over non-lisp-extension, got {err:?}");
9661        };
9662        assert_eq!(slot, ":bibliotecas");
9663        // ParentEscape wins (the path escapes the caixa root).
9664        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
9665        let err = c.validate_code_paths().unwrap_err();
9666        let ManifestError::CodePathParentEscape { slot, .. } = err else {
9667            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
9668        };
9669        assert_eq!(slot, ":bibliotecas");
9670    }
9671
9672    #[test]
9673    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
9674        // Within-slot precedence pin: the per-entry file-type shape gate
9675        // fires before the cross-entry duplicate gate, so the narrower
9676        // structural defect dominates the uniqueness diagnostic. A
9677        // `("lib/x.txt" "lib/x.txt")` shape surfaces
9678        // `CodePathNonLispExtension` on the first entry rather than
9679        // `CodePathDuplicate` on the pair — same posture every per-entry
9680        // shape-gate-precedes-duplicate cascade follows on this surface
9681        // (the empty / absolute / parent-escape arms already precede the
9682        // duplicate arm; the lifted file-type arm joins that set).
9683        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
9684        let err = c.validate_code_paths().unwrap_err();
9685        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
9686            panic!("expected CodePathNonLispExtension, got {err:?}");
9687        };
9688        assert_eq!(slot, ":bibliotecas");
9689        assert_eq!(path, PathBuf::from("lib/x.txt"));
9690    }
9691
9692    #[test]
9693    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
9694        // Diagnostic-shape pin (peer with
9695        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
9696        // on the sandbox-shape arms and
9697        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
9698        // on the duplicate arm): the file-type-arm Display surfaces both
9699        // the offending `:slot` tag, the offending path verbatim, and the
9700        // expected `.lisp` extension named in the remediation text, so a
9701        // `feira lint` run can render the diagnostic without re-parsing.
9702        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
9703        let rendered = c.validate_code_paths().unwrap_err().to_string();
9704        assert!(
9705            rendered.contains(":bibliotecas"),
9706            "diagnostic must name the offending slot: {rendered}",
9707        );
9708        assert!(
9709            rendered.contains("lib/demo.rs"),
9710            "diagnostic must quote the offending path: {rendered}",
9711        );
9712        assert!(
9713            rendered.contains(".lisp"),
9714            "diagnostic must name the expected extension: {rendered}",
9715        );
9716    }
9717
9718    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
9719    //
9720    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
9721    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
9722    // contract. The peer caixa-helm / caixa-flux renderers consume each
9723    // `:servicos` entry through `serde_yaml::from_str` as a typed
9724    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
9725    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
9726    // axis `Path::extension` can't express on its own.
9727
9728    #[test]
9729    fn validate_code_paths_rejects_no_extension_servicos_entry() {
9730        // Canonical "I dragged the wrong file from the workspace tree"
9731        // footgun on the Servico axis. Without the gate the peer
9732        // caixa-helm / caixa-flux renderers hand the extensionless path
9733        // to `serde_yaml::from_str` and fail with a parser-shaped
9734        // diagnostic far from the source caixa.lisp, with no field
9735        // naming the offending `:servicos` entry.
9736        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
9737            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9738            let err = c.validate_code_paths().unwrap_err();
9739            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9740                panic!(
9741                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9742                     got {err:?}"
9743                );
9744            };
9745            assert_eq!(slot, ":servicos");
9746            assert_eq!(path, PathBuf::from(relpath));
9747        }
9748    }
9749
9750    #[test]
9751    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
9752        // Wrong-extension sweep across common authoring footguns on the
9753        // Servico axis. Bare `.yaml` is the canonical "I forgot the
9754        // `.computeunit` segment" typo; the off-by-one-segment shapes
9755        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
9756        // bare `Path::extension` view but mismatch the typed compound
9757        // suffix the renderers' `serde_yaml::from_str` consumer demands.
9758        // Same sweep-posture as the peer
9759        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
9760        // (64772a9) on the sibling tatara-lisp-source axis.
9761        for relpath in [
9762            "servicos/demo.yaml",
9763            "servicos/demo.yml",
9764            "servicos/demo.json",
9765            "servicos/demo.toml",
9766            "servicos/demo.txt",
9767            "servicos/demo.computeunit.yaml.bak",
9768            "servicos/demo.computeunit.yam",
9769            "servicos/demo.computeunit",
9770            "servicos/demo-computeunit.yaml",
9771            "servicos/demo_computeunit.yaml",
9772        ] {
9773            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9774            let err = c.validate_code_paths().unwrap_err();
9775            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9776                panic!(
9777                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9778                     got {err:?}"
9779                );
9780            };
9781            assert_eq!(slot, ":servicos");
9782            assert_eq!(path, PathBuf::from(relpath));
9783        }
9784    }
9785
9786    #[test]
9787    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
9788        // Case-sensitivity sweep — pins the strict lowercase
9789        // `.computeunit.yaml` contract. A case-folded shape that the
9790        // layout's existence check would (case-insensitively, on
9791        // case-insensitive volumes) match the on-disk file still
9792        // mismatches the canonical form the codec emits, breaking the
9793        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
9794        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
9795        // (64772a9) sweep on the sibling tatara-lisp-source axis.
9796        for relpath in [
9797            "servicos/demo.ComputeUnit.yaml",
9798            "servicos/demo.COMPUTEUNIT.yaml",
9799            "servicos/demo.computeunit.YAML",
9800            "servicos/demo.computeunit.Yaml",
9801            "servicos/demo.COMPUTEUNIT.YAML",
9802        ] {
9803            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9804            let err = c.validate_code_paths().unwrap_err();
9805            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9806                panic!(
9807                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9808                     got {err:?}"
9809                );
9810            };
9811            assert_eq!(slot, ":servicos");
9812            assert_eq!(path, PathBuf::from(relpath));
9813        }
9814    }
9815
9816    #[test]
9817    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
9818        // Degenerate hidden-file shape: a file name exactly equal to the
9819        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
9820        // the structural "Servico declared with no identity" footgun.
9821        // The substrate identifies each ComputeUnit by the file-stem
9822        // segment that precedes `.computeunit.yaml` (the rendered
9823        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
9824        // the M3 `:contratos` membership lookup), so an empty stem
9825        // leaves the Servico unidentifiable. Pinned at the typed-axis
9826        // level so a future regression that drops the `name.len() >
9827        // SUFFIX.len()` bound at the predicate surfaces here, not
9828        // piecemeal as a `lareira-` chart-name collision at render time.
9829        for relpath in ["servicos/.computeunit.yaml"] {
9830            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9831            let err = c.validate_code_paths().unwrap_err();
9832            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9833                panic!(
9834                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
9835                     got {err:?}"
9836                );
9837            };
9838            assert_eq!(slot, ":servicos");
9839            assert_eq!(path, PathBuf::from(relpath));
9840        }
9841    }
9842
9843    #[test]
9844    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
9845        // Positive-control sweep through every canonical authoring shape
9846        // every in-tree fixture and the `Caixa::template` scaffold use.
9847        // Mirrors the peer
9848        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
9849        // and the lifted predicate's own
9850        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
9851        // render.rs.
9852        for relpath in [
9853            "servicos/demo.computeunit.yaml",
9854            "servicos/hello-rio.computeunit.yaml",
9855            "servicos/my-service.computeunit.yaml",
9856            "servicos/a.computeunit.yaml",
9857            "./servicos/demo.computeunit.yaml",
9858            "servicos/./demo.computeunit.yaml",
9859            "servicos/sub/nested.computeunit.yaml",
9860            "servicos/v0.1.computeunit.yaml",
9861        ] {
9862            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
9863            c.validate_code_paths()
9864                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
9865        }
9866    }
9867
9868    #[test]
9869    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
9870        // The file-type gate is per-slot — only `:servicos` carries the
9871        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
9872        // entry and an extensionless `:exe` entry are the canonical
9873        // shapes every in-tree fixture uses, and must continue to pass
9874        // validate. Peer of
9875        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
9876        // (64772a9) — together pin that the typed
9877        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
9878        // cross-axis leakage in either direction.
9879        let c = caixa_with_code_paths(
9880            vec!["lib/demo.lisp"],
9881            vec!["exe/demo", "exe/tool"],
9882            vec!["servicos/demo.computeunit.yaml"],
9883        );
9884        c.validate_code_paths().unwrap();
9885    }
9886
9887    #[test]
9888    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
9889        // Cross-arm precedence pin: a `:servicos` entry that is *both*
9890        // sandbox-escaping and wrong-extension surfaces the more
9891        // fundamental sandbox-shape diagnostic first (the
9892        // `.computeunit.yaml` remediation would be misleading when the
9893        // offending path can never resolve under the caixa root
9894        // anyway). Mirrors the peer
9895        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
9896        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
9897        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
9898        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
9899        // table establishes.
9900        //
9901        // Empty wins (the strictly-smaller-scope structural arm).
9902        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
9903        assert!(
9904            matches!(
9905                c.validate_code_paths().unwrap_err(),
9906                ManifestError::CodePathEmpty { slot: ":servicos" }
9907            ),
9908            "empty must win over non-computeunit-yaml-extension",
9909        );
9910        // Absolute wins (the path can't resolve under the caixa root).
9911        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
9912        let err = c.validate_code_paths().unwrap_err();
9913        let ManifestError::CodePathAbsolute { slot, .. } = err else {
9914            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
9915        };
9916        assert_eq!(slot, ":servicos");
9917        // ParentEscape wins (the path escapes the caixa root).
9918        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
9919        let err = c.validate_code_paths().unwrap_err();
9920        let ManifestError::CodePathParentEscape { slot, .. } = err else {
9921            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
9922        };
9923        assert_eq!(slot, ":servicos");
9924    }
9925
9926    #[test]
9927    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
9928        // Within-slot precedence pin: the per-entry file-type shape gate
9929        // fires before the cross-entry duplicate gate, so the narrower
9930        // structural defect dominates the uniqueness diagnostic. A
9931        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
9932        // `CodePathNonComputeUnitYamlExtension` on the first entry
9933        // rather than `CodePathDuplicate` on the pair — same posture
9934        // every per-entry shape-gate-precedes-duplicate cascade follows
9935        // on this surface, peer of the 64772a9 `:bibliotecas`
9936        // `("lib/x.txt" "lib/x.txt")` ordering.
9937        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
9938        let err = c.validate_code_paths().unwrap_err();
9939        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
9940            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
9941        };
9942        assert_eq!(slot, ":servicos");
9943        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
9944    }
9945
9946    #[test]
9947    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
9948     {
9949        // Diagnostic-shape pin (peer with
9950        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
9951        // on the sibling tatara-lisp-source axis): the file-type-arm
9952        // Display surfaces both the offending `:slot` tag, the
9953        // offending path verbatim, and the expected
9954        // `.computeunit.yaml` compound suffix named in the remediation
9955        // text, so a `feira lint` run can render the diagnostic without
9956        // re-parsing.
9957        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
9958        let rendered = c.validate_code_paths().unwrap_err().to_string();
9959        assert!(
9960            rendered.contains(":servicos"),
9961            "diagnostic must name the offending slot: {rendered}",
9962        );
9963        assert!(
9964            rendered.contains("servicos/demo.yaml"),
9965            "diagnostic must quote the offending path: {rendered}",
9966        );
9967        assert!(
9968            rendered.contains(".computeunit.yaml"),
9969            "diagnostic must name the expected compound suffix: {rendered}",
9970        );
9971    }
9972
9973    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
9974
9975    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
9976        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9977        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
9978        c
9979    }
9980
9981    #[test]
9982    fn validate_etiquetas_accepts_empty_list() {
9983        // The empty-list identity: every caixa with no declared tags
9984        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
9985        // so the gate is non-disruptive against every existing manifest.
9986        let c = caixa_with_etiquetas(vec![]);
9987        c.validate_etiquetas().unwrap();
9988    }
9989
9990    #[test]
9991    fn validate_etiquetas_accepts_canonical_forms() {
9992        // Positive control sweep: a canonical-shaped non-empty distinct
9993        // tag list passes, mirroring the example checkout-aplicacao
9994        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
9995        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
9996        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
9997        c.validate_etiquetas().unwrap();
9998    }
9999
10000    #[test]
10001    fn validate_etiquetas_rejects_empty_entry() {
10002        // Canonical paste-from-blank-doc footgun. Without the gate the
10003        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
10004        // no-op tag indexing nothing in the future caixa-registry.
10005        let c = caixa_with_etiquetas(vec![""]);
10006        let err = c.validate_etiquetas().unwrap_err();
10007        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10008    }
10009
10010    #[test]
10011    fn validate_etiquetas_rejects_duplicate_entry() {
10012        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
10013        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
10014        // collect at chart render — a "second wins / one silently
10015        // disappears" shape divergent from every peer typed-graph set
10016        // gate. The duplicate-arm names the offending tag verbatim.
10017        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
10018        let err = c.validate_etiquetas().unwrap_err();
10019        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
10020            panic!("expected EtiquetaDuplicate, got {err:?}");
10021        };
10022        assert_eq!(etiqueta, "demo");
10023    }
10024
10025    #[test]
10026    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
10027        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
10028        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
10029        // structural "this entry has no value" defect dominates the
10030        // cross-entry uniqueness diagnostic. Mirrors the peer
10031        // empty-before-duplicate cascades on `:caracteristicas`
10032        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
10033        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
10034        // `MembroDuplicate`).
10035        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
10036        let err = c.validate_etiquetas().unwrap_err();
10037        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10038    }
10039
10040    #[test]
10041    fn validate_etiquetas_duplicate_reports_first_collision() {
10042        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
10043        // duplicate (the lexicographically-earliest offending position
10044        // — the second `"a"` at index 2 collides with the first `"a"`
10045        // at index 0), not the later `"b"` collision at index 3,
10046        // peer with every other first-collision diagnostic posture on
10047        // this surface (`validate_load_singularity_reports_first_collision`,
10048        // `validate_cleanup_singularity_reports_first_collision`).
10049        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
10050        let err = c.validate_etiquetas().unwrap_err();
10051        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
10052            panic!("expected EtiquetaDuplicate, got {err:?}");
10053        };
10054        assert_eq!(etiqueta, "a");
10055    }
10056
10057    #[test]
10058    fn validate_etiquetas_case_sensitive() {
10059        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
10060        // mirroring the peer `:membros :caixa` / `:children :caixa`
10061        // exact-string-match discipline. The shape gate this routine
10062        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
10063        // grammar) accepts mixed case — crates.io's keyword rule is
10064        // "case-insensitive" at the index layer but admits mixed case
10065        // at the entry layer (the canonical Helm chart `keywords:`
10066        // shape is lowercase by convention, but the grammar admits
10067        // uppercase). Case-sensitivity at the duplicate-set layer
10068        // remains structural — two distinct strings are two distinct
10069        // entries.
10070        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
10071        c.validate_etiquetas().unwrap();
10072    }
10073
10074    #[test]
10075    fn validate_etiquetas_diagnostic_carries_offending_tag() {
10076        // Diagnostic-shape pin (peer with
10077        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
10078        // the error's Display surfaces the offending tag verbatim, so a
10079        // `feira lint` run can render the diagnostic without re-parsing
10080        // and the author can grep their caixa.lisp for the offending
10081        // value.
10082        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
10083        let rendered = c.validate_etiquetas().unwrap_err().to_string();
10084        assert!(
10085            rendered.contains(":etiquetas"),
10086            "diagnostic must name the offending slot: {rendered}",
10087        );
10088        assert!(
10089            rendered.contains("demo"),
10090            "diagnostic must quote the offending tag: {rendered}",
10091        );
10092    }
10093
10094    #[test]
10095    fn validate_etiquetas_rejects_leading_whitespace_entry() {
10096        // Canonical paste-from-aligned-doc footgun. Without the shape
10097        // gate `" mesh"` silently passed validate and landed as a
10098        // YAML plain-style scalar with leading whitespace in the
10099        // rendered Chart.yaml `keywords:` array — every YAML 1.2
10100        // dumper trims leading whitespace from plain-style scalars,
10101        // so the authored space round-tripped inconsistently back
10102        // through `caixa.lisp`. Mirrors the peer
10103        // `validate_autores_rejects_leading_whitespace_entry`.
10104        let c = caixa_with_etiquetas(vec![" mesh"]);
10105        let err = c.validate_etiquetas().unwrap_err();
10106        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10107            panic!("expected EtiquetaInvalid, got {err:?}");
10108        };
10109        assert_eq!(etiqueta, " mesh");
10110        assert!(reason.contains("whitespace"), "got: {reason}");
10111    }
10112
10113    #[test]
10114    fn validate_etiquetas_rejects_embedded_newline_entry() {
10115        // Canonical paste-from-multiline-doc footgun — the author
10116        // pasted a multi-tag block into one `:etiquetas` entry
10117        // instead of splitting into one entry per tag. Without the
10118        // shape gate `"mesh\nhttp"` silently passed validate and
10119        // landed as a YAML-illegal multi-line scalar in the rendered
10120        // Chart.yaml `keywords:` array.
10121        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
10122        let err = c.validate_etiquetas().unwrap_err();
10123        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10124            panic!("expected EtiquetaInvalid, got {err:?}");
10125        };
10126        assert_eq!(etiqueta, "mesh\nhttp");
10127        assert!(reason.contains("newline"), "got: {reason}");
10128    }
10129
10130    #[test]
10131    fn validate_etiquetas_rejects_embedded_comma_entry() {
10132        // Canonical CSV-list-separator-confusion footgun: the author
10133        // confused the CSV-style separator convention with the
10134        // `:etiquetas` list grammar. Without the shape gate
10135        // `"mesh,http,grpc"` silently passed validate and landed as a
10136        // single malformed search tag in the rendered Chart.yaml
10137        // `keywords:` array — Artifact Hub's keyword index would
10138        // either silently drop the tag or index it as
10139        // `mesh,http,grpc` instead of three separate tags.
10140        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
10141        let err = c.validate_etiquetas().unwrap_err();
10142        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10143            panic!("expected EtiquetaInvalid, got {err:?}");
10144        };
10145        assert_eq!(etiqueta, "mesh,http,grpc");
10146        assert!(reason.contains('`'), "got: {reason}");
10147        assert!(reason.contains(','), "got: {reason}");
10148    }
10149
10150    #[test]
10151    fn validate_etiquetas_rejects_embedded_slash_entry() {
10152        // Canonical path-separator-confusion footgun: the author
10153        // confused namespace-path notation with the keyword grammar.
10154        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
10155        let err = c.validate_etiquetas().unwrap_err();
10156        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10157            panic!("expected EtiquetaInvalid, got {err:?}");
10158        };
10159        assert_eq!(etiqueta, "caixa/servico");
10160        assert!(reason.contains('/'), "got: {reason}");
10161    }
10162
10163    #[test]
10164    fn validate_etiquetas_rejects_leading_digit_entry() {
10165        // Canonical paste-from-numbered-list footgun: the author
10166        // copied `1. mesh` from a numbered doc and the `1` leaked
10167        // into the tag.
10168        let c = caixa_with_etiquetas(vec!["1mesh"]);
10169        let err = c.validate_etiquetas().unwrap_err();
10170        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10171            panic!("expected EtiquetaInvalid, got {err:?}");
10172        };
10173        assert_eq!(etiqueta, "1mesh");
10174        assert!(reason.contains("digit"), "got: {reason}");
10175    }
10176
10177    #[test]
10178    fn validate_etiquetas_rejects_leading_hyphen_entry() {
10179        // Canonical kebab-leak footgun.
10180        let c = caixa_with_etiquetas(vec!["-foo"]);
10181        let err = c.validate_etiquetas().unwrap_err();
10182        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10183            panic!("expected EtiquetaInvalid, got {err:?}");
10184        };
10185        assert_eq!(etiqueta, "-foo");
10186        assert!(reason.contains('-'), "got: {reason}");
10187    }
10188
10189    #[test]
10190    fn validate_etiquetas_rejects_non_ascii_entry() {
10191        // Canonical paste-from-Unicode-doc footgun. Every legitimate
10192        // search tag is strict ASCII; raw non-ASCII silently
10193        // round-trips inconsistently across NFC/NFD normalization on
10194        // APFS / case-folding filesystems and breaks the Artifact Hub
10195        // keyword search index lookup.
10196        let c = caixa_with_etiquetas(vec!["café"]);
10197        let err = c.validate_etiquetas().unwrap_err();
10198        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10199            panic!("expected EtiquetaInvalid, got {err:?}");
10200        };
10201        assert_eq!(etiqueta, "café");
10202        assert!(reason.contains("non-ASCII"), "got: {reason}");
10203    }
10204
10205    #[test]
10206    fn validate_etiquetas_rejects_period_entry() {
10207        // Canonical namespace-confusion / version-suffix footgun
10208        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
10209        // excludes `.` from the continuation set even though the
10210        // sibling `:caracteristicas` axis (Cargo's feature-name
10211        // grammar) admits it. Tighter than the sibling axis, peer
10212        // with Cargo's own crates.io keyword shape.
10213        let c = caixa_with_etiquetas(vec!["http.1"]);
10214        let err = c.validate_etiquetas().unwrap_err();
10215        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10216            panic!("expected EtiquetaInvalid, got {err:?}");
10217        };
10218        assert_eq!(etiqueta, "http.1");
10219        assert!(reason.contains('.'), "got: {reason}");
10220    }
10221
10222    #[test]
10223    fn validate_etiquetas_empty_takes_precedence_over_shape() {
10224        // Per-entry empty-first cascade pin: an entry that is both
10225        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
10226        // narrower "this entry has no value" structural defect
10227        // dominates the broader shape-predicate diagnostic). The
10228        // empty arm fires before the shape predicate is consulted,
10229        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
10230        // cascade established on the sibling universal-axis Vec<String>
10231        // surface.
10232        let c = caixa_with_etiquetas(vec![""]);
10233        let err = c.validate_etiquetas().unwrap_err();
10234        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10235    }
10236
10237    #[test]
10238    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
10239        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
10240        // entry that is malformed surfaces `EtiquetaInvalid` even when
10241        // a later entry would have collided on duplicate. The
10242        // per-entry shape arm fires inside the same loop iteration as
10243        // the empty arm, before the seen-set insert at end-of-iteration
10244        // — structural per-entry defects dominate the cross-entry
10245        // uniqueness diagnostic. Mirrors the peer
10246        // `validate_autores_shape_takes_precedence_over_duplicate`.
10247        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
10248        let err = c.validate_etiquetas().unwrap_err();
10249        assert!(
10250            matches!(err, ManifestError::EtiquetaInvalid { .. }),
10251            "got {err:?}",
10252        );
10253    }
10254
10255    #[test]
10256    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
10257        // Diagnostic-shape pin on the new shape arm (peer with
10258        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
10259        // the rendered Display surfaces both the offending slot name
10260        // and the offending value verbatim, so a `feira lint` run
10261        // points the author at the exact `:etiquetas` entry to fix.
10262        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
10263        let rendered = c.validate_etiquetas().unwrap_err().to_string();
10264        assert!(
10265            rendered.contains(":etiquetas"),
10266            "diagnostic must name the offending slot: {rendered}",
10267        );
10268        assert!(
10269            rendered.contains("mesh\\nhttp"),
10270            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10271        );
10272    }
10273
10274    #[test]
10275    fn validate_etiquetas_rejects_at_21_byte_boundary() {
10276        // The 20-byte cap pin — boundary-exceeding case rejected,
10277        // boundary-accepting case passes. Mirrors the peer
10278        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
10279        // side pin, surfaced at the per-axis caller so the cap
10280        // propagates through validate end-to-end. Constructed as a
10281        // single all-`a` token so only the cap arm fires.
10282        let max_ok = "a".repeat(20);
10283        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
10284        c.validate_etiquetas().unwrap();
10285        let too_long = "a".repeat(21);
10286        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
10287        let err = c.validate_etiquetas().unwrap_err();
10288        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
10289            panic!("expected EtiquetaInvalid, got {err:?}");
10290        };
10291        assert!(reason.contains("20"), "got: {reason}");
10292        assert!(reason.contains("21"), "got: {reason}");
10293    }
10294
10295    #[test]
10296    fn validate_etiquetas_accepts_canonical_shaped_forms() {
10297        // Positive control sweep: every canonical-shaped tag from the
10298        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
10299        // example fixtures plus the substrate-fixed tags caixa-helm
10300        // unions in at chart render. Drift between this list and the
10301        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
10302        // sweep surfaces here — one source of truth for the rule.
10303        let c = caixa_with_etiquetas(vec![
10304            "example",
10305            "aplicacao",
10306            "mesh",
10307            "ecommerce",
10308            "demo",
10309            "infrastructure",
10310            "aws",
10311            "akeyless",
10312            "pangea-native",
10313            "hello-world",
10314            "wasm",
10315            "rust",
10316            "tatara-lisp",
10317            "caixa-servico",
10318            "lareira",
10319        ]);
10320        c.validate_etiquetas().unwrap();
10321    }
10322
10323    // ── validate_autores — universal-axis maintainer shape ────────────
10324
10325    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
10326        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10327        c.autores = autores.into_iter().map(String::from).collect();
10328        c
10329    }
10330
10331    #[test]
10332    fn validate_autores_accepts_empty_list() {
10333        // The empty-list identity: `Caixa::template` emits `:autores ()`,
10334        // so the gate is non-disruptive against every existing manifest.
10335        let c = caixa_with_autores(vec![]);
10336        c.validate_autores().unwrap();
10337    }
10338
10339    #[test]
10340    fn validate_autores_accepts_canonical_forms() {
10341        // Positive control sweep: every canonical-shaped non-empty
10342        // distinct maintainer list passes — the hello-rio / checkout-
10343        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
10344        // multi-author shape downstream packaging surfaces emit.
10345        let c = caixa_with_autores(vec!["pleme-io"]);
10346        c.validate_autores().unwrap();
10347        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
10348        c.validate_autores().unwrap();
10349    }
10350
10351    #[test]
10352    fn validate_autores_rejects_empty_entry() {
10353        // Canonical paste-from-blank-doc footgun. Without the gate the
10354        // empty entry rendered as `maintainers: [{name: "", email: null}]`
10355        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
10356        // to.
10357        let c = caixa_with_autores(vec![""]);
10358        let err = c.validate_autores().unwrap_err();
10359        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
10360    }
10361
10362    #[test]
10363    fn validate_autores_rejects_duplicate_entry() {
10364        // Canonical copy-paste-the-wrong-author footgun. Unlike the
10365        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
10366        // dedups the rendered `keywords:` array), the `maintainers:`
10367        // rendering has *no* dedup — duplicates stack verbatim. The
10368        // duplicate-arm names the offending author verbatim.
10369        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
10370        let err = c.validate_autores().unwrap_err();
10371        let ManifestError::AutorDuplicate { autor } = err else {
10372            panic!("expected AutorDuplicate, got {err:?}");
10373        };
10374        assert_eq!(autor, "pleme-io");
10375    }
10376
10377    #[test]
10378    fn validate_autores_empty_takes_precedence_over_duplicate() {
10379        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
10380        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
10381        // "this entry has no value" defect dominates the cross-entry
10382        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
10383        // cascades on `:etiquetas` (`EtiquetaEmpty` before
10384        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
10385        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
10386        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
10387        // `MembroDuplicate`).
10388        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
10389        let err = c.validate_autores().unwrap_err();
10390        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
10391    }
10392
10393    #[test]
10394    fn validate_autores_duplicate_reports_first_collision() {
10395        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
10396        // duplicate (the lexicographically-earliest offending position
10397        // — the second `"a"` at index 2 collides with the first `"a"`
10398        // at index 0), not the later `"b"` collision at index 3,
10399        // peer with every other first-collision diagnostic posture on
10400        // this surface.
10401        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
10402        let err = c.validate_autores().unwrap_err();
10403        let ManifestError::AutorDuplicate { autor } = err else {
10404            panic!("expected AutorDuplicate, got {err:?}");
10405        };
10406        assert_eq!(autor, "a");
10407    }
10408
10409    #[test]
10410    fn validate_autores_case_sensitive() {
10411        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
10412        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
10413        // / `:children :caixa` exact-string-match discipline.
10414        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
10415        c.validate_autores().unwrap();
10416    }
10417
10418    #[test]
10419    fn validate_autores_diagnostic_carries_offending_author() {
10420        // Diagnostic-shape pin (peer with
10421        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
10422        // error's Display surfaces the offending author verbatim, so a
10423        // `feira lint` run can render the diagnostic without re-parsing
10424        // and the author can grep their caixa.lisp for the offending
10425        // value.
10426        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
10427        let rendered = c.validate_autores().unwrap_err().to_string();
10428        assert!(
10429            rendered.contains(":autores"),
10430            "diagnostic must name the offending slot: {rendered}",
10431        );
10432        assert!(
10433            rendered.contains("pleme-io"),
10434            "diagnostic must quote the offending author: {rendered}",
10435        );
10436    }
10437
10438    #[test]
10439    fn validate_autores_rejects_leading_whitespace_entry() {
10440        // Canonical paste-from-aligned-doc footgun. Without the shape
10441        // gate `" pleme-io"` silently passed validate and landed as a
10442        // YAML plain-style scalar with leading whitespace in the
10443        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
10444        // dumper trims leading whitespace from plain-style scalars, so
10445        // the authored space round-tripped inconsistently back through
10446        // `caixa.lisp`. Mirrors the peer
10447        // `validate_descricao_rejects_leading_whitespace`.
10448        let c = caixa_with_autores(vec![" pleme-io"]);
10449        let err = c.validate_autores().unwrap_err();
10450        let ManifestError::AutorInvalid { autor, reason } = err else {
10451            panic!("expected AutorInvalid, got {err:?}");
10452        };
10453        assert_eq!(autor, " pleme-io");
10454        assert!(reason.contains("whitespace"), "got: {reason}");
10455    }
10456
10457    #[test]
10458    fn validate_autores_rejects_trailing_whitespace_entry() {
10459        // Canonical paste-from-doc footgun.
10460        let c = caixa_with_autores(vec!["pleme-io "]);
10461        let err = c.validate_autores().unwrap_err();
10462        let ManifestError::AutorInvalid { autor, reason } = err else {
10463            panic!("expected AutorInvalid, got {err:?}");
10464        };
10465        assert_eq!(autor, "pleme-io ");
10466        assert!(reason.contains("whitespace"), "got: {reason}");
10467    }
10468
10469    #[test]
10470    fn validate_autores_rejects_embedded_newline_entry() {
10471        // Canonical paste-from-multiline-doc footgun — the author
10472        // pasted a multi-line block of author records into one
10473        // `:autores` entry instead of splitting into one entry per
10474        // author. Without the shape gate `"alice\nbob"` silently
10475        // passed validate and landed as a YAML-illegal multi-line
10476        // scalar in the rendered Chart.yaml `maintainers:` array.
10477        let c = caixa_with_autores(vec!["alice\nbob"]);
10478        let err = c.validate_autores().unwrap_err();
10479        let ManifestError::AutorInvalid { autor, reason } = err else {
10480            panic!("expected AutorInvalid, got {err:?}");
10481        };
10482        assert_eq!(autor, "alice\nbob");
10483        assert!(reason.contains("newline"), "got: {reason}");
10484    }
10485
10486    #[test]
10487    fn validate_autores_rejects_embedded_carriage_return_entry() {
10488        // Canonical paste-from-Windows-CRLF-doc footgun.
10489        let c = caixa_with_autores(vec!["alice\rbob"]);
10490        let err = c.validate_autores().unwrap_err();
10491        let ManifestError::AutorInvalid { autor, reason } = err else {
10492            panic!("expected AutorInvalid, got {err:?}");
10493        };
10494        assert_eq!(autor, "alice\rbob");
10495        assert!(reason.contains("carriage return"), "got: {reason}");
10496    }
10497
10498    #[test]
10499    fn validate_autores_rejects_embedded_tab_entry() {
10500        // Canonical tab-from-aligned-doc footgun.
10501        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
10502        let err = c.validate_autores().unwrap_err();
10503        let ManifestError::AutorInvalid { autor, reason } = err else {
10504            panic!("expected AutorInvalid, got {err:?}");
10505        };
10506        assert_eq!(autor, "Pleme\tContributors");
10507        assert!(reason.contains("tab"), "got: {reason}");
10508    }
10509
10510    #[test]
10511    fn validate_autores_rejects_embedded_control_bytes_entry() {
10512        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
10513        // surface the same control-byte arm.
10514        for entry in [
10515            "alice\x00bob",
10516            "alice\x07bob",
10517            "alice\x1bbob",
10518            "alice\x7fbob",
10519        ] {
10520            let c = caixa_with_autores(vec![entry]);
10521            let err = c.validate_autores().unwrap_err();
10522            let ManifestError::AutorInvalid { autor, reason } = err else {
10523                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
10524            };
10525            assert_eq!(autor, entry);
10526            assert!(
10527                reason.contains("control character"),
10528                "{entry:?} reason: {reason}",
10529            );
10530        }
10531    }
10532
10533    #[test]
10534    fn validate_autores_accepts_unicode_entry() {
10535        // Unicode positive control: realistic maintainer names carry
10536        // Unicode (`François`, `日本語`, `naïve`). The predicate must
10537        // round-trip Unicode losslessly, peer with the
10538        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
10539        // sweep.
10540        let c = caixa_with_autores(vec![
10541            "François Dupont",
10542            "日本語の名前",
10543            "naïve <naive@example.com>",
10544        ]);
10545        c.validate_autores().unwrap();
10546    }
10547
10548    #[test]
10549    fn validate_autores_empty_takes_precedence_over_shape() {
10550        // Per-entry empty-first cascade pin: an entry that is both
10551        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
10552        // "this entry has no value" structural defect dominates the
10553        // broader shape-predicate diagnostic). The empty arm fires
10554        // before the shape predicate is consulted, mirroring the peer
10555        // `validate_repositorio_empty_takes_precedence_over_shape`
10556        // cascade on the universal `Option<String>` siblings — and now
10557        // established on the Vec<String> per-entry surface.
10558        let c = caixa_with_autores(vec![""]);
10559        let err = c.validate_autores().unwrap_err();
10560        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
10561    }
10562
10563    #[test]
10564    fn validate_autores_shape_takes_precedence_over_duplicate() {
10565        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
10566        // entry that is malformed surfaces `AutorInvalid` even when a
10567        // later entry would have collided on duplicate. The per-entry
10568        // shape arm fires inside the same loop iteration as the empty
10569        // arm, before the seen-set insert at end-of-iteration —
10570        // structural per-entry defects dominate the cross-entry
10571        // uniqueness diagnostic.
10572        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
10573        let err = c.validate_autores().unwrap_err();
10574        assert!(
10575            matches!(err, ManifestError::AutorInvalid { .. }),
10576            "got {err:?}",
10577        );
10578    }
10579
10580    #[test]
10581    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
10582        // Diagnostic-shape pin on the new shape arm (peer with
10583        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
10584        // the rendered Display surfaces both the offending slot name
10585        // and the offending value verbatim, so a `feira lint` run
10586        // points the author at the exact `:autores` entry to fix.
10587        let c = caixa_with_autores(vec!["alice\nbob"]);
10588        let rendered = c.validate_autores().unwrap_err().to_string();
10589        assert!(
10590            rendered.contains(":autores"),
10591            "diagnostic must name the offending slot: {rendered}",
10592        );
10593        assert!(
10594            rendered.contains("alice\\nbob"),
10595            "diagnostic must quote the offending value (debug-escaped): {rendered}",
10596        );
10597    }
10598
10599    #[test]
10600    fn validate_autores_rejects_at_129_byte_boundary() {
10601        // The 128-byte cap pin — boundary-exceeding case rejected,
10602        // boundary-accepting case passes. Mirrors the peer
10603        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
10604        // substrate-side pin, surfaced at the per-axis caller so the
10605        // cap propagates through validate end-to-end. Constructed as
10606        // a single all-`a` token so only the cap arm fires.
10607        let max_ok = "a".repeat(128);
10608        let c = caixa_with_autores(vec![max_ok.as_str()]);
10609        c.validate_autores().unwrap();
10610        let too_long = "a".repeat(129);
10611        let c = caixa_with_autores(vec![too_long.as_str()]);
10612        let err = c.validate_autores().unwrap_err();
10613        let ManifestError::AutorInvalid { reason, .. } = err else {
10614            panic!("expected AutorInvalid, got {err:?}");
10615        };
10616        assert!(reason.contains("128"), "got: {reason}");
10617        assert!(reason.contains("129"), "got: {reason}");
10618    }
10619
10620    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
10621
10622    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
10623        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10624        c.repositorio = repositorio.map(String::from);
10625        c
10626    }
10627
10628    #[test]
10629    fn validate_repositorio_accepts_none() {
10630        // The omit-the-slot identity: `:repositorio` is optional. The
10631        // gate is a no-op when the author didn't declare a value —
10632        // every caixa without a `:repositorio` line trivially passes,
10633        // and the substrate-side renderers fall back to their
10634        // documented placeholder (`caixa-helm`'s `home: None`,
10635        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
10636        // URL). Mirrors the peer `validate_restart_window_accepts_none`
10637        // posture on the other `Option<String>` Caixa slot.
10638        let c = caixa_with_repositorio(None);
10639        c.validate_repositorio().unwrap();
10640    }
10641
10642    #[test]
10643    fn validate_repositorio_accepts_canonical_forms() {
10644        // Positive control sweep across every documented `:repositorio`
10645        // authoring shape — the same union the shared
10646        // `crate::render::is_git_repo_url` predicate accepts and the
10647        // peer `:deps :fonte :repo` axis already routes through.
10648        // Covers the `github:` shorthand (the canonical pleme-io
10649        // convention used in the `:repositorio` field of every
10650        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
10651        // `examples/`), the `https://…` URL the README quickstart uses,
10652        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
10653        // `file://` URL schemes the shared predicate documents.
10654        for repo in [
10655            "github:pleme-io/hello-rio",
10656            "github:pleme-io/checkout",
10657            "https://github.com/pleme-io/hello-rio",
10658            "ssh://git@github.com/pleme-io/hello-rio.git",
10659            "git://github.com/pleme-io/hello-rio.git",
10660            "git@github.com:pleme-io/hello-rio.git",
10661            "file:///srv/pleme/hello-rio",
10662        ] {
10663            let c = caixa_with_repositorio(Some(repo));
10664            c.validate_repositorio()
10665                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
10666        }
10667    }
10668
10669    #[test]
10670    fn validate_repositorio_rejects_empty_some() {
10671        // Canonical paste-from-blank-doc footgun. The narrower
10672        // [`ManifestError::RepositorioEmpty`] arm fires before the
10673        // shape predicate is consulted, mirroring the empty-first
10674        // cascade every peer per-axis identity gate uses
10675        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
10676        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
10677        // the empty `Some("")` silently passed the renderer's
10678        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
10679        // on `None`) and landed as `home: ""` in `Chart.yaml` /
10680        // `url: ""` in the FluxCD `GitRepository`.
10681        let c = caixa_with_repositorio(Some(""));
10682        let err = c.validate_repositorio().unwrap_err();
10683        assert!(
10684            matches!(err, ManifestError::RepositorioEmpty),
10685            "got {err:?}",
10686        );
10687    }
10688
10689    #[test]
10690    fn validate_repositorio_rejects_whitespace() {
10691        // Paste-from-doc whitespace footgun. The shared
10692        // `is_git_repo_url` predicate refuses any whitespace byte; a
10693        // trailing space in a `:repositorio` value silently broke
10694        // `git clone '<value> '` at clone time. The diagnostic names
10695        // the offending value verbatim.
10696        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
10697        let err = c.validate_repositorio().unwrap_err();
10698        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
10699            panic!("expected RepositorioInvalid, got {err:?}");
10700        };
10701        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
10702    }
10703
10704    #[test]
10705    fn validate_repositorio_rejects_control_char() {
10706        // Paste-from-multiline-doc CRLF footgun — control characters
10707        // at the URL boundary are a class of subprocess-arg injection
10708        // and break git's URL parser at every porcelain entry point.
10709        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
10710        let err = c.validate_repositorio().unwrap_err();
10711        assert!(
10712            matches!(err, ManifestError::RepositorioInvalid { .. }),
10713            "got {err:?}",
10714        );
10715    }
10716
10717    #[test]
10718    fn validate_repositorio_rejects_leading_dash() {
10719        // Canonical CLI-argument-injection footgun: `git clone <repo>`
10720        // interprets a leading `-` as a CLI flag, so a
10721        // `-upload-pack=…` value escapes the subprocess argument
10722        // boundary. The shared predicate refuses every leading-`-`
10723        // shape at validate time.
10724        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
10725        let err = c.validate_repositorio().unwrap_err();
10726        assert!(
10727            matches!(err, ManifestError::RepositorioInvalid { .. }),
10728            "got {err:?}",
10729        );
10730    }
10731
10732    #[test]
10733    fn validate_repositorio_rejects_missing_colon_separator() {
10734        // The bare `org/repo` ambiguity footgun — `git clone` reads
10735        // a no-`:` form as a relative filesystem path rather than the
10736        // GitHub-shorthand expansion the author probably intended.
10737        // The shared predicate refuses every shape without a `:`
10738        // separator.
10739        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
10740        let err = c.validate_repositorio().unwrap_err();
10741        assert!(
10742            matches!(err, ManifestError::RepositorioInvalid { .. }),
10743            "got {err:?}",
10744        );
10745    }
10746
10747    #[test]
10748    fn validate_repositorio_rejects_fragment_anchor() {
10749        // Paste-from-browser-address-bar footgun on the
10750        // `:repositorio` axis — an author copies a GitHub permalink
10751        // to a README section / line-permalink and forgets to trim
10752        // the `#fragment` tail. The shared `is_git_repo_url`
10753        // predicate refuses the byte at the URL-grammar layer
10754        // (libcurl strips the fragment before opening the
10755        // transport, so the byte rides verbatim into the rendered
10756        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
10757        // fields but is silently dropped on the wire — two
10758        // manifest variants whose values differ only in their
10759        // fragment anchor lock to two distinct rendered artifacts
10760        // for the byte-identical clone, defeating the THEORY.md
10761        // §V.2 render-determinism contract on the `:repositorio`
10762        // axis the peer `:fonte :repo` axis already closes).
10763        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
10764        let err = c.validate_repositorio().unwrap_err();
10765        let ManifestError::RepositorioInvalid {
10766            repositorio,
10767            reason,
10768        } = err
10769        else {
10770            panic!("expected RepositorioInvalid, got {err:?}");
10771        };
10772        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
10773        assert!(
10774            reason.contains("must not contain `#`"),
10775            "reason must surface the fragment-`#` arm, got {reason:?}"
10776        );
10777    }
10778
10779    #[test]
10780    fn validate_repositorio_rejects_query_string() {
10781        // Paste-from-browser-address-bar footgun on the
10782        // `:repositorio` axis (peer with the a68f818 fragment-`#`
10783        // arm on the same axis). An author copies a GitHub tab
10784        // deep-link out of the address bar and forgets to trim
10785        // the `?tab=…` query tail. The shared `is_git_repo_url`
10786        // predicate refuses the byte at the URL-grammar layer
10787        // (GitHub / GitLab / Bitbucket silently ignore the
10788        // `?query` tail and serve the same repo regardless, so
10789        // the byte rides verbatim into the rendered `Chart.yaml`
10790        // `home:` and FluxCD `GitRepository` `url:` fields but
10791        // is silently masked at the wire — two manifest variants
10792        // whose values differ only in their query tail lock to
10793        // two distinct rendered artifacts for the byte-identical
10794        // clone, defeating the THEORY.md §V.2 render-determinism
10795        // contract on the `:repositorio` axis the peer `:fonte
10796        // :repo` axis already closes).
10797        let c = caixa_with_repositorio(Some(
10798            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
10799        ));
10800        let err = c.validate_repositorio().unwrap_err();
10801        let ManifestError::RepositorioInvalid {
10802            repositorio,
10803            reason,
10804        } = err
10805        else {
10806            panic!("expected RepositorioInvalid, got {err:?}");
10807        };
10808        assert_eq!(
10809            repositorio,
10810            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
10811        );
10812        assert!(
10813            reason.contains("must not contain `?`"),
10814            "reason must surface the query-`?` arm, got {reason:?}"
10815        );
10816    }
10817
10818    #[test]
10819    fn validate_repositorio_rejects_embedded_backslash() {
10820        // Windows-file-path-confusion footgun on the `:repositorio`
10821        // axis (peer with the prior fragment-`#` / query-`?` arms on
10822        // the same axis, and peer with the new dep-level `:fonte :repo`
10823        // backslash arm on the URL-grammar trajectory). An author
10824        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
10825        // hello-rio` into the `:repositorio` slot, expecting the
10826        // `lareira-<nome>` chart's `home:` field and the FluxCD
10827        // `GitRepository` `url:` field to render the canonical local
10828        // file-URI. The shared `is_git_repo_url` predicate refuses
10829        // the byte at the URL-grammar layer (libcurl silently
10830        // translates `\` → `/` on some platforms and refuses it on
10831        // others, so the byte rides verbatim into the rendered
10832        // artifacts but is silently rewritten or rejected at the wire
10833        // — two manifest variants whose values differ only in
10834        // backslash-vs-forward-slash lock to two distinct rendered
10835        // artifacts for the byte-identical clone, defeating the
10836        // THEORY.md §V.2 render-determinism contract on the
10837        // `:repositorio` axis the peer `:fonte :repo` axis already
10838        // closes).
10839        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
10840        let err = c.validate_repositorio().unwrap_err();
10841        let ManifestError::RepositorioInvalid {
10842            repositorio,
10843            reason,
10844        } = err
10845        else {
10846            panic!("expected RepositorioInvalid, got {err:?}");
10847        };
10848        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
10849        assert!(
10850            reason.contains("must not contain `\\`"),
10851            "reason must surface the backslash-`\\` arm, got {reason:?}"
10852        );
10853    }
10854
10855    #[test]
10856    fn validate_repositorio_rejects_uri_template_placeholder() {
10857        // URI Template (RFC 6570) placeholder footgun on the
10858        // `:repositorio` axis (peer with the prior fragment-`#` /
10859        // query-`?` / backslash-`\` arms on the same axis, and peer
10860        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
10861        // URL-grammar trajectory). An author pastes a quick-start
10862        // README snippet / OpenAPI `servers:` URL / Helm chart
10863        // `home:` template carrying unresolved `{org}` / `{repo}`
10864        // placeholders into the `:repositorio` slot, expecting the
10865        // substrate to resolve the placeholder downstream. The
10866        // shared `is_git_repo_url` predicate refuses the byte at the
10867        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
10868        // `%7B` / `%7D` on the wire, so the byte round-trips
10869        // inconsistently between the rendered `Chart.yaml home:` /
10870        // FluxCD `GitRepository url:` and the resolver's `git clone`
10871        // invocation, defeating the THEORY.md §V.2 render-
10872        // determinism contract on the `:repositorio` axis the peer
10873        // `:fonte :repo` axis already closes; every git porcelain
10874        // entry-point additionally fetches a nonexistent literal-
10875        // `{placeholder}`-named path far from the source caixa.lisp).
10876        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
10877        let err = c.validate_repositorio().unwrap_err();
10878        let ManifestError::RepositorioInvalid {
10879            repositorio,
10880            reason,
10881        } = err
10882        else {
10883            panic!("expected RepositorioInvalid, got {err:?}");
10884        };
10885        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
10886        assert!(
10887            reason.contains("must not contain `{`"),
10888            "reason must surface the open-brace `{{` arm, got {reason:?}"
10889        );
10890        assert!(
10891            reason.contains("URI Template") || reason.contains("RFC 6570"),
10892            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
10893        );
10894    }
10895
10896    #[test]
10897    fn validate_repositorio_empty_takes_precedence_over_shape() {
10898        // Empty-first cascade pin: the empty `Some("")` surfaces the
10899        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
10900        // `RepositorioInvalid`, mirroring the peer
10901        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
10902        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
10903        // `is_git_repo_url` predicate also rejects the empty input
10904        // (defensively, with its own `"must not be empty"` reason),
10905        // but the manifest-layer empty arm runs first to surface the
10906        // narrower diagnostic verbatim.
10907        let c = caixa_with_repositorio(Some(""));
10908        let err = c.validate_repositorio().unwrap_err();
10909        assert!(
10910            matches!(err, ManifestError::RepositorioEmpty),
10911            "got {err:?}",
10912        );
10913    }
10914
10915    #[test]
10916    fn validate_repositorio_diagnostic_carries_offending_value() {
10917        // Diagnostic-shape pin (peer with
10918        // `validate_autores_diagnostic_carries_offending_author`): the
10919        // error's Display surfaces the offending value + slot name
10920        // verbatim, so a `feira lint` run can render the diagnostic
10921        // without re-parsing and the author can grep their caixa.lisp
10922        // for the offending `:repositorio` value.
10923        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
10924        let rendered = c.validate_repositorio().unwrap_err().to_string();
10925        assert!(
10926            rendered.contains(":repositorio"),
10927            "diagnostic must name the offending slot: {rendered}",
10928        );
10929        assert!(
10930            rendered.contains("pleme-io/hello-rio"),
10931            "diagnostic must quote the offending value: {rendered}",
10932        );
10933    }
10934
10935    // ── validate_descricao — universal-axis Chart.yaml description shape ──
10936
10937    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
10938        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10939        c.descricao = descricao.map(String::from);
10940        c
10941    }
10942
10943    #[test]
10944    fn validate_descricao_accepts_none() {
10945        // The omit-the-slot identity: `:descricao` is optional. The
10946        // gate is a no-op when the author didn't declare a value —
10947        // every caixa without a `:descricao` line trivially passes,
10948        // and the substrate-side renderers fall back to their
10949        // documented `caixa.nome`-derived placeholder. Mirrors the
10950        // peer `validate_repositorio_accepts_none` posture on the
10951        // sibling `Option<String>` Caixa slot.
10952        let c = caixa_with_descricao(None);
10953        c.validate_descricao().unwrap();
10954    }
10955
10956    #[test]
10957    fn validate_descricao_accepts_canonical_summary() {
10958        // Positive control: the canonical pleme-io descricao shape —
10959        // a short free-form prose summary — passes the gate. Covers
10960        // the fixture shapes the `caixa-helm` / `caixa-flux` /
10961        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
10962        // wasip2 caixa Servico."`, `"Checkout flow."`).
10963        for desc in [
10964            "Canonical Rust→wasm32-wasip2 caixa Servico.",
10965            "Checkout flow.",
10966            "AWS provider caixa for tatara-lisp",
10967            "FIXME — describe this caixa",
10968            "x",
10969        ] {
10970            let c = caixa_with_descricao(Some(desc));
10971            c.validate_descricao()
10972                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
10973        }
10974    }
10975
10976    #[test]
10977    fn validate_descricao_rejects_empty_some() {
10978        // Canonical paste-from-blank-doc footgun. Without this gate
10979        // the empty `Some("")` silently passed the renderer's
10980        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
10981        // on `None`) and landed as `description: ""` in `Chart.yaml`
10982        // and a blank `README.md` header. Mirrors the peer
10983        // [`ManifestError::RepositorioEmpty`] empty-arm on the
10984        // sibling `Option<String>` Caixa slot.
10985        let c = caixa_with_descricao(Some(""));
10986        let err = c.validate_descricao().unwrap_err();
10987        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
10988    }
10989
10990    #[test]
10991    fn validate_descricao_rejects_leading_whitespace() {
10992        // Paste-from-aligned-doc footgun: a leading ASCII space the
10993        // bare empty-arm gate accepted, the shape predicate now
10994        // refuses. The diagnostic carries the offending value
10995        // verbatim (with the leading space preserved) so the author
10996        // can grep their caixa.lisp for the exact `:descricao` line
10997        // and fix the round-trip-inconsistent leading whitespace.
10998        // Mirrors the peer
10999        // `validate_licenca_rejects_leading_whitespace` arm on the
11000        // sibling `:licenca` axis.
11001        let c = caixa_with_descricao(Some(" Checkout flow."));
11002        let err = c.validate_descricao().unwrap_err();
11003        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
11004            panic!("expected DescricaoInvalid, got {err:?}");
11005        };
11006        assert_eq!(descricao, " Checkout flow.");
11007        assert!(reason.contains("whitespace"), "got: {reason:?}");
11008    }
11009
11010    #[test]
11011    fn validate_descricao_rejects_trailing_whitespace() {
11012        // Paste-from-doc footgun: a trailing ASCII space the bare
11013        // empty-arm gate accepted, the shape predicate now refuses.
11014        let c = caixa_with_descricao(Some("Checkout flow. "));
11015        let err = c.validate_descricao().unwrap_err();
11016        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
11017            panic!("expected DescricaoInvalid, got {err:?}");
11018        };
11019        assert_eq!(descricao, "Checkout flow. ");
11020        assert!(reason.contains("whitespace"), "got: {reason:?}");
11021    }
11022
11023    #[test]
11024    fn validate_descricao_rejects_embedded_newline() {
11025        // Paste-from-multiline-doc footgun: an embedded LF the bare
11026        // empty-arm gate accepted, the shape predicate now refuses.
11027        // Without this gate the embedded newline silently landed in
11028        // the rendered Chart.yaml as a multi-line YAML block scalar,
11029        // and every chart-aware UI (`helm list`, `helm search`,
11030        // Artifact Hub) renders the description in a single-line
11031        // column so the embedded newline is silently dropped at
11032        // every downstream consumer.
11033        let c = caixa_with_descricao(Some("Checkout\nflow."));
11034        let err = c.validate_descricao().unwrap_err();
11035        assert!(
11036            matches!(err, ManifestError::DescricaoInvalid { .. }),
11037            "got {err:?}",
11038        );
11039        assert!(err.to_string().contains("newline"), "got {err}");
11040    }
11041
11042    #[test]
11043    fn validate_descricao_rejects_embedded_carriage_return() {
11044        // Paste-from-Windows-CRLF-doc footgun.
11045        let c = caixa_with_descricao(Some("Checkout\rflow."));
11046        let err = c.validate_descricao().unwrap_err();
11047        assert!(
11048            matches!(err, ManifestError::DescricaoInvalid { .. }),
11049            "got {err:?}",
11050        );
11051        assert!(err.to_string().contains("carriage return"), "got {err}");
11052    }
11053
11054    #[test]
11055    fn validate_descricao_rejects_embedded_tab() {
11056        // Tab-from-aligned-doc footgun.
11057        let c = caixa_with_descricao(Some("Checkout\tflow."));
11058        let err = c.validate_descricao().unwrap_err();
11059        assert!(
11060            matches!(err, ManifestError::DescricaoInvalid { .. }),
11061            "got {err:?}",
11062        );
11063        assert!(err.to_string().contains("tab"), "got {err}");
11064    }
11065
11066    #[test]
11067    fn validate_descricao_rejects_embedded_control_bytes() {
11068        // Paste-from-binary-blob footgun: every other control byte
11069        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
11070        // the peer SPDX-expression control-byte arm.
11071        for s in [
11072            "Checkout\x00flow.",
11073            "Checkout\x07flow.",
11074            "Checkout\x1bflow.",
11075            "Checkout\x7fflow.",
11076        ] {
11077            let c = caixa_with_descricao(Some(s));
11078            let err = c.validate_descricao().unwrap_err();
11079            assert!(
11080                matches!(err, ManifestError::DescricaoInvalid { .. }),
11081                "{s:?} got {err:?}",
11082            );
11083            assert!(
11084                err.to_string().contains("control character"),
11085                "{s:?} got {err}",
11086            );
11087        }
11088    }
11089
11090    #[test]
11091    fn validate_descricao_accepts_unicode_prose() {
11092        // Positive control: Unicode prose is accepted — the
11093        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
11094        // and `Caixa::template`'s `"FIXME — describe this caixa"`
11095        // scaffold every `feira init` emits must continue to pass.
11096        for s in [
11097            "Canonical Rust→wasm32-wasip2 caixa Servico.",
11098            "FIXME — describe this caixa",
11099            "Caixa pour le projet tâche",
11100            "日本語の説明",
11101        ] {
11102            let c = caixa_with_descricao(Some(s));
11103            c.validate_descricao()
11104                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
11105        }
11106    }
11107
11108    #[test]
11109    fn validate_descricao_empty_takes_precedence_over_shape() {
11110        // Cascade pin: a `Some("")` surfaces the narrower
11111        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
11112        // shape-predicate arm. Mirrors the peer
11113        // `validate_licenca_empty_takes_precedence_over_shape` pin
11114        // on the sibling `:licenca` axis.
11115        let c = caixa_with_descricao(Some(""));
11116        let err = c.validate_descricao().unwrap_err();
11117        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
11118    }
11119
11120    #[test]
11121    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
11122        // Diagnostic-shape pin: the error's Display surfaces both
11123        // the `:descricao` slot name and the offending value
11124        // verbatim, so a `feira lint` run can render the diagnostic
11125        // without re-parsing and the author can grep their caixa.lisp
11126        // for the offending `:descricao` line. Mirrors the peer
11127        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
11128        // pin (ee2e888) on the sibling `:licenca` axis.
11129        // The `{descricao:?}` Debug format escapes embedded control
11130        // bytes; the quoted offending value surfaces as
11131        // `"Checkout\nflow."` (literal backslash-n) in the rendered
11132        // diagnostic. The author can grep their caixa.lisp for the
11133        // literal `Checkout` summary prefix.
11134        let c = caixa_with_descricao(Some("Checkout\nflow."));
11135        let rendered = c.validate_descricao().unwrap_err().to_string();
11136        assert!(
11137            rendered.contains(":descricao"),
11138            "diagnostic must name the offending slot: {rendered}",
11139        );
11140        assert!(
11141            rendered.contains("Checkout\\nflow."),
11142            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11143        );
11144    }
11145
11146    #[test]
11147    fn validate_descricao_template_passes() {
11148        // Round-trip pin: the bare `Caixa::template` shape carries
11149        // `:descricao "FIXME — describe this caixa"` (a non-empty
11150        // sentinel), so the template-derived Caixa passes the gate by
11151        // construction. A future template-shape change that omits or
11152        // empties `:descricao` would surface here as a regression.
11153        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11154        c.validate_descricao().unwrap();
11155    }
11156
11157    #[test]
11158    fn validate_descricao_diagnostic_names_offending_slot() {
11159        // Diagnostic-shape pin (peer with
11160        // `validate_repositorio_diagnostic_carries_offending_value`):
11161        // the error's Display surfaces the `:descricao` slot name
11162        // verbatim, so a `feira lint` run can render the diagnostic
11163        // without re-parsing and the author can grep their caixa.lisp
11164        // for the offending `:descricao` line.
11165        let c = caixa_with_descricao(Some(""));
11166        let rendered = c.validate_descricao().unwrap_err().to_string();
11167        assert!(
11168            rendered.contains(":descricao"),
11169            "diagnostic must name the offending slot: {rendered}",
11170        );
11171    }
11172
11173    // ── validate_licenca — universal-axis chart README license shape ──
11174
11175    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
11176        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11177        c.licenca = licenca.map(String::from);
11178        c
11179    }
11180
11181    #[test]
11182    fn validate_licenca_accepts_none() {
11183        // The omit-the-slot identity: `:licenca` is optional. The
11184        // gate is a no-op when the author didn't declare a value —
11185        // every caixa without a `:licenca` line trivially passes,
11186        // and the substrate-side `caixa-helm` renderer falls back to
11187        // the documented `"MIT"` placeholder. Mirrors the peer
11188        // `validate_descricao_accepts_none` posture on the sibling
11189        // `Option<String>` Caixa slot.
11190        let c = caixa_with_licenca(None);
11191        c.validate_licenca().unwrap();
11192    }
11193
11194    #[test]
11195    fn validate_licenca_accepts_canonical_expressions() {
11196        // Positive control: every canonical SPDX expression shape
11197        // pleme-io carries in its existing fixtures + the canonical
11198        // SPDX dual-license / with-exception / `+`-suffix / grouped /
11199        // user-defined-reference shapes all pass the gate. Covers
11200        // the single-license, `OR`-compound, `AND`-compound,
11201        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
11202        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
11203        // production the SPDX 2.1 expression grammar admits that
11204        // sits within the alphabet floor the
11205        // `is_spdx_expression_shape` predicate enforces.
11206        for lic in [
11207            "MIT",
11208            "Apache-2.0",
11209            "Apache-2.0 OR MIT",
11210            "Apache-2.0 AND MIT",
11211            "BSD-3-Clause",
11212            "MPL-2.0",
11213            "GPL-3.0-or-later",
11214            "GPL-2.0+",
11215            "Apache-2.0 WITH LLVM-exception",
11216            "(MIT OR Apache-2.0) AND BSD-3-Clause",
11217            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
11218            "LicenseRef-MyLicense",
11219            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
11220            "x",
11221        ] {
11222            let c = caixa_with_licenca(Some(lic));
11223            c.validate_licenca()
11224                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
11225        }
11226    }
11227
11228    #[test]
11229    fn validate_licenca_rejects_trailing_whitespace() {
11230        // Paste-from-doc whitespace footgun. A trailing space in the
11231        // `:licenca` value would silently break a downstream SPDX
11232        // parser that splits on exact `AND` / `OR` / `WITH` keyword
11233        // boundaries. The shape predicate refuses every trailing
11234        // whitespace byte by construction. Peer with
11235        // `validate_repositorio_rejects_whitespace` and
11236        // `validate_edicao_rejects_trailing_whitespace`.
11237        let c = caixa_with_licenca(Some("MIT "));
11238        let err = c.validate_licenca().unwrap_err();
11239        let ManifestError::LicencaInvalid { licenca, .. } = err else {
11240            panic!("expected LicencaInvalid, got {err:?}");
11241        };
11242        assert_eq!(licenca, "MIT ");
11243    }
11244
11245    #[test]
11246    fn validate_licenca_rejects_leading_whitespace() {
11247        // Symmetric paste-from-doc whitespace footgun on the leading
11248        // boundary — the gate refuses every shape that starts with a
11249        // space byte by construction. Peer with
11250        // `validate_edicao_rejects_leading_whitespace`.
11251        let c = caixa_with_licenca(Some(" MIT"));
11252        let err = c.validate_licenca().unwrap_err();
11253        assert!(
11254            matches!(err, ManifestError::LicencaInvalid { .. }),
11255            "got {err:?}",
11256        );
11257    }
11258
11259    #[test]
11260    fn validate_licenca_rejects_control_char() {
11261        // Paste-from-multiline-doc CRLF footgun — control characters
11262        // at the value boundary land as a malformed line in the
11263        // rendered chart `README.md` `## License` section. Peer with
11264        // `validate_repositorio_rejects_control_char` and
11265        // `validate_edicao_rejects_control_char`.
11266        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
11267            let c = caixa_with_licenca(Some(lic));
11268            let err = c.validate_licenca().unwrap_err();
11269            assert!(
11270                matches!(err, ManifestError::LicencaInvalid { .. }),
11271                "expected LicencaInvalid on {lic:?}, got {err:?}",
11272            );
11273        }
11274    }
11275
11276    #[test]
11277    fn validate_licenca_rejects_tab() {
11278        // Tab-from-aligned-doc footgun — SPDX expressions use a
11279        // single ASCII space between tokens; a tab breaks every
11280        // downstream SPDX parser that splits on exact `" "`
11281        // boundaries.
11282        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
11283        let err = c.validate_licenca().unwrap_err();
11284        assert!(
11285            matches!(err, ManifestError::LicencaInvalid { .. }),
11286            "got {err:?}",
11287        );
11288    }
11289
11290    #[test]
11291    fn validate_licenca_rejects_non_ascii() {
11292        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
11293        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
11294        // ".")` production. The shape predicate refuses every
11295        // non-ASCII byte by construction; peer with
11296        // `validate_edicao_rejects_non_ascii_lookalike`.
11297        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
11298            let c = caixa_with_licenca(Some(lic));
11299            let err = c.validate_licenca().unwrap_err();
11300            assert!(
11301                matches!(err, ManifestError::LicencaInvalid { .. }),
11302                "expected LicencaInvalid on {lic:?}, got {err:?}",
11303            );
11304        }
11305    }
11306
11307    #[test]
11308    fn validate_licenca_rejects_underscore() {
11309        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
11310        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
11311        // snake-case identifier conventions that don't apply to the
11312        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
11313        // "-" / "."`). The shape predicate refuses every underscore
11314        // byte by construction.
11315        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
11316            let c = caixa_with_licenca(Some(lic));
11317            let err = c.validate_licenca().unwrap_err();
11318            assert!(
11319                matches!(err, ManifestError::LicencaInvalid { .. }),
11320                "expected LicencaInvalid on {lic:?}, got {err:?}",
11321            );
11322        }
11323    }
11324
11325    #[test]
11326    fn validate_licenca_rejects_comma_separator() {
11327        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
11328        // SPDX expressions compose multiple licenses via `AND` / `OR`
11329        // keywords, not the comma separator. The shape predicate
11330        // refuses every comma byte by construction.
11331        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
11332            let c = caixa_with_licenca(Some(lic));
11333            let err = c.validate_licenca().unwrap_err();
11334            assert!(
11335                matches!(err, ManifestError::LicencaInvalid { .. }),
11336                "expected LicencaInvalid on {lic:?}, got {err:?}",
11337            );
11338        }
11339    }
11340
11341    #[test]
11342    fn validate_licenca_rejects_slash_dual_license() {
11343        // Slash-dual-license colloquial idiom footgun — the
11344        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
11345        // `package.license` field but non-SPDX; the SPDX equivalent
11346        // is `MIT OR Apache-2.0`. The shape predicate refuses every
11347        // forward-slash byte by construction.
11348        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
11349            let c = caixa_with_licenca(Some(lic));
11350            let err = c.validate_licenca().unwrap_err();
11351            assert!(
11352                matches!(err, ManifestError::LicencaInvalid { .. }),
11353                "expected LicencaInvalid on {lic:?}, got {err:?}",
11354            );
11355        }
11356    }
11357
11358    #[test]
11359    fn validate_licenca_rejects_semicolon_separator() {
11360        // Semicolon-list-separator confusion footgun — adjacent to
11361        // the comma-separator idiom, every list-separator-belongs-
11362        // to-list-grammar confusion lands here.
11363        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
11364        let err = c.validate_licenca().unwrap_err();
11365        assert!(
11366            matches!(err, ManifestError::LicencaInvalid { .. }),
11367            "got {err:?}",
11368        );
11369    }
11370
11371    #[test]
11372    fn validate_licenca_empty_takes_precedence_over_shape() {
11373        // Empty-first cascade pin: the empty `Some("")` surfaces the
11374        // narrower `LicencaEmpty` not the shape-predicate-wrapped
11375        // `LicencaInvalid`, mirroring the peer
11376        // `validate_edicao_empty_takes_precedence_over_shape` and
11377        // `validate_repositorio_empty_takes_precedence_over_shape`
11378        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
11379        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
11380        // The shape predicate also refuses the empty input
11381        // (defensively — `"must not be empty"`), but the manifest-
11382        // layer empty arm runs first to surface the narrower
11383        // diagnostic verbatim.
11384        let c = caixa_with_licenca(Some(""));
11385        let err = c.validate_licenca().unwrap_err();
11386        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
11387    }
11388
11389    #[test]
11390    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
11391        // Diagnostic-shape pin on the shape-predicate arm (peer with
11392        // `validate_edicao_invalid_diagnostic_carries_offending_value`
11393        // and `validate_repositorio_diagnostic_carries_offending_value`):
11394        // the error's Display surfaces the offending value + slot
11395        // name verbatim, so a `feira lint` run can render the
11396        // diagnostic without re-parsing and the author can grep
11397        // their caixa.lisp for the offending `:licenca` value.
11398        let c = caixa_with_licenca(Some("Apache_2.0"));
11399        let rendered = c.validate_licenca().unwrap_err().to_string();
11400        assert!(
11401            rendered.contains(":licenca"),
11402            "diagnostic must name the offending slot: {rendered}",
11403        );
11404        assert!(
11405            rendered.contains("Apache_2.0"),
11406            "diagnostic must quote the offending value: {rendered}",
11407        );
11408    }
11409
11410    #[test]
11411    fn validate_licenca_rejects_empty_some() {
11412        // Canonical paste-from-blank-doc footgun. Without this gate
11413        // the empty `Some("")` silently passed the renderer's
11414        // `Option::unwrap_or_else(|| "MIT".into())` (which only
11415        // fires on `None`) and landed as a bare trailing period in
11416        // the rendered chart `README.md` `## License` section.
11417        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
11418        // arm on the sibling `Option<String>` Caixa slot.
11419        let c = caixa_with_licenca(Some(""));
11420        let err = c.validate_licenca().unwrap_err();
11421        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
11422    }
11423
11424    #[test]
11425    fn validate_licenca_template_passes() {
11426        // Round-trip pin: the bare `Caixa::template` shape (whether
11427        // it carries `:licenca` or omits it) passes the gate by
11428        // construction. A future template-shape change that
11429        // introduced `(:licenca "")` would surface here as a
11430        // regression. Mirrors the peer
11431        // `validate_descricao_template_passes` pin.
11432        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11433        c.validate_licenca().unwrap();
11434    }
11435
11436    #[test]
11437    fn validate_licenca_diagnostic_names_offending_slot() {
11438        // Diagnostic-shape pin (peer with
11439        // `validate_descricao_diagnostic_names_offending_slot`):
11440        // the error's Display surfaces the `:licenca` slot name
11441        // verbatim, so a `feira lint` run can render the diagnostic
11442        // without re-parsing and the author can grep their caixa.lisp
11443        // for the offending `:licenca` line.
11444        let c = caixa_with_licenca(Some(""));
11445        let rendered = c.validate_licenca().unwrap_err().to_string();
11446        assert!(
11447            rendered.contains(":licenca"),
11448            "diagnostic must name the offending slot: {rendered}",
11449        );
11450    }
11451
11452    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
11453
11454    #[test]
11455    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
11456        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
11457        // pin: [`Caixa::licenca`] must return the `:licenca` typed
11458        // byte-string verbatim as an `Option<&str>`, byte-equal to the
11459        // raw `self.licenca.as_deref()` access across every
11460        // representative value in the accept-set — `None` (the "omit
11461        // the slot to defer to the caixa-helm renderer's `MIT`
11462        // fallback" arm every existing fixture without a `:licenca`
11463        // line carries), `Some("")` (a past-the-guard sentinel that
11464        // pins the accessor doesn't perform a silent
11465        // `Some("") → None` collapse on the empty arm — validate
11466        // rejects `Some("")` through `LicencaEmpty` but the accessor
11467        // must ship the raw slot verbatim so a validate-time gate
11468        // regression surfaces at the caixa-helm emit boundary rather
11469        // than being silently absorbed into the fallback), `Some("MIT")`
11470        // (the canonical single-license shape every `feira init`
11471        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
11472        // canonical `OR`-compound shape the peer
11473        // `validate_licenca_accepts_canonical_expressions` positive
11474        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
11475        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
11476        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
11477        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
11478        // guard sentinels — validate rejects each through
11479        // `LicencaInvalid` but the accessor must ship the raw slot
11480        // verbatim).
11481        //
11482        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
11483        // accessor pin on the substrate primitive — opens the "outer
11484        // [`Caixa`] `Option<&str>` scalar" projection pattern the
11485        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
11486        // future lifts fold on. Sibling in shape to the peer per-`:placement`
11487        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11488        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11489        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11490        // axes, extended onto the outer top-level [`Caixa`] universal-
11491        // axis surface. Pins against a future silent detour that
11492        // returned an owned `Option<String>` (which would type-check
11493        // but silently allocate on every accessor call, breaking the
11494        // zero-cost projection every peer sibling accessor carries), a
11495        // `Some("") → None` collapse (which would silently absorb the
11496        // `LicencaEmpty` refusal case at the accessor boundary and the
11497        // caixa-helm emit path would silently fall back to `"MIT"` on
11498        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
11499        // `None → Some("MIT")` collapse (which would silently reify
11500        // the caixa-helm renderer's `"MIT"` fallback at the accessor
11501        // boundary and every downstream consumer keying off the
11502        // `Option::is_none()` discriminator would lose the "author
11503        // omitted the slot" signal).
11504        for licenca in [
11505            None,
11506            Some(""),
11507            Some("MIT"),
11508            Some("Apache-2.0 OR MIT"),
11509            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
11510            Some("MIT "),
11511            Some(" MIT"),
11512            Some("MIT\n"),
11513            Some("Apache_2.0"),
11514            Some("MIT,Apache-2.0"),
11515        ] {
11516            let c = caixa_with_licenca(licenca);
11517            assert_eq!(
11518                c.licenca(),
11519                licenca,
11520                "Caixa::licenca must return :licenca verbatim (got {:?}, \
11521                 expected {licenca:?})",
11522                c.licenca(),
11523            );
11524            assert_eq!(
11525                c.licenca(),
11526                c.licenca.as_deref(),
11527                "Caixa::licenca must byte-equal the raw \
11528                 `self.licenca.as_deref()` field access across every \
11529                 value in the Option<&str> accept-set",
11530            );
11531        }
11532    }
11533
11534    #[test]
11535    fn validate_licenca_empty_arm_routes_through_accessor() {
11536        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
11537        // must key off [`Caixa::licenca`], not the raw
11538        // `self.licenca.as_deref()` field access. Structurally: a
11539        // `Caixa { licenca: Some(""), .. }` must surface the
11540        // `LicencaEmpty` refusal exactly, and a
11541        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
11542        // single-license form) must pass validate. The pair jointly
11543        // pins the accessor + validate-gate composition: any future
11544        // silent detour that had the accessor return `None` on the
11545        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
11546        // silently absorb the `LicencaEmpty` refusal at the accessor
11547        // boundary and the validate gate would accept a struct-literal
11548        // `Caixa { licenca: Some(""), .. }` — the composition pin
11549        // catches that at caixa-core build time.
11550        //
11551        // Peer of the per-`:politicas :circuit-breaker`
11552        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
11553        // accessor-composition pin
11554        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
11555        // on the sibling per-M3-mesh-slot required-`u32` axis — same
11556        // "the validate / shape-gate predicate must route through the
11557        // substrate-primitive typed dispatch" discipline extended onto
11558        // the outer top-level [`Caixa`] universal-axis
11559        // `Option<&str>`-composition surface.
11560        let c = caixa_with_licenca(Some(""));
11561        assert!(
11562            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
11563            "validate_licenca must reject licenca == Some(\"\") with \
11564             LicencaEmpty — the accessor and the validate gate must \
11565             route through the same substrate-primitive typed dispatch \
11566             on the :licenca empty arm",
11567        );
11568        let c = caixa_with_licenca(Some("MIT"));
11569        assert!(
11570            c.validate_licenca().is_ok(),
11571            "validate_licenca must accept licenca == Some(\"MIT\") \
11572             (the canonical single-license SPDX shape)",
11573        );
11574    }
11575
11576    #[test]
11577    fn licenca_projects_option_str_by_borrow() {
11578        // The by-borrow pin: [`Caixa::licenca`] returns
11579        // `Option<&str>` by borrow — the `&str` borrows the underlying
11580        // `String` storage of the `Option<String>` slot and the
11581        // accessor must not allocate a fresh `String` on every call.
11582        // Peer of the per-`:placement`
11583        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
11584        // borrow pin on the peer per-M3-mesh-slot
11585        // `Option<&str>`-return axis, extended onto the outer top-
11586        // level [`Caixa`] universal-axis `Option<&str>` shape — the
11587        // accessor's returned `&str` must borrow from `&self` (the
11588        // returned reference's lifetime is tied to `&self`), and
11589        // calling the accessor twice on the same [`Caixa`] must yield
11590        // the same `Option<&str>` verbatim (idempotent, no side
11591        // effects on `&self`).
11592        //
11593        // Pins against a future silent detour that returned an owned
11594        // `Option<String>` (which would type-check but silently
11595        // allocate on every call, breaking the zero-cost projection
11596        // every peer sibling accessor carries), or a one-arm-only
11597        // accessor that returned a saturating value on some sentinel
11598        // input (breaking the pass-through invariant the sibling
11599        // required-scalar accessors carry).
11600        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
11601            let c = caixa_with_licenca(licenca);
11602            let first = c.licenca();
11603            let second = c.licenca();
11604            assert_eq!(
11605                first, second,
11606                "Caixa::licenca must be idempotent — two successive \
11607                 calls on the same &self must return the same \
11608                 Option<&str>",
11609            );
11610            assert_eq!(
11611                first, licenca,
11612                "Caixa::licenca must return :licenca verbatim by \
11613                 borrow — got {first:?}, expected {licenca:?}",
11614            );
11615        }
11616    }
11617
11618    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
11619
11620    #[test]
11621    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
11622        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
11623        // pin: [`Caixa::repositorio`] must return the `:repositorio`
11624        // typed byte-string verbatim as an `Option<&str>`, byte-equal
11625        // to the raw `self.repositorio.as_deref()` access across every
11626        // representative value in the accept-set — `None` (the "omit
11627        // the slot to defer to the per-renderer placeholder" arm every
11628        // existing fixture without a `:repositorio` line carries),
11629        // `Some("")` (a past-the-guard sentinel that pins the accessor
11630        // doesn't perform a silent `Some("") → None` collapse on the
11631        // empty arm — validate rejects `Some("")` through
11632        // `RepositorioEmpty` but the accessor must ship the raw slot
11633        // verbatim so a validate-time gate regression surfaces at the
11634        // caixa-helm / caixa-flux emit boundary rather than being
11635        // silently absorbed into the per-renderer fallback),
11636        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
11637        // shorthand every existing manifest fixture across
11638        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
11639        // `Some("https://github.com/pleme-io/checkout")` (the canonical
11640        // `https://` URL the README quickstart uses),
11641        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
11642        // `Some("git://github.com/pleme-io/checkout.git")` /
11643        // `Some("git@github.com:pleme-io/checkout.git")` /
11644        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
11645        // github scheme the shared `is_git_repo_url` predicate
11646        // documents), and five past-the-guard sentinels for the
11647        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
11648        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
11649        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
11650        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
11651        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
11652        // sentinels pin the accessor doesn't silently absorb the
11653        // refusal cases into a fallback).
11654        //
11655        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
11656        // accessor pin on the substrate primitive — sibling of the peer
11657        // [`Caixa::licenca`] (6d5bc28) pin
11658        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
11659        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
11660        // projection pin pattern this pin folds on. Sibling in shape to
11661        // the peer per-`:placement`
11662        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
11663        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
11664        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
11665        // axes, extended onto the outer top-level [`Caixa`] universal-
11666        // axis surface. Pins against a future silent detour that
11667        // returned an owned `Option<String>` (which would type-check
11668        // but silently allocate on every accessor call, breaking the
11669        // zero-cost projection every peer sibling accessor carries), a
11670        // `Some("") → None` collapse (which would silently absorb the
11671        // `RepositorioEmpty` refusal case at the accessor boundary and
11672        // the caixa-helm `Chart.yaml` `home:` fold would silently
11673        // render a `home: null` / omitted field on a struct-literal
11674        // `Caixa { repositorio: Some(""), .. }`), or a
11675        // `None → Some(<default>)` collapse (which would silently reify
11676        // the per-renderer fallback at the accessor boundary and every
11677        // downstream consumer keying off the `Option::is_none()`
11678        // discriminator would lose the "author omitted the slot"
11679        // signal).
11680        for repositorio in [
11681            None,
11682            Some(""),
11683            Some("github:pleme-io/hello-rio"),
11684            Some("https://github.com/pleme-io/checkout"),
11685            Some("ssh://git@github.com/pleme-io/checkout.git"),
11686            Some("git://github.com/pleme-io/checkout.git"),
11687            Some("git@github.com:pleme-io/checkout.git"),
11688            Some("file:///opt/mirrors/pleme-io/checkout"),
11689            Some("pleme-io/checkout"),
11690            Some("-upload-pack=evil"),
11691            Some("github:pleme-io/checkout?ref=main"),
11692            Some("github:pleme-io/checkout#main"),
11693            Some("github:pleme-io/{tpl}"),
11694        ] {
11695            let c = caixa_with_repositorio(repositorio);
11696            assert_eq!(
11697                c.repositorio(),
11698                repositorio,
11699                "Caixa::repositorio must return :repositorio verbatim \
11700                 (got {:?}, expected {repositorio:?})",
11701                c.repositorio(),
11702            );
11703            assert_eq!(
11704                c.repositorio(),
11705                c.repositorio.as_deref(),
11706                "Caixa::repositorio must byte-equal the raw \
11707                 `self.repositorio.as_deref()` field access across every \
11708                 value in the Option<&str> accept-set",
11709            );
11710        }
11711    }
11712
11713    #[test]
11714    fn validate_repositorio_empty_arm_routes_through_accessor() {
11715        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
11716        // gate must key off [`Caixa::repositorio`], not the raw
11717        // `self.repositorio.as_deref()` field access. Structurally: a
11718        // `Caixa { repositorio: Some(""), .. }` must surface the
11719        // `RepositorioEmpty` refusal exactly, and a
11720        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
11721        // (the canonical `github:` shorthand form) must pass validate.
11722        // The pair jointly pins the accessor + validate-gate
11723        // composition: any future silent detour that had the accessor
11724        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
11725        // collapse) would silently absorb the `RepositorioEmpty` refusal
11726        // at the accessor boundary and the validate gate would accept a
11727        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
11728        // composition pin catches that at caixa-core build time.
11729        //
11730        // Peer of the [`Caixa::licenca`] (6d5bc28)
11731        // `validate_licenca_empty_arm_routes_through_accessor`
11732        // composition pin on the sibling outer top-level [`Caixa`]
11733        // `Option<&str>` universal-axis surface — same "the validate /
11734        // shape-gate predicate must route through the substrate-
11735        // primitive typed dispatch" discipline extended onto the second
11736        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
11737        // composition surface.
11738        let c = caixa_with_repositorio(Some(""));
11739        assert!(
11740            matches!(
11741                c.validate_repositorio(),
11742                Err(ManifestError::RepositorioEmpty),
11743            ),
11744            "validate_repositorio must reject repositorio == Some(\"\") \
11745             with RepositorioEmpty — the accessor and the validate gate \
11746             must route through the same substrate-primitive typed \
11747             dispatch on the :repositorio empty arm",
11748        );
11749        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
11750        assert!(
11751            c.validate_repositorio().is_ok(),
11752            "validate_repositorio must accept repositorio == \
11753             Some(\"github:pleme-io/hello-rio\") (the canonical \
11754             `github:` shorthand git-repo-URL shape)",
11755        );
11756    }
11757
11758    #[test]
11759    fn repositorio_projects_option_str_by_borrow() {
11760        // The by-borrow pin: [`Caixa::repositorio`] returns
11761        // `Option<&str>` by borrow — the `&str` borrows the underlying
11762        // `String` storage of the `Option<String>` slot and the
11763        // accessor must not allocate a fresh `String` on every call.
11764        // Peer of the per-`:placement`
11765        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
11766        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
11767        // `Option<&str>`-return axes, extended onto the second outer
11768        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
11769        // the accessor's returned `&str` must borrow from `&self` (the
11770        // returned reference's lifetime is tied to `&self`), and
11771        // calling the accessor twice on the same [`Caixa`] must yield
11772        // the same `Option<&str>` verbatim (idempotent, no side effects
11773        // on `&self`).
11774        //
11775        // Pins against a future silent detour that returned an owned
11776        // `Option<String>` (which would type-check but silently
11777        // allocate on every call, breaking the zero-cost projection
11778        // every peer sibling accessor carries), or a one-arm-only
11779        // accessor that returned a saturating value on some sentinel
11780        // input (breaking the pass-through invariant the sibling
11781        // required-scalar accessors carry).
11782        for repositorio in [
11783            None,
11784            Some(""),
11785            Some("github:pleme-io/hello-rio"),
11786            Some("https://github.com/pleme-io/checkout"),
11787        ] {
11788            let c = caixa_with_repositorio(repositorio);
11789            let first = c.repositorio();
11790            let second = c.repositorio();
11791            assert_eq!(
11792                first, second,
11793                "Caixa::repositorio must be idempotent — two successive \
11794                 calls on the same &self must return the same \
11795                 Option<&str>",
11796            );
11797            assert_eq!(
11798                first, repositorio,
11799                "Caixa::repositorio must return :repositorio verbatim by \
11800                 borrow — got {first:?}, expected {repositorio:?}",
11801            );
11802        }
11803    }
11804
11805    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
11806
11807    #[test]
11808    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
11809        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
11810        // return the author-declared `:repositorio` byte-string verbatim
11811        // on the `Some` arm — no scheme rewrite, no trailing-slash
11812        // canonicalization, no `github:` → `https://github.com/`
11813        // desugaring. The resolved-URL composer is the projection of
11814        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
11815        // the `String`-return arity every substrate-side field-fill
11816        // consumer keys off; on the `Some` arm the projection is
11817        // `str::to_owned` verbatim, so every accept-set value the
11818        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
11819        // across_permutations` pin covers (`https://…`, `github:…`,
11820        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
11821        // guard sentinel `pleme-io/…`) must survive the accessor
11822        // byte-equal. Pins against a future silent detour that rewrote
11823        // the `github:` shorthand to the `https://github.com/` full URL
11824        // at the accessor boundary (which would silently split the
11825        // resolved-URL surface from the raw [`Caixa::repositorio`]
11826        // accessor's documented pass-through invariant), or a trailing-
11827        // slash normalization (which would silently break the
11828        // FluxCD `GitRepository` `spec.url` byte-exact match every
11829        // downstream consumer keys the source-controller reconcile off).
11830        for repositorio in [
11831            "github:pleme-io/hello-rio",
11832            "https://github.com/pleme-io/checkout",
11833            "ssh://git@github.com/pleme-io/checkout.git",
11834            "git://github.com/pleme-io/checkout.git",
11835            "git@github.com:pleme-io/checkout.git",
11836            "file:///opt/mirrors/pleme-io/checkout",
11837        ] {
11838            let c = caixa_with_repositorio(Some(repositorio));
11839            assert_eq!(
11840                c.canonical_git_url(),
11841                repositorio,
11842                "Caixa::canonical_git_url on the Some arm must return \
11843                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
11844                c.canonical_git_url(),
11845            );
11846        }
11847    }
11848
11849    #[test]
11850    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
11851        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
11852        // `None` arm must emit the substrate's canonical pleme-org github
11853        // URL derived from `caixa.nome()` — `https://github.com/<org>/
11854        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
11855        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
11856        // is the exact byte-image of the prior inline
11857        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
11858        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
11859        // re-derived open-coded. Pins against a future silent detour
11860        // that migrated the `<org>` segment to a different constant (a
11861        // fork rebranding that split off a new
11862        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
11863        // to migrate onto), a scheme change (`https://` → `git://` or
11864        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
11865        // override (which would break the substrate-wide single-source-
11866        // of-truth guarantee this method encodes).
11867        let c = caixa_with_repositorio(None);
11868        let expected = format!(
11869            "https://github.com/{org}/{nome}",
11870            org = crate::DEFAULT_PLEME_GIT_ORG,
11871            nome = c.nome(),
11872        );
11873        assert_eq!(
11874            c.canonical_git_url(),
11875            expected,
11876            "Caixa::canonical_git_url on the None arm must fold through \
11877             the substrate's canonical pleme-org github URL fallback \
11878             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
11879             {:?}, expected {expected:?}",
11880            c.canonical_git_url(),
11881        );
11882    }
11883
11884    #[test]
11885    fn canonical_git_url_byte_matches_manual_composition() {
11886        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
11887        // byte-identically to the manual open-coded
11888        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
11889        //  format!("https://github.com/{org}/{nome}", ...))` composition
11890        // every prior substrate-side caller re-derived. Guards the
11891        // paired-site convergence just applied at caixa-flux's
11892        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
11893        // now routes through this accessor): a future implementation of
11894        // this method that reordered the format arguments, swapped the
11895        // `<org>` constant for a different one, or interposed a
11896        // canonicalization pass on the `Some` arm surfaces here as a
11897        // caixa-core build-time test failure rather than as a downstream
11898        // FluxCD `GitRepository` reconcile mismatch far from this
11899        // method's source.
11900        for repositorio in [
11901            None,
11902            Some("github:pleme-io/hello-rio"),
11903            Some("https://github.com/pleme-io/checkout"),
11904            Some("ssh://git@github.com/pleme-io/checkout.git"),
11905        ] {
11906            let c = caixa_with_repositorio(repositorio);
11907            let manual = c.repositorio().map_or_else(
11908                || {
11909                    format!(
11910                        "https://github.com/{org}/{nome}",
11911                        org = crate::DEFAULT_PLEME_GIT_ORG,
11912                        nome = c.nome(),
11913                    )
11914                },
11915                str::to_owned,
11916            );
11917            assert_eq!(
11918                c.canonical_git_url(),
11919                manual,
11920                "Caixa::canonical_git_url must byte-equal the manual \
11921                 open-coded `repositorio().map(str::to_owned)\
11922                 .unwrap_or_else(|| format!(...))` composition across \
11923                 every representative :repositorio input — got {:?}, \
11924                 expected {manual:?}",
11925                c.canonical_git_url(),
11926            );
11927        }
11928    }
11929
11930    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
11931
11932    #[test]
11933    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
11934        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
11935        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
11936        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
11937        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
11938        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
11939        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
11940        // the `0.0.0` boundary case. Every accept-set value the peer
11941        // validate gate lets through must survive the resolved-tag
11942        // projection byte-equal.
11943        for versao in [
11944            "0.1.0",
11945            "0.0.0",
11946            "1.0.0",
11947            "1.2.3-rc.1",
11948            "1.2.3+build.42",
11949            "1.2.3-rc.1+build.42",
11950        ] {
11951            let c = caixa_with_versao(versao);
11952            let expected = format!(
11953                "{prefix}{versao}",
11954                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11955            );
11956            assert_eq!(
11957                c.publish_tag(),
11958                expected,
11959                "Caixa::publish_tag must compose \
11960                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
11961                 :versao ({versao:?}) verbatim — got {got:?}, \
11962                 expected {expected:?}",
11963                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11964                got = c.publish_tag(),
11965            );
11966        }
11967    }
11968
11969    #[test]
11970    fn publish_tag_starts_with_default_publish_tag_prefix() {
11971        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
11972        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
11973        // byte-string on every input, guarding a hypothetical future
11974        // implementation that migrated the prefix segment to an inline
11975        // literal (`"v"`) that would silently drift from any rebrand of
11976        // the lifted constant. Peer to the sibling caixa-flux
11977        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
11978        // test which pins the same prefix invariant at the reader-side
11979        // `GitRefSpec::Tag` emit site.
11980        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
11981            let c = caixa_with_versao(versao);
11982            let tag = c.publish_tag();
11983            assert!(
11984                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
11985                "Caixa::publish_tag emission {tag:?} must start with \
11986                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
11987                 ({prefix:?})",
11988                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
11989            );
11990        }
11991    }
11992
11993    #[test]
11994    fn publish_tag_byte_matches_manual_composition() {
11995        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
11996        // identically to the manual open-coded
11997        // `format!("{prefix}{versao}", prefix =
11998        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
11999        //  caixa.versao())` composition every prior substrate-side
12000        // caller re-derived. Guards the paired-site convergence just
12001        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
12002        // `git_ref` composer (which now routes through this accessor):
12003        // a future implementation of this method that reordered the
12004        // format arguments, swapped the `<prefix>` constant for a
12005        // different one, or interposed a canonicalization pass on the
12006        // `:versao` axis surfaces here as a caixa-core build-time test
12007        // failure rather than as a downstream FluxCD `GitRepository`
12008        // reconcile mismatch far from this method's source.
12009        for versao in [
12010            "0.1.0",
12011            "0.0.0",
12012            "1.2.3-rc.1",
12013            "1.2.3+build.42",
12014            "1.2.3-rc.1+build.42",
12015        ] {
12016            let c = caixa_with_versao(versao);
12017            let manual = format!(
12018                "{prefix}{versao}",
12019                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12020                versao = c.versao(),
12021            );
12022            assert_eq!(
12023                c.publish_tag(),
12024                manual,
12025                "Caixa::publish_tag must byte-equal the manual \
12026                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
12027                 composition across every representative :versao input \
12028                 — got {got:?}, expected {manual:?}",
12029                got = c.publish_tag(),
12030            );
12031        }
12032    }
12033
12034    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
12035
12036    #[test]
12037    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
12038        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
12039        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
12040        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
12041        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
12042        // set sweep documents — single-word, hyphen-joined, version-
12043        // suffixed, single-char, two-char, digit-start, retry-suffixed.
12044        // Every accept-set value the peer validate gate lets through must
12045        // survive the resolved-chart-name projection byte-equal.
12046        for nome in [
12047            "checkout",
12048            "cart-v2",
12049            "a",
12050            "db",
12051            "3rd-party-shim",
12052            "payment-retry",
12053            "0",
12054        ] {
12055            let c = caixa_with_nome(nome);
12056            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
12057            assert_eq!(
12058                c.lareira_chart_name(),
12059                expected,
12060                "Caixa::lareira_chart_name must compose \
12061                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
12062                 :nome ({nome:?}) verbatim — got {got:?}, \
12063                 expected {expected:?}",
12064                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
12065                got = c.lareira_chart_name(),
12066            );
12067        }
12068    }
12069
12070    #[test]
12071    fn lareira_chart_name_starts_with_lifted_prefix() {
12072        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
12073        // must begin with the canonical
12074        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
12075        // input, guarding a hypothetical future implementation that
12076        // migrated the prefix segment to an inline literal (`"lareira-"`)
12077        // that would silently drift from any rebrand of the lifted
12078        // constant. Peer to the sibling
12079        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
12080        // the co-resident resolved-publish-tag composer's prefix axis.
12081        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
12082            let c = caixa_with_nome(nome);
12083            let chart = c.lareira_chart_name();
12084            assert!(
12085                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
12086                "Caixa::lareira_chart_name emission {chart:?} must start \
12087                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
12088                 ({prefix:?})",
12089                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
12090            );
12091        }
12092    }
12093
12094    #[test]
12095    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
12096        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
12097        // byte-identically to the manual open-coded
12098        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
12099        // composition every prior substrate-side caller re-derived.
12100        // Guards the paired-site convergence just applied at caixa-helm's
12101        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
12102        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
12103        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
12104        // composer (all of which now route through this accessor): a
12105        // future implementation of this method that reordered the
12106        // composition arguments, swapped the `<prefix>` constant for a
12107        // different one, or interposed a canonicalization pass on the
12108        // `:nome` axis surfaces here as a caixa-core build-time test
12109        // failure rather than as a downstream Helm chart-render / FluxCD
12110        // reconcile / tatara Process-CR mismatch far from this method's
12111        // source.
12112        for nome in [
12113            "checkout",
12114            "cart-v2",
12115            "a",
12116            "db",
12117            "3rd-party-shim",
12118            "payment-retry",
12119        ] {
12120            let c = caixa_with_nome(nome);
12121            let manual = crate::lareira_chart_name(c.nome());
12122            assert_eq!(
12123                c.lareira_chart_name(),
12124                manual,
12125                "Caixa::lareira_chart_name must byte-equal the manual \
12126                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
12127                 composition across every representative :nome input — \
12128                 got {got:?}, expected {manual:?}",
12129                got = c.lareira_chart_name(),
12130            );
12131        }
12132    }
12133
12134    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
12135
12136    #[test]
12137    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
12138        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
12139        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
12140        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
12141        // across the full paired `(registry, :nome)` accept-set — every
12142        // representative registry the substrate-side emitters carry
12143        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
12144        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
12145        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
12146        // inline_format` render-side pin exercises; `registry.example.
12147        // com`, an off-org shape; `localhost:5000`, the local-dev shape
12148        // every `feira chart` iteration path lands under) × every DNS-
12149        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
12150        // forms` positive-set sweep documents (single-word, hyphen-
12151        // joined, single-char, two-char, digit-start, retry-suffixed).
12152        // Every accept-set pair the peer validate gates let through must
12153        // survive the resolved-OCI-ref projection byte-equal.
12154        for registry in [
12155            "ghcr.io/pleme-io/charts",
12156            "ghcr.io/pleme-io",
12157            "registry.example.com",
12158            "localhost:5000",
12159        ] {
12160            for nome in [
12161                "checkout",
12162                "cart-v2",
12163                "a",
12164                "db",
12165                "3rd-party-shim",
12166                "payment-retry",
12167                "0",
12168            ] {
12169                let c = caixa_with_nome(nome);
12170                let expected = format!(
12171                    "{scheme}{registry}/{chart}",
12172                    scheme = crate::OCI_SCHEME_PREFIX,
12173                    chart = crate::lareira_chart_name(nome),
12174                );
12175                assert_eq!(
12176                    c.oci_chart_ref(registry),
12177                    expected,
12178                    "Caixa::oci_chart_ref must compose \
12179                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
12180                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
12181                     expected {expected:?}",
12182                    scheme = crate::OCI_SCHEME_PREFIX,
12183                    got = c.oci_chart_ref(registry),
12184                );
12185            }
12186        }
12187    }
12188
12189    #[test]
12190    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
12191        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
12192        // emission must begin with the canonical
12193        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
12194        // a hypothetical future implementation that migrated the scheme
12195        // segment to an inline literal (`"oci://"`) that would silently
12196        // drift from any rebrand of the lifted constant. Peer to the
12197        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
12198        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
12199        // co-resident resolved-publish-tag / resolved-chart-name
12200        // composers' prefix axes.
12201        for registry in [
12202            "ghcr.io/pleme-io/charts",
12203            "ghcr.io/pleme-io",
12204            "localhost:5000",
12205        ] {
12206            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
12207                let c = caixa_with_nome(nome);
12208                let ref_ = c.oci_chart_ref(registry);
12209                assert!(
12210                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
12211                    "Caixa::oci_chart_ref emission {ref_:?} must start \
12212                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
12213                     — registry ({registry:?}), :nome ({nome:?})",
12214                    scheme = crate::OCI_SCHEME_PREFIX,
12215                );
12216            }
12217        }
12218    }
12219
12220    #[test]
12221    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
12222        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
12223        // identically to the manual open-coded
12224        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
12225        // composition every prior substrate-side caller re-derived.
12226        // Guards the paired-site convergence just applied at caixa-
12227        // tatara's [`derive_chart_ref`] helper (which now routes through
12228        // this accessor): a future implementation of this method that
12229        // reordered the composition arguments, swapped the `<scheme>`
12230        // constant for a different one, migrated the `<chart>` segment
12231        // off the paired [`crate::lareira_chart_name`] composer, or
12232        // interposed a canonicalization pass on either input axis
12233        // surfaces here as a caixa-core build-time test failure rather
12234        // than as a downstream `helm install` / FluxCD OCI-source
12235        // reconcile / tatara `Process`-CR mismatch far from this
12236        // method's source. Sibling to the peer
12237        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
12238        // / [`publish_tag_byte_matches_manual_composition`] /
12239        // [`canonical_git_url_byte_matches_manual_composition`] byte-
12240        // parity pins that carry the same discipline on the co-resident
12241        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
12242        // composers.
12243        for registry in [
12244            "ghcr.io/pleme-io/charts",
12245            "ghcr.io/pleme-io",
12246            "registry.example.com",
12247            "localhost:5000",
12248        ] {
12249            for nome in [
12250                "checkout",
12251                "cart-v2",
12252                "a",
12253                "db",
12254                "3rd-party-shim",
12255                "payment-retry",
12256            ] {
12257                let c = caixa_with_nome(nome);
12258                let manual = crate::oci_chart_ref(registry, c.nome());
12259                assert_eq!(
12260                    c.oci_chart_ref(registry),
12261                    manual,
12262                    "Caixa::oci_chart_ref must byte-equal the manual \
12263                     open-coded `caixa_core::oci_chart_ref(registry, \
12264                     caixa.nome())` composition across every representative \
12265                     (registry, :nome) pair — registry ({registry:?}), \
12266                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
12267                    got = c.oci_chart_ref(registry),
12268                );
12269            }
12270        }
12271    }
12272
12273    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
12274
12275    #[test]
12276    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
12277        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
12278        // pin: [`Caixa::descricao`] must return the `:descricao` typed
12279        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12280        // raw `self.descricao.as_deref()` access across every
12281        // representative value in the accept-set — `None` (the "omit
12282        // the slot to defer to the per-renderer `caixa.nome`-derived
12283        // fallback" arm every existing fixture without a `:descricao`
12284        // line carries), `Some("")` (a past-the-guard sentinel that
12285        // pins the accessor doesn't perform a silent `Some("") → None`
12286        // collapse on the empty arm — validate rejects `Some("")`
12287        // through `DescricaoEmpty` but the accessor must ship the raw
12288        // slot verbatim so a validate-time gate regression surfaces at
12289        // the caixa-helm / caixa-feira emit boundary rather than being
12290        // silently absorbed into the per-renderer `caixa.nome`-derived
12291        // fallback), `Some("Checkout flow.")` (the canonical one-line
12292        // prose descriptor the peer
12293        // `validate_descricao_accepts_canonical_value` positive sweep
12294        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
12295        // Servico.")` (the multi-byte Unicode continuation-byte shape
12296        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
12297        // multi-glyph Unicode shape the peer
12298        // `is_chart_description_shape` predicate accepts), and five
12299        // past-the-guard sentinels for the `DescricaoInvalid` refusal
12300        // cases (`Some(" Checkout flow.")` leading-whitespace,
12301        // `Some("Checkout flow. ")` trailing-whitespace,
12302        // `Some("Checkout\nflow.")` embedded-LF,
12303        // `Some("Checkout\tflow.")` embedded-TAB, and
12304        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
12305        // the accessor doesn't silently absorb the refusal cases into
12306        // a fallback).
12307        //
12308        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
12309        // accessor pin on the substrate primitive — sibling of the peer
12310        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
12311        // (cc7332d) pins that opened the "outer [`Caixa`]
12312        // `Option<&str>` scalar" projection pin pattern this pin folds
12313        // on. Sibling in shape to the peer per-`:placement`
12314        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12315        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12316        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12317        // axes, extended onto the outer top-level [`Caixa`] universal-
12318        // axis surface. Pins against a future silent detour that
12319        // returned an owned `Option<String>` (which would type-check
12320        // but silently allocate on every accessor call, breaking the
12321        // zero-cost projection every peer sibling accessor carries), a
12322        // `Some("") → None` collapse (which would silently absorb the
12323        // `DescricaoEmpty` refusal case at the accessor boundary and
12324        // the caixa-helm `Chart.yaml` `description:` fold would
12325        // silently render a `caixa.nome`-derived fallback on a
12326        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
12327        // `None → Some(<default>)` collapse (which would silently
12328        // reify the per-renderer `caixa.nome`-derived fallback at the
12329        // accessor boundary and every downstream consumer keying off
12330        // the `Option::is_none()` discriminator would lose the "author
12331        // omitted the slot" signal).
12332        for descricao in [
12333            None,
12334            Some(""),
12335            Some("Checkout flow."),
12336            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
12337            Some("→ — · ✓"),
12338            Some(" Checkout flow."),
12339            Some("Checkout flow. "),
12340            Some("Checkout\nflow."),
12341            Some("Checkout\tflow."),
12342            Some("Checkout\x00flow."),
12343        ] {
12344            let c = caixa_with_descricao(descricao);
12345            assert_eq!(
12346                c.descricao(),
12347                descricao,
12348                "Caixa::descricao must return :descricao verbatim (got \
12349                 {:?}, expected {descricao:?})",
12350                c.descricao(),
12351            );
12352            assert_eq!(
12353                c.descricao(),
12354                c.descricao.as_deref(),
12355                "Caixa::descricao must byte-equal the raw \
12356                 `self.descricao.as_deref()` field access across every \
12357                 value in the Option<&str> accept-set",
12358            );
12359        }
12360    }
12361
12362    #[test]
12363    fn validate_descricao_empty_arm_routes_through_accessor() {
12364        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
12365        // gate must key off [`Caixa::descricao`], not the raw
12366        // `self.descricao.as_deref()` field access. Structurally: a
12367        // `Caixa { descricao: Some(""), .. }` must surface the
12368        // `DescricaoEmpty` refusal exactly, and a
12369        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
12370        // canonical one-line-prose form) must pass validate. The pair
12371        // jointly pins the accessor + validate-gate composition: any
12372        // future silent detour that had the accessor return `None` on
12373        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
12374        // silently absorb the `DescricaoEmpty` refusal at the accessor
12375        // boundary and the validate gate would accept a struct-literal
12376        // `Caixa { descricao: Some(""), .. }` — the composition pin
12377        // catches that at caixa-core build time.
12378        //
12379        // Peer of the [`Caixa::licenca`] (6d5bc28)
12380        // `validate_licenca_empty_arm_routes_through_accessor` and
12381        // [`Caixa::repositorio`] (cc7332d)
12382        // `validate_repositorio_empty_arm_routes_through_accessor`
12383        // composition pins on the sibling outer top-level [`Caixa`]
12384        // `Option<&str>` universal-axis surface — same "the validate /
12385        // shape-gate predicate must route through the substrate-
12386        // primitive typed dispatch" discipline extended onto the third
12387        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
12388        // composition surface.
12389        let c = caixa_with_descricao(Some(""));
12390        assert!(
12391            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
12392            "validate_descricao must reject descricao == Some(\"\") \
12393             with DescricaoEmpty — the accessor and the validate gate \
12394             must route through the same substrate-primitive typed \
12395             dispatch on the :descricao empty arm",
12396        );
12397        let c = caixa_with_descricao(Some("Checkout flow."));
12398        assert!(
12399            c.validate_descricao().is_ok(),
12400            "validate_descricao must accept descricao == \
12401             Some(\"Checkout flow.\") (the canonical one-line-prose \
12402             chart-description shape)",
12403        );
12404    }
12405
12406    #[test]
12407    fn descricao_projects_option_str_by_borrow() {
12408        // The by-borrow pin: [`Caixa::descricao`] returns
12409        // `Option<&str>` by borrow — the `&str` borrows the underlying
12410        // `String` storage of the `Option<String>` slot and the
12411        // accessor must not allocate a fresh `String` on every call.
12412        // Peer of the [`Caixa::licenca`] (6d5bc28) and
12413        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
12414        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
12415        // the per-`:placement`
12416        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12417        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
12418        // return axis, extended onto the third outer top-level
12419        // [`Caixa`] universal-axis `Option<&str>` shape — the
12420        // accessor's returned `&str` must borrow from `&self` (the
12421        // returned reference's lifetime is tied to `&self`), and
12422        // calling the accessor twice on the same [`Caixa`] must yield
12423        // the same `Option<&str>` verbatim (idempotent, no side
12424        // effects on `&self`).
12425        //
12426        // Pins against a future silent detour that returned an owned
12427        // `Option<String>` (which would type-check but silently
12428        // allocate on every call, breaking the zero-cost projection
12429        // every peer sibling accessor carries), or a one-arm-only
12430        // accessor that returned a saturating value on some sentinel
12431        // input (breaking the pass-through invariant the sibling
12432        // required-scalar accessors carry).
12433        for descricao in [
12434            None,
12435            Some(""),
12436            Some("Checkout flow."),
12437            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
12438        ] {
12439            let c = caixa_with_descricao(descricao);
12440            let first = c.descricao();
12441            let second = c.descricao();
12442            assert_eq!(
12443                first, second,
12444                "Caixa::descricao must be idempotent — two successive \
12445                 calls on the same &self must return the same \
12446                 Option<&str>",
12447            );
12448            assert_eq!(
12449                first, descricao,
12450                "Caixa::descricao must return :descricao verbatim by \
12451                 borrow — got {first:?}, expected {descricao:?}",
12452            );
12453        }
12454    }
12455
12456    // ── validate_edicao — universal-axis language-edition shape ──
12457
12458    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
12459        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12460        c.edicao = edicao.map(String::from);
12461        c
12462    }
12463
12464    #[test]
12465    fn validate_edicao_accepts_none() {
12466        // The omit-the-slot identity: `:edicao` is optional. The
12467        // gate is a no-op when the author didn't declare a value —
12468        // every caixa without an `:edicao` line trivially passes,
12469        // and the substrate-side build pipeline falls back to the
12470        // documented default edition. Mirrors the peer
12471        // `validate_licenca_accepts_none` posture on the sibling
12472        // `Option<String>` Caixa slot.
12473        let c = caixa_with_edicao(None);
12474        c.validate_edicao().unwrap();
12475    }
12476
12477    #[test]
12478    fn validate_edicao_accepts_canonical_value() {
12479        // Positive control: the canonical `"2026"` edition every
12480        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
12481        // `caixa-mesh`) carries by construction passes the gate.
12482        // Future-introduced sibling editions (`"2027"`, `"2030"`,
12483        // `"2049"`) that match the same 4-digit ASCII decimal year
12484        // shape must also trivially pass — the structural shape
12485        // predicate accepts every well-formed year regardless of
12486        // whether the substrate yet understands the specific value
12487        // (a future known-edition allowlist tightens that).
12488        for ed in ["2026", "2027", "2030", "2049"] {
12489            let c = caixa_with_edicao(Some(ed));
12490            c.validate_edicao()
12491                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
12492        }
12493    }
12494
12495    #[test]
12496    fn validate_edicao_rejects_empty_some() {
12497        // Canonical paste-from-blank-doc footgun. Without this gate
12498        // the empty `Some("")` silently lands as `(:edicao "")` in
12499        // the rendered caixa.lisp and a future renderer-side
12500        // consumer's `Option::unwrap_or_else` (which only fires on
12501        // `None`) skips its fallback. Mirrors the peer
12502        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
12503        // `Option<String>` Caixa slot.
12504        let c = caixa_with_edicao(Some(""));
12505        let err = c.validate_edicao().unwrap_err();
12506        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
12507    }
12508
12509    #[test]
12510    fn validate_edicao_rejects_free_form_non_year() {
12511        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
12512        // `"nightly"` shapes carry no operational meaning on the
12513        // substrate's build-time edition selector. Until this gate
12514        // landed the bare empty-arm check let every such value
12515        // through and broke far from the source caixa.lisp. Peer
12516        // with the shape-predicate cascade
12517        // `validate_repositorio_rejects_missing_colon_separator`
12518        // establishes past its own empty arm.
12519        for ed in ["x", "latest", "nightly", "stable"] {
12520            let c = caixa_with_edicao(Some(ed));
12521            let err = c.validate_edicao().unwrap_err();
12522            assert!(
12523                matches!(err, ManifestError::EdicaoInvalid { .. }),
12524                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12525            );
12526        }
12527    }
12528
12529    #[test]
12530    fn validate_edicao_rejects_trailing_whitespace() {
12531        // Paste-from-doc whitespace footgun. A trailing space in
12532        // the `:edicao` value would silently break the substrate's
12533        // build-time edition match-table lookup at the rendered
12534        // artifact's edition-selector consumer. The shape predicate
12535        // refuses every whitespace byte by construction (any byte
12536        // outside `0-9` fails `is_ascii_digit`). Peer with
12537        // `validate_repositorio_rejects_whitespace`.
12538        let c = caixa_with_edicao(Some("2026 "));
12539        let err = c.validate_edicao().unwrap_err();
12540        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
12541            panic!("expected EdicaoInvalid, got {err:?}");
12542        };
12543        assert_eq!(edicao, "2026 ");
12544    }
12545
12546    #[test]
12547    fn validate_edicao_rejects_leading_whitespace() {
12548        // Symmetric paste-from-doc whitespace footgun on the leading
12549        // boundary — the gate refuses every shape with a non-digit
12550        // byte by construction.
12551        let c = caixa_with_edicao(Some(" 2026"));
12552        let err = c.validate_edicao().unwrap_err();
12553        assert!(
12554            matches!(err, ManifestError::EdicaoInvalid { .. }),
12555            "got {err:?}",
12556        );
12557    }
12558
12559    #[test]
12560    fn validate_edicao_rejects_control_char() {
12561        // Paste-from-multiline-doc CRLF footgun — control characters
12562        // at the value boundary break the substrate's build-time
12563        // edition-selector parser. Peer with
12564        // `validate_repositorio_rejects_control_char`.
12565        let c = caixa_with_edicao(Some("2026\n"));
12566        let err = c.validate_edicao().unwrap_err();
12567        assert!(
12568            matches!(err, ManifestError::EdicaoInvalid { .. }),
12569            "got {err:?}",
12570        );
12571    }
12572
12573    #[test]
12574    fn validate_edicao_rejects_non_ascii_lookalike() {
12575        // Fullwidth-keyboard look-alike footgun — `"2026"` is
12576        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
12577        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
12578        // edition selector wants an ASCII year, and the gate
12579        // refuses every non-ASCII shape by construction (length in
12580        // bytes is 12 ≠ 4, *and* every byte falls outside
12581        // `is_ascii_digit`'s `0-9` range).
12582        let c = caixa_with_edicao(Some("2026"));
12583        let err = c.validate_edicao().unwrap_err();
12584        assert!(
12585            matches!(err, ManifestError::EdicaoInvalid { .. }),
12586            "got {err:?}",
12587        );
12588    }
12589
12590    #[test]
12591    fn validate_edicao_rejects_version_tag_prefix() {
12592        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
12593        // / `"r2026"` are familiar shapes from git-tag / Rust
12594        // edition / release-tag conventions that don't apply to
12595        // the year-shaped edition axis. The shape predicate refuses
12596        // every leading non-digit prefix.
12597        for ed in ["v2026", "e2026", "r2026"] {
12598            let c = caixa_with_edicao(Some(ed));
12599            let err = c.validate_edicao().unwrap_err();
12600            assert!(
12601                matches!(err, ManifestError::EdicaoInvalid { .. }),
12602                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12603            );
12604        }
12605    }
12606
12607    #[test]
12608    fn validate_edicao_rejects_decimal_shape() {
12609        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
12610        // `"2026.0"` are familiar shapes from semver / float
12611        // conventions that don't apply to the year-shaped edition
12612        // axis. The shape predicate refuses every non-digit byte
12613        // (`.` falls outside `is_ascii_digit`).
12614        for ed in ["2026.1", "2026.0", "2026.0.1"] {
12615            let c = caixa_with_edicao(Some(ed));
12616            let err = c.validate_edicao().unwrap_err();
12617            assert!(
12618                matches!(err, ManifestError::EdicaoInvalid { .. }),
12619                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12620            );
12621        }
12622    }
12623
12624    #[test]
12625    fn validate_edicao_rejects_wrong_length_numeric() {
12626        // Wrong-length numeric footgun — `"26"` (truncated) /
12627        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
12628        // (zero-padded too wide) all parse as integers but don't
12629        // name a 4-digit year. The shape predicate refuses every
12630        // value whose length isn't exactly 4 bytes.
12631        for ed in ["26", "202", "20260", "00026", "9"] {
12632            let c = caixa_with_edicao(Some(ed));
12633            let err = c.validate_edicao().unwrap_err();
12634            assert!(
12635                matches!(err, ManifestError::EdicaoInvalid { .. }),
12636                "expected EdicaoInvalid on {ed:?}, got {err:?}",
12637            );
12638        }
12639    }
12640
12641    #[test]
12642    fn validate_edicao_empty_takes_precedence_over_shape() {
12643        // Empty-first cascade pin: the empty `Some("")` surfaces
12644        // the narrower `EdicaoEmpty` not the shape-predicate-
12645        // wrapped `EdicaoInvalid`, mirroring the peer
12646        // `validate_repositorio_empty_takes_precedence_over_shape`
12647        // (`RepositorioEmpty` → `RepositorioInvalid`),
12648        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
12649        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
12650        // cascades. The shape predicate also refuses the empty
12651        // input (defensively — `s.len() != 4`), but the
12652        // manifest-layer empty arm runs first to surface the
12653        // narrower diagnostic verbatim.
12654        let c = caixa_with_edicao(Some(""));
12655        let err = c.validate_edicao().unwrap_err();
12656        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
12657    }
12658
12659    #[test]
12660    fn validate_edicao_template_passes() {
12661        // Round-trip pin: the bare `Caixa::template` shape (which
12662        // carries `:edicao "2026"` verbatim) passes the gate by
12663        // construction. A future template-shape change that
12664        // introduced `(:edicao "")` or a non-year value would
12665        // surface here as a regression. Mirrors the peer
12666        // `validate_licenca_template_passes` pin.
12667        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12668        c.validate_edicao().unwrap();
12669    }
12670
12671    #[test]
12672    fn validate_edicao_diagnostic_names_offending_slot() {
12673        // Diagnostic-shape pin (peer with
12674        // `validate_licenca_diagnostic_names_offending_slot`): the
12675        // error's Display surfaces the `:edicao` slot name verbatim,
12676        // so a `feira lint` run can render the diagnostic without
12677        // re-parsing and the author can grep their caixa.lisp for
12678        // the offending `:edicao` line.
12679        let c = caixa_with_edicao(Some(""));
12680        let rendered = c.validate_edicao().unwrap_err().to_string();
12681        assert!(
12682            rendered.contains(":edicao"),
12683            "diagnostic must name the offending slot: {rendered}",
12684        );
12685    }
12686
12687    #[test]
12688    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
12689        // Diagnostic-shape pin on the shape-predicate arm (peer
12690        // with `validate_repositorio_diagnostic_carries_offending_value`):
12691        // the error's Display surfaces the offending value + slot
12692        // name verbatim, so a `feira lint` run can render the
12693        // diagnostic without re-parsing and the author can grep
12694        // their caixa.lisp for the offending `:edicao` value.
12695        let c = caixa_with_edicao(Some("v2026"));
12696        let rendered = c.validate_edicao().unwrap_err().to_string();
12697        assert!(
12698            rendered.contains(":edicao"),
12699            "diagnostic must name the offending slot: {rendered}",
12700        );
12701        assert!(
12702            rendered.contains("v2026"),
12703            "diagnostic must quote the offending value: {rendered}",
12704        );
12705    }
12706
12707    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
12708
12709    #[test]
12710    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
12711        // The canonical per-`Caixa` `:edicao` language-edition scalar
12712        // pin: [`Caixa::edicao`] must return the `:edicao` typed
12713        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12714        // raw `self.edicao.as_deref()` access across every representative
12715        // value in the accept-set — `None` (the "omit the slot to defer
12716        // to the substrate's default edition" arm every existing
12717        // [`caixa-resolver`] fixture without an `:edicao` line carries),
12718        // `Some("")` (a past-the-guard sentinel that pins the accessor
12719        // doesn't perform a silent `Some("") → None` collapse on the
12720        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
12721        // but the accessor must ship the raw slot verbatim so a
12722        // validate-time gate regression surfaces at any future edition-
12723        // aware consumer's boundary rather than being silently absorbed
12724        // into the substrate's default edition), `Some("2026")` (the
12725        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
12726        // template scaffolds via [`Caixa::template`] and every
12727        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
12728        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
12729        // carries by construction), `Some("2018")` / `Some("2021")` /
12730        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
12731        // peer with Cargo's `[package] edition` grammar every future-
12732        // introduced sibling to `"2026"` will follow), and eight
12733        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
12734        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
12735        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
12736        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
12737        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
12738        // length-numeric, `Some("latest")` free-form-non-year — the
12739        // sentinels pin the accessor doesn't silently absorb the
12740        // refusal cases into a substrate-default-edition fallback).
12741        //
12742        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
12743        // return scalar accessor pin on the substrate primitive —
12744        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
12745        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
12746        // (3f16e2f) pins that opened the "outer [`Caixa`]
12747        // `Option<&str>` scalar" projection pin pattern this pin folds
12748        // on. Sibling in shape to the peer per-`:placement`
12749        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12750        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12751        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12752        // axes, extended onto the outer top-level [`Caixa`] universal-
12753        // axis surface's last unlifted `Option<String>` slot. Pins
12754        // against a future silent detour that returned an owned
12755        // `Option<String>` (which would type-check but silently
12756        // allocate on every accessor call, breaking the zero-cost
12757        // projection every peer sibling accessor carries), a
12758        // `Some("") → None` collapse (which would silently absorb the
12759        // `EdicaoEmpty` refusal case at the accessor boundary and any
12760        // future edition-aware consumer would silently fall back to
12761        // the substrate's default edition on a struct-literal
12762        // `Caixa { edicao: Some(""), .. }`), or a
12763        // `None → Some("2026")` collapse (which would silently reify
12764        // the substrate's default edition at the accessor boundary
12765        // and every downstream consumer keying off the
12766        // `Option::is_none()` discriminator would lose the "author
12767        // omitted the slot" signal).
12768        for edicao in [
12769            None,
12770            Some(""),
12771            Some("2026"),
12772            Some("2018"),
12773            Some("2021"),
12774            Some("2024"),
12775            Some("2026 "),
12776            Some(" 2026"),
12777            Some("2026\n"),
12778            Some("2026"),
12779            Some("v2026"),
12780            Some("2026.1"),
12781            Some("26"),
12782            Some("latest"),
12783        ] {
12784            let c = caixa_with_edicao(edicao);
12785            assert_eq!(
12786                c.edicao(),
12787                edicao,
12788                "Caixa::edicao must return :edicao verbatim (got {:?}, \
12789                 expected {edicao:?})",
12790                c.edicao(),
12791            );
12792            assert_eq!(
12793                c.edicao(),
12794                c.edicao.as_deref(),
12795                "Caixa::edicao must byte-equal the raw \
12796                 `self.edicao.as_deref()` field access across every \
12797                 value in the Option<&str> accept-set",
12798            );
12799        }
12800    }
12801
12802    #[test]
12803    fn validate_edicao_empty_arm_routes_through_accessor() {
12804        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
12805        // must key off [`Caixa::edicao`], not the raw
12806        // `self.edicao.as_deref()` field access. Structurally: a
12807        // `Caixa { edicao: Some(""), .. }` must surface the
12808        // `EdicaoEmpty` refusal exactly, and a
12809        // `Caixa { edicao: Some("2026"), .. }` (the canonical
12810        // 4-digit-ASCII-decimal-year form) must pass validate. The
12811        // pair jointly pins the accessor + validate-gate composition:
12812        // any future silent detour that had the accessor return `None`
12813        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
12814        // would silently absorb the `EdicaoEmpty` refusal at the
12815        // accessor boundary and the validate gate would accept a
12816        // struct-literal `Caixa { edicao: Some(""), .. }` — the
12817        // composition pin catches that at caixa-core build time.
12818        //
12819        // Peer of the [`Caixa::licenca`] (6d5bc28)
12820        // `validate_licenca_empty_arm_routes_through_accessor`,
12821        // [`Caixa::repositorio`] (cc7332d)
12822        // `validate_repositorio_empty_arm_routes_through_accessor`,
12823        // and [`Caixa::descricao`] (3f16e2f)
12824        // `validate_descricao_empty_arm_routes_through_accessor`
12825        // composition pins on the sibling outer top-level [`Caixa`]
12826        // `Option<&str>` universal-axis surface — same "the validate /
12827        // shape-gate predicate must route through the substrate-
12828        // primitive typed dispatch" discipline extended onto the
12829        // fourth and final outer top-level [`Caixa`] universal-axis
12830        // `Option<&str>`-composition surface, closing the accessor-
12831        // composition family.
12832        let c = caixa_with_edicao(Some(""));
12833        assert!(
12834            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
12835            "validate_edicao must reject edicao == Some(\"\") with \
12836             EdicaoEmpty — the accessor and the validate gate must \
12837             route through the same substrate-primitive typed dispatch \
12838             on the :edicao empty arm",
12839        );
12840        let c = caixa_with_edicao(Some("2026"));
12841        assert!(
12842            c.validate_edicao().is_ok(),
12843            "validate_edicao must accept edicao == Some(\"2026\") \
12844             (the canonical 4-digit-ASCII-decimal-year shape)",
12845        );
12846    }
12847
12848    #[test]
12849    fn edicao_projects_option_str_by_borrow() {
12850        // The by-borrow pin: [`Caixa::edicao`] returns
12851        // `Option<&str>` by borrow — the `&str` borrows the underlying
12852        // `String` storage of the `Option<String>` slot and the
12853        // accessor must not allocate a fresh `String` on every call.
12854        // Peer of the [`Caixa::licenca`] (6d5bc28),
12855        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
12856        // (3f16e2f) by-borrow pins on the peer outer top-level
12857        // [`Caixa`] `Option<&str>`-return axes, and of the
12858        // per-`:placement`
12859        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12860        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
12861        // return axis, extended onto the fourth and final outer top-
12862        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12863        // accessor's returned `&str` must borrow from `&self` (the
12864        // returned reference's lifetime is tied to `&self`), and
12865        // calling the accessor twice on the same [`Caixa`] must yield
12866        // the same `Option<&str>` verbatim (idempotent, no side
12867        // effects on `&self`).
12868        //
12869        // Pins against a future silent detour that returned an owned
12870        // `Option<String>` (which would type-check but silently
12871        // allocate on every call, breaking the zero-cost projection
12872        // every peer sibling accessor carries), or a one-arm-only
12873        // accessor that returned a saturating value on some sentinel
12874        // input (breaking the pass-through invariant the sibling
12875        // required-scalar accessors carry).
12876        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
12877            let c = caixa_with_edicao(edicao);
12878            let first = c.edicao();
12879            let second = c.edicao();
12880            assert_eq!(
12881                first, second,
12882                "Caixa::edicao must be idempotent — two successive \
12883                 calls on the same &self must return the same \
12884                 Option<&str>",
12885            );
12886            assert_eq!(
12887                first, edicao,
12888                "Caixa::edicao must return :edicao verbatim by \
12889                 borrow — got {first:?}, expected {edicao:?}",
12890            );
12891        }
12892    }
12893
12894    #[test]
12895    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
12896        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
12897        // label caixa-identity scalar pin: [`Caixa::nome`] must return
12898        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
12899        // the raw field access across every representative value in
12900        // the accept-set — the canonical `"demo"` template baseline
12901        // (the same `feira init`-scaffolded default the sibling
12902        // `validate_nome_accepts_canonical_template` positive-control
12903        // gate pins), plus every sibling per-typed-slot atom accessor's
12904        // canonical positive-arm byte-string (`"catalog"` per
12905        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
12906        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
12907        // `caixa-helm`/`caixa-flux` cross-crate integration-test
12908        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
12909        // canonical example), plus every past-the-guard sentinel for
12910        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
12911        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
12912        // the bare DNS-1123 63-byte cap but overflows the joint
12913        // `lareira-<nome>` chart-name budget the sibling
12914        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
12915        //
12916        // The past-the-guard sentinels pin the accessor doesn't
12917        // silently absorb the refusal cases into a template-derived
12918        // fallback (a future `.nome().is_empty().then(|| "demo")`
12919        // collapse would silently absorb the `NomeEmpty` refusal at
12920        // the accessor boundary and the validate gate would accept a
12921        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
12922        // catches that at caixa-core build time).
12923        //
12924        // First outer top-level [`Caixa`] `&str`-return required-
12925        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
12926        // required-scalar" projection pattern the sibling per-`Caixa`
12927        // `:versao` future lift folds on. Sibling in shape to the peer
12928        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
12929        // required-`String`-carry accessor pin on the sibling per-
12930        // sub-struct required-axis, extended onto the outer top-level
12931        // [`Caixa`] universal-axis required-`String`-carry axis.
12932        for nome in [
12933            "demo",
12934            "catalog",
12935            "cart",
12936            "hello-rio",
12937            "checkout",
12938            "",
12939            "Bad_Name",
12940            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
12941        ] {
12942            let c = caixa_with_nome(nome);
12943            assert_eq!(
12944                c.nome(),
12945                nome,
12946                "Caixa::nome must return :nome verbatim (got {}, \
12947                 expected {nome})",
12948                c.nome(),
12949            );
12950            assert_eq!(
12951                c.nome(),
12952                c.nome.as_str(),
12953                "Caixa::nome must byte-equal the raw .nome field \
12954                 access across every value in the String accept-set",
12955            );
12956        }
12957    }
12958
12959    #[test]
12960    fn validate_nome_empty_arm_routes_through_accessor() {
12961        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
12962        // key off [`Caixa::nome`], not the raw `.nome` field access.
12963        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
12964        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
12965        // template baseline (the peer positive-arm the sibling
12966        // `validate_nome_accepts_canonical_template` gate carves out)
12967        // must pass validate. The pair jointly pins the accessor +
12968        // validate-gate composition: any future silent detour that
12969        // had the accessor return a fresh `"demo"` on the empty arm
12970        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
12971        // would silently absorb the `NomeEmpty` refusal at the
12972        // accessor boundary and the validate gate would accept a
12973        // struct-literal `Caixa { nome: "".into(), .. }` — the
12974        // composition pin catches that at caixa-core build time.
12975        //
12976        // Peer of the sibling per-`Caixa`
12977        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
12978        // / `validate_repositorio_empty_arm_routes_through_accessor`
12979        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
12980        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
12981        // (2641cbd) composition pins on the sibling outer top-level
12982        // [`Caixa`] `Option<&str>` axes — same "the validate /
12983        // shape-gate predicate must route through the substrate-
12984        // primitive typed dispatch" discipline extended onto the peer
12985        // outer top-level [`Caixa`] required-`&str` composition axis.
12986        let c = caixa_with_nome("");
12987        assert!(
12988            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
12989            "validate_nome must reject nome == \"\" with NomeEmpty — \
12990             the accessor and the validate gate must route through the \
12991             same substrate-primitive typed dispatch on the :nome \
12992             empty-arm",
12993        );
12994        let c = caixa_with_nome("demo");
12995        assert!(
12996            c.validate_nome().is_ok(),
12997            "validate_nome must accept nome == \"demo\" (the canonical \
12998             DNS-1123-label template baseline)",
12999        );
13000    }
13001
13002    #[test]
13003    fn nome_projects_str_by_borrow() {
13004        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
13005        // — the `&str` borrows the underlying `String` storage of the
13006        // required `nome` slot and the accessor must not allocate a
13007        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
13008        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
13009        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
13010        // by-borrow pins on the peer outer top-level [`Caixa`]
13011        // `Option<&str>`-return axes, extended onto the first outer
13012        // top-level [`Caixa`] required-`&str`-return axis — the
13013        // accessor's returned `&str` must borrow from `&self` (the
13014        // returned reference's lifetime is tied to `&self`), and
13015        // calling the accessor twice on the same [`Caixa`] must yield
13016        // the same `&str` verbatim (idempotent, no side effects on
13017        // `&self`).
13018        //
13019        // Pins against a future silent detour that returned an owned
13020        // `String` (which would type-check but silently allocate on
13021        // every call, breaking the zero-cost projection every peer
13022        // sibling accessor carries), an accidental
13023        // `.nome.to_lowercase()` detour that returned a fresh
13024        // allocation through an already-DNS-1123-lowercase-only
13025        // string (breaking a future `const fn` regression), or a
13026        // one-arm-only accessor that returned a canonicalized value
13027        // on some sentinel input (breaking the pass-through invariant
13028        // the sibling required-scalar accessors carry).
13029        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
13030            let c = caixa_with_nome(nome);
13031            let first = c.nome();
13032            let second = c.nome();
13033            assert_eq!(
13034                first, second,
13035                "Caixa::nome must be idempotent — two successive calls \
13036                 on the same &self must return the same &str",
13037            );
13038            assert_eq!(
13039                first, nome,
13040                "Caixa::nome must return :nome verbatim by borrow — \
13041                 got {first}, expected {nome}",
13042            );
13043        }
13044    }
13045
13046    #[test]
13047    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
13048        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
13049        // pinned-version scalar pin: [`Caixa::versao`] must return the
13050        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
13051        // raw `.versao` field access across every representative value
13052        // in the accept-set — the canonical `"0.1.0"` template baseline
13053        // (the same `feira init`-scaffolded default the sibling
13054        // `validate_versao_accepts_canonical_template` positive-control
13055        // gate pins), plus every canonical SemVer-2 shape the sibling
13056        // `validate_versao_accepts_canonical_forms` positive-arm sweep
13057        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
13058        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
13059        // `"10.20.30"`), plus every past-the-guard sentinel for the
13060        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
13061        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
13062        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
13063        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
13064        // `"latest"` the docker-tag-shape footgun — the sentinels pin
13065        // the accessor doesn't silently absorb the refusal cases into a
13066        // template-derived fallback like `"0.1.0"`).
13067        //
13068        // The past-the-guard sentinels pin the accessor doesn't silently
13069        // absorb the refusal cases into a template-derived fallback (a
13070        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
13071        // silently absorb the `VersaoEmpty` refusal at the accessor
13072        // boundary and the validate gate would accept a struct-literal
13073        // `Caixa { versao: "".into(), .. }` — the pin catches that at
13074        // caixa-core build time).
13075        //
13076        // Second outer top-level [`Caixa`] `&str`-return required-scalar
13077        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
13078        // scalar" projection pattern the sibling per-`Caixa`
13079        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
13080        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
13081        // (4127bb6) / per-`:children`
13082        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
13083        // / per-`:upgrade-from`
13084        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
13085        // struct `:versao`-shaped `&str`-return accessor pins on the
13086        // sibling per-typed-slot version-carrier axes, extended onto the
13087        // second outer top-level [`Caixa`] universal-axis required-
13088        // `String`-carry axis so the two universal-axis identity-
13089        // carrying scalars every `defcaixa` form supplies (`:nome` +
13090        // `:versao`) share the same "one typed dispatch per axis" pin
13091        // discipline.
13092        for versao in [
13093            "0.1.0",
13094            "0.0.0",
13095            "1.0.0",
13096            "0.2.0-rc.1",
13097            "1.0.0-alpha.0",
13098            "1.0.0+build.42",
13099            "1.0.0-rc.1+build.42",
13100            "10.20.30",
13101            "",
13102            "v0.1.0",
13103            "0.1",
13104            "^0.1",
13105            "0.1.0.0",
13106            "latest",
13107        ] {
13108            let c = caixa_with_versao(versao);
13109            assert_eq!(
13110                c.versao(),
13111                versao,
13112                "Caixa::versao must return :versao verbatim (got {}, \
13113                 expected {versao})",
13114                c.versao(),
13115            );
13116            assert_eq!(
13117                c.versao(),
13118                c.versao.as_str(),
13119                "Caixa::versao must byte-equal the raw .versao field \
13120                 access across every value in the String accept-set",
13121            );
13122        }
13123    }
13124
13125    #[test]
13126    fn validate_versao_empty_arm_routes_through_accessor() {
13127        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
13128        // must key off [`Caixa::versao`], not the raw `.versao` field
13129        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
13130        // surface the `VersaoEmpty` refusal exactly, and the canonical
13131        // `"0.1.0"` template baseline (the peer positive-arm the sibling
13132        // `validate_versao_accepts_canonical_template` gate carves out)
13133        // must pass validate. The pair jointly pins the accessor +
13134        // validate-gate composition: any future silent detour that had
13135        // the accessor return a fresh `"0.1.0"` on the empty arm
13136        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
13137        // would silently absorb the `VersaoEmpty` refusal at the
13138        // accessor boundary and the validate gate would accept a
13139        // struct-literal `Caixa { versao: "".into(), .. }` — the
13140        // composition pin catches that at caixa-core build time.
13141        //
13142        // Peer of the sibling per-`Caixa`
13143        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
13144        // composition pin on the sibling outer top-level [`Caixa`]
13145        // required-`&str` universal-axis surface — same "the validate /
13146        // shape-gate predicate must route through the substrate-
13147        // primitive typed dispatch" discipline extended onto the peer
13148        // outer top-level [`Caixa`] required-`&str` universal-axis
13149        // pinned-version composition axis, closing the second
13150        // coordinate of the "one canonical typed dispatch per per-Caixa
13151        // required-`&str` universal-axis" discipline.
13152        let c = caixa_with_versao("");
13153        assert!(
13154            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
13155            "validate_versao must reject versao == \"\" with VersaoEmpty — \
13156             the accessor and the validate gate must route through the \
13157             same substrate-primitive typed dispatch on the :versao \
13158             empty-arm",
13159        );
13160        let c = caixa_with_versao("0.1.0");
13161        assert!(
13162            c.validate_versao().is_ok(),
13163            "validate_versao must accept versao == \"0.1.0\" (the \
13164             canonical SemVer-2 template baseline)",
13165        );
13166    }
13167
13168    #[test]
13169    fn versao_projects_str_by_borrow() {
13170        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
13171        // — the `&str` borrows the underlying `String` storage of the
13172        // required `versao` slot and the accessor must not allocate a
13173        // fresh `String` on every call. Peer of the [`Caixa::nome`]
13174        // (e6b7d97) by-borrow pin on the sibling outer top-level
13175        // [`Caixa`] required-`&str`-return axis, extended onto the
13176        // second outer top-level [`Caixa`] required-`&str`-return
13177        // universal-axis pinned-version surface — the accessor's
13178        // returned `&str` must borrow from `&self` (the returned
13179        // reference's lifetime is tied to `&self`), and calling the
13180        // accessor twice on the same [`Caixa`] must yield the same
13181        // `&str` verbatim (idempotent, no side effects on `&self`).
13182        //
13183        // Pins against a future silent detour that returned an owned
13184        // `String` (which would type-check but silently allocate on
13185        // every call, breaking the zero-cost projection every peer
13186        // sibling accessor carries), an accidental
13187        // `semver::Version::parse(&self.versao).unwrap().to_string()`
13188        // detour that returned a canonicalized fresh allocation through
13189        // an already-canonical byte-string (breaking a future `const fn`
13190        // regression and silently absorbing the `VersaoInvalid` refusal
13191        // at the accessor boundary), or a one-arm-only accessor that
13192        // returned a canonicalized value on some sentinel input
13193        // (breaking the pass-through invariant the sibling required-
13194        // scalar accessors carry).
13195        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
13196            let c = caixa_with_versao(versao);
13197            let first = c.versao();
13198            let second = c.versao();
13199            assert_eq!(
13200                first, second,
13201                "Caixa::versao must be idempotent — two successive \
13202                 calls on the same &self must return the same &str",
13203            );
13204            assert_eq!(
13205                first, versao,
13206                "Caixa::versao must return :versao verbatim by borrow \
13207                 — got {first}, expected {versao}",
13208            );
13209        }
13210    }
13211
13212    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
13213        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13214        c.kind = kind;
13215        c
13216    }
13217
13218    #[test]
13219    fn kind_returns_kind_variant_verbatim_across_permutations() {
13220        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
13221        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
13222        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
13223        // the raw `.kind` field access across every variant in the
13224        // closed accept-set (`Biblioteca` — the library kind that
13225        // exports lisp forms; `Binario` — the nix-built executable kind
13226        // under `exe/`; `Servico` — the wasm-component daemon kind
13227        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
13228        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
13229        // composition kind).
13230        //
13231        // Pins against a future silent detour that re-derived the kind
13232        // from a peer axis (an accidental fallback to
13233        // `if !servicos.is_empty() { Servico } else if
13234        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
13235        // collapse that read the code-surface / mesh-slot columns into
13236        // the kind discriminator), a variant remap the operator
13237        // authors on one consumer without the other, or a stale-derive
13238        // detour that substituted [`CaixaKind::Biblioteca`] as the
13239        // default when the field held any other variant (which would
13240        // silently collapse the distinction between "author explicitly
13241        // declared `:kind Servico`" and "author declared any other
13242        // kind" every downstream renderer-dispatch site depends on).
13243        //
13244        // First outer top-level [`Caixa`] `Copy`-return required-enum-
13245        // discriminant accessor pin — opens the "outer [`Caixa`]
13246        // `Copy`-return required-discriminant" projection pattern.
13247        // Sibling in shape to the peer per-`:supervisor`
13248        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
13249        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
13250        // (921fe1b), and per-`:children`
13251        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
13252        // `Copy`-return closed-set-enum discriminant accessor pins on
13253        // the sibling nested-spec typed-slot discriminator axes,
13254        // extended here to the outer top-level [`Caixa`] universal-
13255        // axis surface.
13256        for kind in [
13257            CaixaKind::Biblioteca,
13258            CaixaKind::Binario,
13259            CaixaKind::Servico,
13260            CaixaKind::Supervisor,
13261            CaixaKind::Aplicacao,
13262        ] {
13263            let c = caixa_with_kind(kind);
13264            assert_eq!(
13265                c.kind(),
13266                kind,
13267                "Caixa::kind must return :kind verbatim (got {:?}, \
13268                 expected {kind:?})",
13269                c.kind(),
13270            );
13271            assert_eq!(
13272                c.kind(),
13273                c.kind,
13274                "Caixa::kind accessor and .kind field access must \
13275                 byte-equal — the accessor is the substrate-primitive \
13276                 typed dispatch every downstream kind-gate consumer \
13277                 must route through",
13278            );
13279        }
13280    }
13281
13282    #[test]
13283    fn require_kind_reads_through_lifted_kind_accessor() {
13284        // Two-consumer coherence pin: the [`crate::render::require_kind`]
13285        // entry-gate predicate (the canonical two-line
13286        // `require_kind(caixa, Servico)?` prelude every per-Servico /
13287        // per-Aplicacao renderer runs at its entry-point) and the
13288        // sibling [`crate::render::KindMismatch`] error carrier's
13289        // `actual:` field (which names the offending caixa's variant
13290        // in the diagnostic) must both key off the lifted accessor, so
13291        // any future rebrand on the typed slot's reader shape lands at
13292        // exactly one place. Pins the two-site coherence by exercising
13293        // every off-diagonal `(actual, expected)` pair across the
13294        // closed accept-set — the `KindMismatch { actual, expected }`
13295        // surfaced on the mismatch arm must byte-equal the pair the
13296        // accessor returns for each side.
13297        //
13298        // Peer of the sibling per-`:placement`
13299        // `validate_placement_reads_through_lifted_estrategia_accessor`
13300        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
13301        // `Copy`-return discriminant axis — same "the entry-gate
13302        // predicate and the error carrier's `actual:` field must route
13303        // through the substrate-primitive typed dispatch" discipline
13304        // extended onto the outer top-level [`Caixa`] universal-axis
13305        // discriminant surface.
13306        for expected in [
13307            CaixaKind::Biblioteca,
13308            CaixaKind::Binario,
13309            CaixaKind::Servico,
13310            CaixaKind::Supervisor,
13311            CaixaKind::Aplicacao,
13312        ] {
13313            for actual in [
13314                CaixaKind::Biblioteca,
13315                CaixaKind::Binario,
13316                CaixaKind::Servico,
13317                CaixaKind::Supervisor,
13318                CaixaKind::Aplicacao,
13319            ] {
13320                let c = caixa_with_kind(actual);
13321                let result = crate::render::require_kind(&c, expected);
13322                if expected == actual {
13323                    assert!(
13324                        result.is_ok(),
13325                        "require_kind must accept when actual == expected \
13326                         (actual={actual:?}, expected={expected:?})",
13327                    );
13328                } else {
13329                    let err = result.expect_err("require_kind must reject when actual != expected");
13330                    assert_eq!(
13331                        err.actual,
13332                        c.kind(),
13333                        "KindMismatch.actual must byte-equal Caixa::kind() \
13334                         — the error carrier's `actual:` field reads \
13335                         through the lifted accessor",
13336                    );
13337                    assert_eq!(
13338                        err.expected, expected,
13339                        "KindMismatch.expected must byte-equal the \
13340                         expected variant passed to require_kind",
13341                    );
13342                }
13343            }
13344        }
13345    }
13346
13347    #[test]
13348    fn aplicacao_view_kind_gate_routes_through_accessor() {
13349        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
13350        // must key off [`Caixa::kind`], not the raw `.kind` field
13351        // access. Structurally: a `Caixa { kind: X, .. }` for any
13352        // non-`Aplicacao` variant must fold to `None` on the
13353        // `aplicacao_view` composer (the "kind mismatch → no typed
13354        // view" contract every downstream Aplicacao consumer keys off
13355        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
13356        // `Some(_)`. The pair jointly pins the accessor + view-gate
13357        // composition: any future silent detour that had the accessor
13358        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
13359        // input would silently absorb the kind-mismatch case at the
13360        // accessor boundary and every per-Aplicacao renderer would
13361        // silently render a non-Aplicacao caixa's mesh slots — the
13362        // composition pin catches that at caixa-core build time.
13363        //
13364        // Peer of the sibling per-`Caixa`
13365        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
13366        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
13367        // composition pins on the sibling outer top-level [`Caixa`]
13368        // required-`&str` universal-axis surfaces — same "the
13369        // composer / validate gate must route through the substrate-
13370        // primitive typed dispatch" discipline extended onto the
13371        // outer top-level [`Caixa`] `Copy`-return required-
13372        // discriminant composition axis.
13373        for kind in [
13374            CaixaKind::Biblioteca,
13375            CaixaKind::Binario,
13376            CaixaKind::Servico,
13377            CaixaKind::Supervisor,
13378        ] {
13379            let c = caixa_with_kind(kind);
13380            assert!(
13381                c.aplicacao_view().is_none(),
13382                "aplicacao_view must return None on non-Aplicacao \
13383                 kind {kind:?} — the composer's kind-gate must route \
13384                 through Caixa::kind()",
13385            );
13386        }
13387        let c = caixa_with_kind(CaixaKind::Aplicacao);
13388        assert!(
13389            c.aplicacao_view().is_some(),
13390            "aplicacao_view must return Some on kind Aplicacao — \
13391             the composer's kind-gate must accept the matching arm \
13392             through Caixa::kind()",
13393        );
13394    }
13395
13396    #[test]
13397    fn supervisor_view_kind_gate_routes_through_accessor() {
13398        // Composition pin (mirror of the sibling
13399        // `aplicacao_view_kind_gate_routes_through_accessor` on the
13400        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
13401        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
13402        // field access. A `Caixa { kind: X, .. }` for any non-
13403        // `Supervisor` variant must fold to `None` on the
13404        // `supervisor_view` composer, and a `Caixa { kind:
13405        // Supervisor, .. }` must fold to `Some(_)`. Same peer
13406        // composition pin discipline on the second `_view` composer
13407        // axis.
13408        for kind in [
13409            CaixaKind::Biblioteca,
13410            CaixaKind::Binario,
13411            CaixaKind::Servico,
13412            CaixaKind::Aplicacao,
13413        ] {
13414            let c = caixa_with_kind(kind);
13415            assert!(
13416                c.supervisor_view().is_none(),
13417                "supervisor_view must return None on non-Supervisor \
13418                 kind {kind:?} — the composer's kind-gate must route \
13419                 through Caixa::kind()",
13420            );
13421        }
13422        let mut c = caixa_with_kind(CaixaKind::Supervisor);
13423        // A Supervisor caixa needs a strategy + at least one child to
13424        // fold to a Some(_) that also validates; the composer itself
13425        // requires only the kind arm, so bare kind flip is enough to
13426        // pin the `Some(_)` return, but we populate the minimum
13427        // supervisor shape so a future strengthening of the composer
13428        // to reject an empty spec doesn't false-positive this pin.
13429        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
13430        c.children = vec![crate::supervisor::ChildSpec {
13431            caixa: "child".into(),
13432            versao: "^0.1".into(),
13433            restart: crate::supervisor::RestartPolicy::Permanent,
13434        }];
13435        assert!(
13436            c.supervisor_view().is_some(),
13437            "supervisor_view must return Some on kind Supervisor — \
13438             the composer's kind-gate must accept the matching arm \
13439             through Caixa::kind()",
13440        );
13441    }
13442
13443    #[test]
13444    fn kind_projects_by_copy() {
13445        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
13446        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
13447        // `&self` (the returned value is owned, `Copy`-projected from
13448        // the underlying [`CaixaKind`] storage; two calls on the same
13449        // [`Caixa`] must yield byte-equal values). Peer of the peer
13450        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
13451        // `SupervisorSpec::estrategia` / per-`:children`
13452        // `ChildSpec::restart` `Copy`-return discriminant accessor
13453        // pins on the sibling nested-spec typed-slot discriminator
13454        // axes, extended onto the first outer top-level [`Caixa`]
13455        // required-`Copy`-return axis — pins against a future silent
13456        // detour that returned `&CaixaKind` (which would type-check
13457        // but silently constrain every consumer's callsite to a
13458        // borrow-shaped dispatch, breaking the zero-cost `Copy`
13459        // projection every peer sibling accessor carries).
13460        for kind in [
13461            CaixaKind::Biblioteca,
13462            CaixaKind::Binario,
13463            CaixaKind::Servico,
13464            CaixaKind::Supervisor,
13465            CaixaKind::Aplicacao,
13466        ] {
13467            let c = caixa_with_kind(kind);
13468            let first: CaixaKind = c.kind();
13469            let second: CaixaKind = c.kind();
13470            assert_eq!(
13471                first, second,
13472                "Caixa::kind must be idempotent — two successive \
13473                 calls on the same &self must return the same \
13474                 CaixaKind variant",
13475            );
13476            assert_eq!(
13477                first, kind,
13478                "Caixa::kind must return :kind verbatim by Copy — \
13479                 got {first:?}, expected {kind:?}",
13480            );
13481        }
13482    }
13483
13484    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
13485
13486    #[test]
13487    fn autores_returns_autores_slice_verbatim_across_permutations() {
13488        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
13489        // name-list slice pin: [`Caixa::autores`] must return the
13490        // `:autores` typed [`Vec<String>`] list verbatim as a
13491        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
13492        // access across every representative value in the accept-set —
13493        // `[]` (the "no maintainers declared" arm every existing
13494        // fixture without an `:autores` line carries), `[""]` (a past-
13495        // the-guard sentinel that pins the accessor doesn't perform a
13496        // silent `[""] → []` collapse on the empty-entry arm — validate
13497        // rejects `[""]` through `AutorEmpty` but the accessor must
13498        // ship the raw slot verbatim so a validate-time gate regression
13499        // surfaces at the caixa-helm emit boundary rather than being
13500        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
13501        // canonical single-maintainer form every `feira init` template
13502        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
13503        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
13504        // (the canonical RFC-5322 `<name> <email>` form the
13505        // `is_chart_maintainer_name_shape` predicate accepts), and
13506        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
13507        // sentinel — validate rejects through `AutorDuplicate` but the
13508        // accessor must ship the raw slot verbatim).
13509        //
13510        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
13511        // pin on the substrate primitive — opens the "outer [`Caixa`]
13512        // `&[T]` slice" projection pattern the sibling per-`Caixa`
13513        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
13514        // / `:servicos` / `:upgrade-from` / `:children` future lifts
13515        // fold on. Sibling in shape to the peer per-`:supervisor`
13516        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
13517        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
13518        // (a6e18d7), per-`:membros`
13519        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
13520        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
13521        // (0dcc926), and per-`:upgrade-from :instructions`
13522        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
13523        // `&[T]`-return slice accessor pins on the sibling per-M2 /
13524        // per-M3 typed-slot list axes, extended onto the outer top-
13525        // level [`Caixa`] universal-axis surface. Pins against a future
13526        // silent detour that returned an owned `Vec<String>` (which
13527        // would type-check but silently clone on every accessor call,
13528        // breaking the zero-cost projection every peer sibling slice
13529        // accessor carries), a `[""] → []` collapse (which would
13530        // silently absorb the `AutorEmpty` refusal case at the accessor
13531        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
13532        // would silently absorb the `AutorDuplicate` refusal case at
13533        // the accessor boundary and the caixa-helm `maintainers:` fold
13534        // would silently render a dedupped list on a struct-literal
13535        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
13536        for autores in [
13537            vec![],
13538            vec![""],
13539            vec!["pleme-io"],
13540            vec!["alice", "bob"],
13541            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
13542            vec!["pleme-io", "pleme-io"],
13543        ] {
13544            let c = caixa_with_autores(autores.clone());
13545            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
13546            assert_eq!(
13547                c.autores(),
13548                expected.as_slice(),
13549                "Caixa::autores must return :autores verbatim (got {:?}, \
13550                 expected {expected:?})",
13551                c.autores(),
13552            );
13553            assert_eq!(
13554                c.autores(),
13555                c.autores.as_slice(),
13556                "Caixa::autores must byte-equal the raw \
13557                 `self.autores.as_slice()` field access across every \
13558                 value in the Vec<String> accept-set",
13559            );
13560        }
13561    }
13562
13563    #[test]
13564    fn validate_autores_empty_entry_arm_routes_through_accessor() {
13565        // Composition pin: [`Caixa::validate_autores`]'s per-entry
13566        // empty-arm gate must key off [`Caixa::autores`], not the raw
13567        // `&self.autores` field-borrow walk. Structurally: a
13568        // `Caixa { autores: vec!["".into()], .. }` must surface the
13569        // `AutorEmpty` refusal exactly, and a
13570        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
13571        // canonical single-maintainer form) must pass validate. The
13572        // pair jointly pins the accessor + validate-gate composition:
13573        // any future silent detour that had the accessor return an
13574        // empty slice on the `[""]` arm (a
13575        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
13576        // would silently absorb the `AutorEmpty` refusal at the
13577        // accessor boundary and the validate gate would accept a
13578        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
13579        // the composition pin catches that at caixa-core build time.
13580        //
13581        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
13582        // accessor-composition pin
13583        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
13584        // sibling `Option<&str>`-composition axis and the
13585        // per-`:politicas :circuit-breaker`
13586        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
13587        // accessor-composition pin
13588        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
13589        // on the sibling required-`u32`-composition axis — same "the
13590        // validate / shape-gate predicate must route through the
13591        // substrate-primitive typed dispatch" discipline extended onto
13592        // the outer top-level [`Caixa`] universal-axis `&[T]`-
13593        // composition surface.
13594        let c = caixa_with_autores(vec![""]);
13595        assert!(
13596            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
13597            "validate_autores must reject autores == vec![\"\"] with \
13598             AutorEmpty — the accessor and the validate gate must \
13599             route through the same substrate-primitive typed dispatch \
13600             on the :autores per-entry empty arm",
13601        );
13602        let c = caixa_with_autores(vec!["pleme-io"]);
13603        assert!(
13604            c.validate_autores().is_ok(),
13605            "validate_autores must accept autores == vec![\"pleme-io\"] \
13606             (the canonical single-maintainer shape every `feira init` \
13607             template scaffolds)",
13608        );
13609    }
13610
13611    #[test]
13612    fn autores_projects_slice_by_borrow() {
13613        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
13614        // borrow — the returned slice borrows the underlying
13615        // `Vec<String>` storage of the `:autores` slot and the
13616        // accessor must not clone the backing `Vec` on every call.
13617        // Peer of the per-`:membros`
13618        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
13619        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
13620        // (0dcc926) / per-`:placement`
13621        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
13622        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
13623        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
13624        // typed-slot `&[T]`-return axes, extended onto the outer top-
13625        // level [`Caixa`] universal-axis `&[String]` shape — the
13626        // accessor's returned slice must borrow from `&self` (the
13627        // returned reference's lifetime is tied to `&self`), and
13628        // calling the accessor twice on the same [`Caixa`] must yield
13629        // slices that are pointer-equal (the underlying byte-buffer is
13630        // the storage `Vec`'s allocation, not a fresh copy) as well as
13631        // value-equal (idempotent, no side effects on `&self`).
13632        //
13633        // Pins against a future silent detour that returned an owned
13634        // `Vec<String>` (which would type-check but silently clone on
13635        // every call, breaking the zero-cost projection every peer
13636        // sibling slice accessor carries), a `&Vec<String>` return
13637        // (which would leak the backing `Vec`'s grow/push/reserve
13638        // surface no downstream consumer reaches for), or a one-arm-
13639        // only accessor that returned a saturating value on some
13640        // sentinel input (breaking the pass-through invariant the
13641        // sibling slice accessors carry).
13642        for autores in [
13643            vec![],
13644            vec!["pleme-io"],
13645            vec!["alice", "bob"],
13646            vec!["pleme-io", "pleme-io"],
13647        ] {
13648            let c = caixa_with_autores(autores.clone());
13649            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
13650            let first = c.autores();
13651            let second = c.autores();
13652            assert_eq!(
13653                first, second,
13654                "Caixa::autores must be idempotent — two successive \
13655                 calls on the same &self must return the same \
13656                 &[String]",
13657            );
13658            assert_eq!(
13659                first.as_ptr(),
13660                second.as_ptr(),
13661                "Caixa::autores must borrow the underlying Vec<String> \
13662                 storage — two successive calls must return slices \
13663                 with the same backing pointer (a fresh Vec<String> \
13664                 clone would change the pointer on every call)",
13665            );
13666            assert_eq!(
13667                first,
13668                expected.as_slice(),
13669                "Caixa::autores must return :autores verbatim by \
13670                 borrow — got {first:?}, expected {expected:?}",
13671            );
13672        }
13673    }
13674
13675    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
13676
13677    #[test]
13678    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
13679        // The canonical per-`Caixa` `:etiquetas` universal-axis
13680        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
13681        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
13682        // as a `&[String]`, byte-equal to the raw
13683        // `self.etiquetas.as_slice()` access across every representative
13684        // value in the accept-set — `[]` (the "no tags declared" arm
13685        // every existing fixture without an `:etiquetas` line carries),
13686        // `[""]` (a past-the-guard sentinel that pins the accessor
13687        // doesn't perform a silent `[""] → []` collapse on the empty-
13688        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
13689        // but the accessor must ship the raw slot verbatim so a
13690        // validate-time gate regression surfaces at the caixa-helm emit
13691        // boundary rather than being silently absorbed into a keyword-
13692        // drop), `["demo"]` (the canonical single-tag form every
13693        // `feira init` template scaffolds), `["example", "aplicacao",
13694        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
13695        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
13696        // (a past-the-guard duplicate sentinel — validate rejects
13697        // through `EtiquetaDuplicate` but the accessor must ship the
13698        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
13699        // at chart-render time isn't silently promoted into the
13700        // accessor boundary and struct-literal
13701        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
13702        // fixtures continue to expose the duplicate at the accessor).
13703        //
13704        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
13705        // pin on the substrate primitive — folds on the "outer
13706        // [`Caixa`] `&[T]` slice" projection pattern
13707        // `autores_returns_autores_slice_verbatim_across_permutations`
13708        // (b5d813f) opened, sibling in shape and idiom. Pins against a
13709        // future silent detour that returned an owned `Vec<String>`
13710        // (which would type-check but silently clone on every accessor
13711        // call, breaking the zero-cost projection every peer sibling
13712        // slice accessor carries), a `[""] → []` collapse (which would
13713        // silently absorb the `EtiquetaEmpty` refusal case at the
13714        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
13715        // (which would silently absorb the `EtiquetaDuplicate` refusal
13716        // case at the accessor boundary — the caixa-helm chart-render
13717        // `BTreeSet::collect` dedup is downstream of the accessor and
13718        // must not be silently promoted into it).
13719        for etiquetas in [
13720            vec![],
13721            vec![""],
13722            vec!["demo"],
13723            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
13724            vec!["demo", "demo"],
13725        ] {
13726            let c = caixa_with_etiquetas(etiquetas.clone());
13727            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
13728            assert_eq!(
13729                c.etiquetas(),
13730                expected.as_slice(),
13731                "Caixa::etiquetas must return :etiquetas verbatim (got \
13732                 {:?}, expected {expected:?})",
13733                c.etiquetas(),
13734            );
13735            assert_eq!(
13736                c.etiquetas(),
13737                c.etiquetas.as_slice(),
13738                "Caixa::etiquetas must byte-equal the raw \
13739                 `self.etiquetas.as_slice()` field access across every \
13740                 value in the Vec<String> accept-set",
13741            );
13742        }
13743    }
13744
13745    #[test]
13746    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
13747        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
13748        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
13749        // `&self.etiquetas` field-borrow walk. Structurally: a
13750        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
13751        // `EtiquetaEmpty` refusal exactly, and a
13752        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
13753        // single-tag form) must pass validate. The pair jointly pins
13754        // the accessor + validate-gate composition: any future silent
13755        // detour that had the accessor return an empty slice on the
13756        // `[""]` arm (a
13757        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
13758        // silently absorb the `EtiquetaEmpty` refusal at the accessor
13759        // boundary and the validate gate would accept a struct-literal
13760        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
13761        // pin catches that at caixa-core build time.
13762        //
13763        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
13764        // through_accessor` (b5d813f) accessor-composition pin on the
13765        // sibling `&[T]`-composition axis — same "the validate / shape-
13766        // gate predicate must route through the substrate-primitive
13767        // typed dispatch" discipline extended onto the sibling outer
13768        // top-level [`Caixa`] `&[T]`-composition surface.
13769        let c = caixa_with_etiquetas(vec![""]);
13770        assert!(
13771            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
13772            "validate_etiquetas must reject etiquetas == vec![\"\"] \
13773             with EtiquetaEmpty — the accessor and the validate gate \
13774             must route through the same substrate-primitive typed \
13775             dispatch on the :etiquetas per-entry empty arm",
13776        );
13777        let c = caixa_with_etiquetas(vec!["demo"]);
13778        assert!(
13779            c.validate_etiquetas().is_ok(),
13780            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
13781             (the canonical single-tag shape every `feira init` \
13782             template scaffolds)",
13783        );
13784    }
13785
13786    #[test]
13787    fn etiquetas_projects_slice_by_borrow() {
13788        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
13789        // by borrow — the returned slice borrows the underlying
13790        // `Vec<String>` storage of the `:etiquetas` slot and the
13791        // accessor must not clone the backing `Vec` on every call.
13792        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13793        // (b5d813f) by-borrow pin on the sibling outer top-level
13794        // [`Caixa`] `&[String]`-return axis — the accessor's returned
13795        // slice must borrow from `&self` (the returned reference's
13796        // lifetime is tied to `&self`), and calling the accessor twice
13797        // on the same [`Caixa`] must yield slices that are pointer-
13798        // equal (the underlying byte-buffer is the storage `Vec`'s
13799        // allocation, not a fresh copy) as well as value-equal
13800        // (idempotent, no side effects on `&self`).
13801        //
13802        // Pins against a future silent detour that returned an owned
13803        // `Vec<String>` (which would type-check but silently clone on
13804        // every call, breaking the zero-cost projection every peer
13805        // sibling slice accessor carries), a `&Vec<String>` return
13806        // (which would leak the backing `Vec`'s grow/push/reserve
13807        // surface no downstream consumer reaches for), or a one-arm-
13808        // only accessor that returned a saturating value on some
13809        // sentinel input (breaking the pass-through invariant the
13810        // sibling slice accessors carry).
13811        for etiquetas in [
13812            vec![],
13813            vec!["demo"],
13814            vec!["example", "aplicacao", "mesh"],
13815            vec!["demo", "demo"],
13816        ] {
13817            let c = caixa_with_etiquetas(etiquetas.clone());
13818            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
13819            let first = c.etiquetas();
13820            let second = c.etiquetas();
13821            assert_eq!(
13822                first, second,
13823                "Caixa::etiquetas must be idempotent — two successive \
13824                 calls on the same &self must return the same \
13825                 &[String]",
13826            );
13827            assert_eq!(
13828                first.as_ptr(),
13829                second.as_ptr(),
13830                "Caixa::etiquetas must borrow the underlying \
13831                 Vec<String> storage — two successive calls must \
13832                 return slices with the same backing pointer (a fresh \
13833                 Vec<String> clone would change the pointer on every \
13834                 call)",
13835            );
13836            assert_eq!(
13837                first,
13838                expected.as_slice(),
13839                "Caixa::etiquetas must return :etiquetas verbatim by \
13840                 borrow — got {first:?}, expected {expected:?}",
13841            );
13842        }
13843    }
13844
13845    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
13846
13847    #[test]
13848    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
13849        // The canonical per-`Caixa` `:bibliotecas` universal-axis
13850        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
13851        // must return the `:bibliotecas` typed [`Vec<String>`] list
13852        // verbatim as a `&[String]`, byte-equal to the raw
13853        // `self.bibliotecas.as_slice()` access across every
13854        // representative value in the accept-set — `[]` (the "no
13855        // libraries declared" arm every `:kind` other than `Biblioteca`
13856        // + every `Biblioteca` relying on the canonical
13857        // `lib/<nome>.lisp` implicit-default path carries; the
13858        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
13859        // fires exactly on this empty-slot + `Biblioteca`-kind
13860        // combination), `[""]` (a past-the-guard sentinel that pins
13861        // the accessor doesn't perform a silent `[""] → []` collapse
13862        // on the empty-entry arm — validate rejects `[""]` through
13863        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
13864        // must ship the raw slot verbatim so a validate-time gate
13865        // regression surfaces at the `feira build` phase-1 parse
13866        // boundary rather than being silently absorbed into a
13867        // library-drop), `["lib/demo.lisp"]` (the canonical single-
13868        // entry form `Caixa::template` scaffolds and every `feira init`
13869        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
13870        // (the canonical multi-library form the
13871        // `validate_code_paths_accepts_explicit_relative_paths_on_
13872        // every_slot` fixture emits), and `["lib/foo.lisp",
13873        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
13874        // validate rejects through `CodePathDuplicate { slot:
13875        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
13876        // but the accessor must ship the raw slot verbatim so the
13877        // `feira build` `for entry in caixa.bibliotecas()` parse walk
13878        // sees the duplicate at the accessor boundary and struct-
13879        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
13880        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
13881        // the duplicate at the accessor).
13882        //
13883        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
13884        // pin on the substrate primitive — folds on the "outer
13885        // [`Caixa`] `&[T]` slice" projection pattern
13886        // `autores_returns_autores_slice_verbatim_across_permutations`
13887        // (b5d813f) opened and
13888        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
13889        // (78c7d3c) folded on, sibling in shape and idiom. Pins
13890        // against a future silent detour that returned an owned
13891        // `Vec<String>` (which would type-check but silently clone on
13892        // every accessor call, breaking the zero-cost projection
13893        // every peer sibling slice accessor carries), a `[""] → []`
13894        // collapse (which would silently absorb the `CodePathEmpty`
13895        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
13896        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
13897        // would silently absorb the `CodePathDuplicate` refusal case
13898        // at the accessor boundary — the per-slot set-not-multiset
13899        // gate is downstream of the accessor and must not be silently
13900        // promoted into it).
13901        for bibliotecas in [
13902            vec![],
13903            vec![""],
13904            vec!["lib/demo.lisp"],
13905            vec!["lib/demo.lisp", "lib/helpers.lisp"],
13906            vec!["lib/foo.lisp", "lib/foo.lisp"],
13907        ] {
13908            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
13909            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
13910            assert_eq!(
13911                c.bibliotecas(),
13912                expected.as_slice(),
13913                "Caixa::bibliotecas must return :bibliotecas verbatim \
13914                 (got {:?}, expected {expected:?})",
13915                c.bibliotecas(),
13916            );
13917            assert_eq!(
13918                c.bibliotecas(),
13919                c.bibliotecas.as_slice(),
13920                "Caixa::bibliotecas must byte-equal the raw \
13921                 `self.bibliotecas.as_slice()` field access across \
13922                 every value in the Vec<String> accept-set",
13923            );
13924        }
13925    }
13926
13927    #[test]
13928    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
13929        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
13930        // empty-arm gate on the `:bibliotecas` slot must key off
13931        // [`Caixa::bibliotecas`], not a divergent raw
13932        // `&self.bibliotecas` field-borrow walk. Structurally: a
13933        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
13934        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
13935        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
13936        // into()], .. }` (the canonical single-library form
13937        // `Caixa::template` scaffolds) must pass validate. The pair
13938        // jointly pins the accessor + validate-gate composition: any
13939        // future silent detour that had the accessor return an empty
13940        // slice on the `[""]` arm (a `.iter().filter(|s|
13941        // !s.is_empty()).collect()` collapse) would silently absorb
13942        // the `CodePathEmpty` refusal at the accessor boundary and
13943        // the validate gate would accept a struct-literal
13944        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
13945        // composition pin catches that at caixa-core build time.
13946        //
13947        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
13948        // through_accessor` (b5d813f) and
13949        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
13950        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
13951        // composition axes — same "the validate / shape-gate
13952        // predicate must route through the substrate-primitive typed
13953        // dispatch" discipline extended onto the sibling outer top-
13954        // level [`Caixa`] `&[T]`-composition surface. Nominally the
13955        // in-tree `validate_code_paths` production body still keys
13956        // off the internal `[(":bibliotecas", &self.bibliotecas,
13957        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
13958        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
13959        // (the tuple's homogeneous slice-typed shape blocks a per-
13960        // element accessor swap in isolation — a future companion
13961        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
13962        // `&[T]` slice-accessor axis closes that tuple onto the
13963        // triple of typed dispatches as a unit); the composition pin
13964        // catches any future accessor-side silent filter drop against
13965        // that eventual tuple-closure regardless of whether the
13966        // `:bibliotecas` slot is threaded through the accessor or the
13967        // raw field access at the tuple's construction site.
13968        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
13969        assert!(
13970            matches!(
13971                c.validate_code_paths(),
13972                Err(ManifestError::CodePathEmpty {
13973                    slot: ":bibliotecas"
13974                })
13975            ),
13976            "validate_code_paths must reject bibliotecas == vec![\"\"] \
13977             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
13978             accessor and the validate gate must route through the \
13979             same substrate-primitive typed dispatch on the \
13980             :bibliotecas per-entry empty arm",
13981        );
13982        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
13983        assert!(
13984            c.validate_code_paths().is_ok(),
13985            "validate_code_paths must accept bibliotecas == \
13986             vec![\"lib/demo.lisp\"] (the canonical single-library \
13987             shape every `feira init` template scaffolds)",
13988        );
13989    }
13990
13991    #[test]
13992    fn bibliotecas_projects_slice_by_borrow() {
13993        // The by-borrow pin: [`Caixa::bibliotecas`] returns
13994        // `&[String]` by borrow — the returned slice borrows the
13995        // underlying `Vec<String>` storage of the `:bibliotecas` slot
13996        // and the accessor must not clone the backing `Vec` on every
13997        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
13998        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
13999        // by-borrow pins on the sibling outer top-level [`Caixa`]
14000        // `&[String]`-return axes — the accessor's returned slice
14001        // must borrow from `&self` (the returned reference's lifetime
14002        // is tied to `&self`), and calling the accessor twice on the
14003        // same [`Caixa`] must yield slices that are pointer-equal
14004        // (the underlying byte-buffer is the storage `Vec`'s
14005        // allocation, not a fresh copy) as well as value-equal
14006        // (idempotent, no side effects on `&self`).
14007        //
14008        // Pins against a future silent detour that returned an owned
14009        // `Vec<String>` (which would type-check but silently clone on
14010        // every call, breaking the zero-cost projection every peer
14011        // sibling slice accessor carries), a `&Vec<String>` return
14012        // (which would leak the backing `Vec`'s grow/push/reserve
14013        // surface no downstream consumer reaches for), or a one-arm-
14014        // only accessor that returned a saturating value on some
14015        // sentinel input (breaking the pass-through invariant the
14016        // sibling slice accessors carry).
14017        for bibliotecas in [
14018            vec![],
14019            vec!["lib/demo.lisp"],
14020            vec!["lib/demo.lisp", "lib/helpers.lisp"],
14021            vec!["lib/foo.lisp", "lib/foo.lisp"],
14022        ] {
14023            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
14024            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
14025            let first = c.bibliotecas();
14026            let second = c.bibliotecas();
14027            assert_eq!(
14028                first, second,
14029                "Caixa::bibliotecas must be idempotent — two \
14030                 successive calls on the same &self must return the \
14031                 same &[String]",
14032            );
14033            assert_eq!(
14034                first.as_ptr(),
14035                second.as_ptr(),
14036                "Caixa::bibliotecas must borrow the underlying \
14037                 Vec<String> storage — two successive calls must \
14038                 return slices with the same backing pointer (a \
14039                 fresh Vec<String> clone would change the pointer on \
14040                 every call)",
14041            );
14042            assert_eq!(
14043                first,
14044                expected.as_slice(),
14045                "Caixa::bibliotecas must return :bibliotecas verbatim \
14046                 by borrow — got {first:?}, expected {expected:?}",
14047            );
14048        }
14049    }
14050
14051    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
14052
14053    #[test]
14054    fn exe_returns_exe_slice_verbatim_across_permutations() {
14055        // The canonical per-`Caixa` `:exe` universal-axis
14056        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
14057        // must return the `:exe` typed [`Vec<String>`] list verbatim as
14058        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
14059        // access across every representative value in the accept-set —
14060        // `[]` (the "no executable declared" arm every `:kind` other
14061        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
14062        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
14063        // + `Binario`-kind combination), `[""]` (a past-the-guard
14064        // sentinel that pins the accessor doesn't perform a silent
14065        // `[""] → []` collapse on the empty-entry arm — validate rejects
14066        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
14067        // accessor must ship the raw slot verbatim so a validate-time
14068        // gate regression surfaces at the layout / `feira nix` boundary
14069        // rather than being silently absorbed into an executable-drop),
14070        // `["exe/cli"]` (the canonical single-entry Binario form every
14071        // in-tree `caixa_with_code_paths` positive control uses),
14072        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
14073        // form the `validate_code_paths_accepts_explicit_relative_paths_
14074        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
14075        // (a past-the-guard duplicate sentinel — validate rejects
14076        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
14077        // set-not-multiset gate, but the accessor must ship the raw
14078        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
14079        // into(), "exe/cli".into()], .. }` fixtures continue to expose
14080        // the duplicate at the accessor).
14081        //
14082        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
14083        // pin on the substrate primitive — folds on the "outer
14084        // [`Caixa`] `&[T]` slice" projection pattern
14085        // `autores_returns_autores_slice_verbatim_across_permutations`
14086        // (b5d813f) opened,
14087        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14088        // (78c7d3c) folded on, and
14089        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14090        // (8a36c23) closed the universal-axis text-tag family of.
14091        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
14092        // the sibling `:servicos` future lift closes onto. Pins against
14093        // a future silent detour that returned an owned `Vec<String>`
14094        // (which would type-check but silently clone on every accessor
14095        // call, breaking the zero-cost projection every peer sibling
14096        // slice accessor carries), a `[""] → []` collapse (which would
14097        // silently absorb the `CodePathEmpty` refusal case at the
14098        // accessor boundary), or an `["exe/cli", "exe/cli"] →
14099        // ["exe/cli"]` dedup collapse (which would silently absorb the
14100        // `CodePathDuplicate` refusal case at the accessor boundary —
14101        // the per-slot set-not-multiset gate is downstream of the
14102        // accessor and must not be silently promoted into it).
14103        for exe in [
14104            vec![],
14105            vec![""],
14106            vec!["exe/cli"],
14107            vec!["exe/cli", "exe/serve"],
14108            vec!["exe/cli", "exe/cli"],
14109        ] {
14110            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
14111            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
14112            assert_eq!(
14113                c.exe(),
14114                expected.as_slice(),
14115                "Caixa::exe must return :exe verbatim (got {:?}, \
14116                 expected {expected:?})",
14117                c.exe(),
14118            );
14119            assert_eq!(
14120                c.exe(),
14121                c.exe.as_slice(),
14122                "Caixa::exe must byte-equal the raw \
14123                 `self.exe.as_slice()` field access across every value \
14124                 in the Vec<String> accept-set",
14125            );
14126        }
14127    }
14128
14129    #[test]
14130    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
14131        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
14132        // empty-arm gate on the `:exe` slot must key off
14133        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
14134        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
14135        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
14136        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
14137        // (the canonical single-executable form every in-tree
14138        // `caixa_with_code_paths` positive control uses) must pass
14139        // validate. The pair jointly pins the accessor + validate-gate
14140        // composition: any future silent detour that had the accessor
14141        // return an empty slice on the `[""]` arm (a
14142        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
14143        // silently absorb the `CodePathEmpty` refusal at the accessor
14144        // boundary and the validate gate would accept a struct-literal
14145        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
14146        // catches that at caixa-core build time.
14147        //
14148        // Peer of the per-`Caixa`
14149        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
14150        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
14151        // (b5d813f), and
14152        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14153        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
14154        // composition axes — same "the validate / shape-gate predicate
14155        // must route through the substrate-primitive typed dispatch"
14156        // discipline extended onto the sibling outer top-level [`Caixa`]
14157        // `&[T]`-composition surface. Nominally the in-tree
14158        // `validate_code_paths` production body still keys off the
14159        // internal `[(":bibliotecas", &self.bibliotecas,
14160        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
14161        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
14162        // (the tuple's homogeneous slice-typed shape blocks a per-
14163        // element accessor swap in isolation — a future companion lift
14164        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
14165        // accessor axis closes that tuple onto the triple of typed
14166        // dispatches as a unit); the composition pin catches any future
14167        // accessor-side silent filter drop against that eventual tuple-
14168        // closure regardless of whether the `:exe` slot is threaded
14169        // through the accessor or the raw field access at the tuple's
14170        // construction site.
14171        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
14172        assert!(
14173            matches!(
14174                c.validate_code_paths(),
14175                Err(ManifestError::CodePathEmpty { slot: ":exe" })
14176            ),
14177            "validate_code_paths must reject exe == vec![\"\"] \
14178             with CodePathEmpty {{ slot: \":exe\" }} — the \
14179             accessor and the validate gate must route through the \
14180             same substrate-primitive typed dispatch on the \
14181             :exe per-entry empty arm",
14182        );
14183        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
14184        assert!(
14185            c.validate_code_paths().is_ok(),
14186            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
14187             (the canonical single-executable shape every in-tree \
14188             `caixa_with_code_paths` positive control uses)",
14189        );
14190    }
14191
14192    #[test]
14193    fn exe_projects_slice_by_borrow() {
14194        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
14195        // borrow — the returned slice borrows the underlying
14196        // `Vec<String>` storage of the `:exe` slot and the accessor
14197        // must not clone the backing `Vec` on every call. Peer of the
14198        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
14199        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
14200        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
14201        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
14202        // return axes — the accessor's returned slice must borrow from
14203        // `&self` (the returned reference's lifetime is tied to
14204        // `&self`), and calling the accessor twice on the same
14205        // [`Caixa`] must yield slices that are pointer-equal (the
14206        // underlying byte-buffer is the storage `Vec`'s allocation,
14207        // not a fresh copy) as well as value-equal (idempotent, no
14208        // side effects on `&self`).
14209        //
14210        // Pins against a future silent detour that returned an owned
14211        // `Vec<String>` (which would type-check but silently clone on
14212        // every call, breaking the zero-cost projection every peer
14213        // sibling slice accessor carries), a `&Vec<String>` return
14214        // (which would leak the backing `Vec`'s grow/push/reserve
14215        // surface no downstream consumer reaches for), or a one-arm-
14216        // only accessor that returned a saturating value on some
14217        // sentinel input (breaking the pass-through invariant the
14218        // sibling slice accessors carry).
14219        for exe in [
14220            vec![],
14221            vec!["exe/cli"],
14222            vec!["exe/cli", "exe/serve"],
14223            vec!["exe/cli", "exe/cli"],
14224        ] {
14225            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
14226            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
14227            let first = c.exe();
14228            let second = c.exe();
14229            assert_eq!(
14230                first, second,
14231                "Caixa::exe must be idempotent — two successive calls \
14232                 on the same &self must return the same &[String]",
14233            );
14234            assert_eq!(
14235                first.as_ptr(),
14236                second.as_ptr(),
14237                "Caixa::exe must borrow the underlying Vec<String> \
14238                 storage — two successive calls must return slices \
14239                 with the same backing pointer (a fresh Vec<String> \
14240                 clone would change the pointer on every call)",
14241            );
14242            assert_eq!(
14243                first,
14244                expected.as_slice(),
14245                "Caixa::exe must return :exe verbatim by borrow — \
14246                 got {first:?}, expected {expected:?}",
14247            );
14248        }
14249    }
14250
14251    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
14252
14253    #[test]
14254    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
14255        // The canonical per-`Caixa` `:servicos` universal-axis
14256        // ComputeUnit-CR-YAML-entry-path-list slice pin:
14257        // [`Caixa::servicos`] must return the `:servicos` typed
14258        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
14259        // the raw `self.servicos.as_slice()` access across every
14260        // representative value in the accept-set — `[]` (the "no
14261        // ComputeUnit-CR declared" arm every `:kind` other than
14262        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
14263        // `ServicoWithoutServicos` arm-gate fires exactly on this
14264        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
14265        // guard sentinel that pins the accessor doesn't perform a
14266        // silent `[""] → []` collapse on the empty-entry arm — validate
14267        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
14268        // but the accessor must ship the raw slot verbatim so a
14269        // validate-time gate regression surfaces at the layout /
14270        // per-Servico renderer boundary rather than being silently
14271        // absorbed into a component-drop),
14272        // `["servicos/demo.computeunit.yaml"]` (the canonical
14273        // singleton V0-shape every in-tree `caixa_with_code_paths`
14274        // positive control uses; the same shape
14275        // [`crate::require_single_servico`] admits),
14276        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
14277        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
14278        // singularity gate rejects through `ServicoCountMismatch
14279        // { count: 2 }` but the accessor must ship the raw slot
14280        // verbatim so struct-literal `Caixa { servicos: vec![...,
14281        // ...], .. }` fixtures continue to expose the count at the
14282        // accessor), and `["servicos/a.computeunit.yaml",
14283        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
14284        // sentinel — validate rejects through
14285        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
14286        // set-not-multiset gate, but the accessor must ship the raw
14287        // slot verbatim so struct-literal fixtures continue to expose
14288        // the duplicate at the accessor).
14289        //
14290        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
14291        // slice accessor pin on the substrate primitive — folds on the
14292        // "outer [`Caixa`] `&[T]` slice" projection pattern
14293        // `autores_returns_autores_slice_verbatim_across_permutations`
14294        // (b5d813f) opened,
14295        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14296        // (78c7d3c) folded on,
14297        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14298        // (8a36c23) closed the universal-axis text-tag family of, and
14299        // `exe_returns_exe_slice_verbatim_across_permutations`
14300        // (65d9527) opened the foreign-code-slot sub-family of. Closes
14301        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
14302        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
14303        // `:servicos`) now each carries a substrate-canonical slice
14304        // accessor. Pins against a future silent detour that returned
14305        // an owned `Vec<String>` (which would type-check but silently
14306        // clone on every accessor call, breaking the zero-cost
14307        // projection every peer sibling slice accessor carries), a
14308        // `[""] → []` collapse (which would silently absorb the
14309        // `CodePathEmpty` refusal case at the accessor boundary), an
14310        // `[a, a] → [a]` dedup collapse (which would silently absorb
14311        // the `CodePathDuplicate` refusal case at the accessor
14312        // boundary — the per-slot set-not-multiset gate is downstream
14313        // of the accessor and must not be silently promoted into it),
14314        // or a `[a, b] → [a]` singleton collapse (which would silently
14315        // absorb the V0 `ServicoCountMismatch` refusal case at the
14316        // accessor boundary — the V0 singularity gate is downstream of
14317        // the accessor and must not be silently promoted into it).
14318        for servicos in [
14319            vec![],
14320            vec![""],
14321            vec!["servicos/demo.computeunit.yaml"],
14322            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
14323            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
14324        ] {
14325            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
14326            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
14327            assert_eq!(
14328                c.servicos(),
14329                expected.as_slice(),
14330                "Caixa::servicos must return :servicos verbatim (got \
14331                 {:?}, expected {expected:?})",
14332                c.servicos(),
14333            );
14334            assert_eq!(
14335                c.servicos(),
14336                c.servicos.as_slice(),
14337                "Caixa::servicos must byte-equal the raw \
14338                 `self.servicos.as_slice()` field access across every \
14339                 value in the Vec<String> accept-set",
14340            );
14341        }
14342    }
14343
14344    #[test]
14345    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
14346        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
14347        // empty-arm gate on the `:servicos` slot must key off
14348        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
14349        // field-borrow walk. Structurally: a `Caixa { servicos:
14350        // vec!["".into()], .. }` must surface the `CodePathEmpty
14351        // { slot: ":servicos" }` refusal exactly, and a `Caixa
14352        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
14353        // .. }` (the canonical singleton V0-shape every in-tree
14354        // `caixa_with_code_paths` positive control uses) must pass
14355        // validate. The pair jointly pins the accessor + validate-gate
14356        // composition: any future silent detour that had the accessor
14357        // return an empty slice on the `[""]` arm (a `.iter().filter
14358        // (|s| !s.is_empty()).collect()` collapse) would silently
14359        // absorb the `CodePathEmpty` refusal at the accessor boundary
14360        // and the validate gate would accept a struct-literal
14361        // `Caixa { servicos: vec!["".into()], .. }` — the composition
14362        // pin catches that at caixa-core build time.
14363        //
14364        // Peer of the per-`Caixa`
14365        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
14366        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
14367        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
14368        // (b5d813f), and
14369        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14370        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
14371        // composition axes — same "the validate / shape-gate predicate
14372        // must route through the substrate-primitive typed dispatch"
14373        // discipline extended onto the sibling outer top-level
14374        // [`Caixa`] `&[T]`-composition surface, closing the trio of
14375        // code-surface accessor-composition pins on the same axis.
14376        // Nominally the in-tree `validate_code_paths` production body
14377        // still keys off the internal
14378        // `[(":bibliotecas", &self.bibliotecas,
14379        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
14380        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
14381        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
14382        // per-element accessor swap in isolation — a future companion
14383        // lift promotes the tuple's element type to `&[String]` and
14384        // threads the triple of typed dispatches through as a unit);
14385        // the composition pin catches any future accessor-side silent
14386        // filter drop against that eventual tuple-closure regardless
14387        // of whether the `:servicos` slot is threaded through the
14388        // accessor or the raw field access at the tuple's construction
14389        // site.
14390        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
14391        assert!(
14392            matches!(
14393                c.validate_code_paths(),
14394                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
14395            ),
14396            "validate_code_paths must reject servicos == vec![\"\"] \
14397             with CodePathEmpty {{ slot: \":servicos\" }} — the \
14398             accessor and the validate gate must route through the \
14399             same substrate-primitive typed dispatch on the \
14400             :servicos per-entry empty arm",
14401        );
14402        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
14403        assert!(
14404            c.validate_code_paths().is_ok(),
14405            "validate_code_paths must accept servicos == \
14406             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
14407             singleton V0-shape every in-tree `caixa_with_code_paths` \
14408             positive control uses)",
14409        );
14410    }
14411
14412    #[test]
14413    fn servicos_projects_slice_by_borrow() {
14414        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
14415        // borrow — the returned slice borrows the underlying
14416        // `Vec<String>` storage of the `:servicos` slot and the
14417        // accessor must not clone the backing `Vec` on every call.
14418        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14419        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
14420        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
14421        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
14422        // the sibling outer top-level [`Caixa`] `&[String]`-return
14423        // axes — the accessor's returned slice must borrow from
14424        // `&self` (the returned reference's lifetime is tied to
14425        // `&self`), and calling the accessor twice on the same
14426        // [`Caixa`] must yield slices that are pointer-equal (the
14427        // underlying byte-buffer is the storage `Vec`'s allocation,
14428        // not a fresh copy) as well as value-equal (idempotent, no
14429        // side effects on `&self`).
14430        //
14431        // Pins against a future silent detour that returned an owned
14432        // `Vec<String>` (which would type-check but silently clone on
14433        // every call, breaking the zero-cost projection every peer
14434        // sibling slice accessor carries), a `&Vec<String>` return
14435        // (which would leak the backing `Vec`'s grow/push/reserve
14436        // surface no downstream consumer reaches for), or a one-arm-
14437        // only accessor that returned a saturating value on some
14438        // sentinel input (breaking the pass-through invariant the
14439        // sibling slice accessors carry).
14440        for servicos in [
14441            vec![],
14442            vec!["servicos/demo.computeunit.yaml"],
14443            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
14444            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
14445        ] {
14446            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
14447            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
14448            let first = c.servicos();
14449            let second = c.servicos();
14450            assert_eq!(
14451                first, second,
14452                "Caixa::servicos must be idempotent — two successive \
14453                 calls on the same &self must return the same &[String]",
14454            );
14455            assert_eq!(
14456                first.as_ptr(),
14457                second.as_ptr(),
14458                "Caixa::servicos must borrow the underlying \
14459                 Vec<String> storage — two successive calls must \
14460                 return slices with the same backing pointer (a fresh \
14461                 Vec<String> clone would change the pointer on every \
14462                 call)",
14463            );
14464            assert_eq!(
14465                first,
14466                expected.as_slice(),
14467                "Caixa::servicos must return :servicos verbatim by \
14468                 borrow — got {first:?}, expected {expected:?}",
14469            );
14470        }
14471    }
14472
14473    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
14474
14475    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
14476        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14477        c.deps = deps;
14478        c
14479    }
14480
14481    #[test]
14482    fn deps_returns_deps_slice_verbatim_across_permutations() {
14483        // The canonical per-`Caixa` `:deps` universal-axis runtime-
14484        // dependency-declaration-list slice pin: [`Caixa::deps`] must
14485        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
14486        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
14487        // access across every representative value in the accept-set —
14488        // `[]` (the "no runtime deps declared" arm every existing
14489        // fixture without a `:deps` line carries; the
14490        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
14491        // single-entry list (the shape most consumer caixas carry), a
14492        // canonical two-entry list (the multi-dep runtime closure), and
14493        // two past-the-guard sentinels — a `[""]`-`:nome` entry
14494        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
14495        // `NomeInvalid` but the accessor must ship the raw slot
14496        // verbatim) and a `[a, a]` duplicate (validate rejects through
14497        // `DuplicateNome { list: ":deps" }` but the accessor must ship
14498        // the raw slot verbatim so struct-literal fixtures continue to
14499        // expose the duplicate at the accessor).
14500        //
14501        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
14502        // pin on the substrate primitive — opens the outer-`Caixa`
14503        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
14504        // future lift closes on. Peer of the closed outer-`Caixa`
14505        // foreign-code-slot `&[String]` sub-family
14506        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14507        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
14508        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
14509        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
14510        // (`autores_returns_autores_slice_verbatim_across_permutations`
14511        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14512        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
14513        // projection pattern onto a novel element-type axis (`Dep`
14514        // composite vs the prior sibling family's `String` scalar).
14515        // Pins against a future silent detour that returned an owned
14516        // `Vec<Dep>` (which would type-check but silently clone on every
14517        // accessor call, breaking the zero-cost projection every peer
14518        // sibling slice accessor carries), a `[""] → []` collapse (which
14519        // would silently absorb the `NomeEmpty` refusal case at the
14520        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
14521        // would silently absorb the `DuplicateNome` refusal case at the
14522        // accessor boundary).
14523        for deps in [
14524            vec![],
14525            vec![Dep::simple("", "^0.1")],
14526            vec![Dep::simple("caixa-teia", "^0.1")],
14527            vec![
14528                Dep::simple("caixa-teia", "^0.1"),
14529                Dep::simple("caixa-core", "^0.1"),
14530            ],
14531            vec![
14532                Dep::simple("caixa-teia", "^0.1"),
14533                Dep::simple("caixa-teia", "^0.2"),
14534            ],
14535        ] {
14536            let c = caixa_with_deps(deps.clone());
14537            assert_eq!(
14538                c.deps(),
14539                deps.as_slice(),
14540                "Caixa::deps must return :deps verbatim (got {:?}, \
14541                 expected {deps:?})",
14542                c.deps(),
14543            );
14544            assert_eq!(
14545                c.deps(),
14546                c.deps.as_slice(),
14547                "Caixa::deps must element-equal the raw \
14548                 `self.deps.as_slice()` field access across every \
14549                 value in the Vec<Dep> accept-set",
14550            );
14551        }
14552    }
14553
14554    #[test]
14555    fn validate_deps_duplicate_arm_routes_through_accessor() {
14556        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
14557        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
14558        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
14559        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
14560        // "^0.2")], .. }` must surface the `DuplicateNome { list:
14561        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
14562        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
14563        // form) must pass validate. The pair jointly pins the accessor +
14564        // validate-gate composition: any future silent detour that had
14565        // the accessor return a dedupped slice on the `[a, a]` arm (a
14566        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
14567        // would silently absorb the `DuplicateNome` refusal at the
14568        // accessor boundary and the validate gate would accept a
14569        // struct-literal `Caixa` carrying the drift — the composition
14570        // pin catches that at caixa-core build time.
14571        //
14572        // Peer of the per-`Caixa`
14573        // `validate_autores_empty_entry_arm_routes_through_accessor`
14574        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14575        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
14576        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
14577        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
14578        // (611f78b) accessor-composition pins on the sibling `&[T]`-
14579        // composition axes — same "the validate gate must route through
14580        // the substrate-primitive typed dispatch" discipline extended
14581        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
14582        // composition surface, opening the outer-`Caixa` dependency-slot
14583        // arm of the composition-pin family.
14584        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
14585        let err = c.validate_deps().unwrap_err();
14586        assert!(
14587            matches!(
14588                err,
14589                DepError::DuplicateNome { ref nome, list } if nome == "d"
14590                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
14591            ),
14592            "validate_deps must reject deps == \
14593             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
14594             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
14595             accessor and the validate gate must route through the \
14596             same substrate-primitive typed dispatch on the :deps \
14597             within-list duplicate arm (got {err:?})",
14598        );
14599        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
14600        assert!(
14601            c.validate_deps().is_ok(),
14602            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
14603             (the canonical single-entry form)",
14604        );
14605    }
14606
14607    #[test]
14608    fn deps_projects_slice_by_borrow() {
14609        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
14610        // — the returned slice borrows the underlying `Vec<Dep>` storage
14611        // of the `:deps` slot and the accessor must not clone the
14612        // backing `Vec` on every call. Peer of the per-`Caixa`
14613        // `autores_projects_slice_by_borrow` (b5d813f),
14614        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
14615        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
14616        // `exe_projects_slice_by_borrow` (65d9527), and
14617        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
14618        // on the sibling outer top-level [`Caixa`] `&[String]`-return
14619        // axes — the accessor's returned slice must borrow from `&self`
14620        // (the returned reference's lifetime is tied to `&self`), and
14621        // calling the accessor twice on the same [`Caixa`] must yield
14622        // slices that are pointer-equal (the underlying byte-buffer is
14623        // the storage `Vec`'s allocation, not a fresh copy) as well as
14624        // value-equal (idempotent, no side effects on `&self`).
14625        //
14626        // Pins against a future silent detour that returned an owned
14627        // `Vec<Dep>` (which would type-check but silently clone on
14628        // every call), a `&Vec<Dep>` return (which would leak the
14629        // backing `Vec`'s grow/push/reserve surface no downstream
14630        // consumer reaches for), or a one-arm-only accessor that
14631        // returned a saturating value on some sentinel input.
14632        for deps in [
14633            vec![],
14634            vec![Dep::simple("caixa-teia", "^0.1")],
14635            vec![
14636                Dep::simple("caixa-teia", "^0.1"),
14637                Dep::simple("caixa-core", "^0.1"),
14638            ],
14639        ] {
14640            let c = caixa_with_deps(deps.clone());
14641            let first = c.deps();
14642            let second = c.deps();
14643            assert_eq!(
14644                first, second,
14645                "Caixa::deps must be idempotent — two successive calls \
14646                 on the same &self must return the same &[Dep]",
14647            );
14648            assert_eq!(
14649                first.as_ptr(),
14650                second.as_ptr(),
14651                "Caixa::deps must borrow the underlying Vec<Dep> \
14652                 storage — two successive calls must return slices \
14653                 with the same backing pointer (a fresh Vec<Dep> clone \
14654                 would change the pointer on every call)",
14655            );
14656            assert_eq!(
14657                first,
14658                deps.as_slice(),
14659                "Caixa::deps must return :deps verbatim by borrow — \
14660                 got {first:?}, expected {deps:?}",
14661            );
14662        }
14663    }
14664
14665    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
14666
14667    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
14668        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14669        c.deps_dev = deps_dev;
14670        c
14671    }
14672
14673    #[test]
14674    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
14675        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
14676        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
14677        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
14678        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
14679        // access across every representative value in the accept-set —
14680        // `[]` (the "no dev deps declared" arm every existing fixture
14681        // without a `:deps-dev` line carries; the [`Caixa::template`]
14682        // scaffold emits `:deps-dev ()`), a canonical single-entry list
14683        // (the shape most consumer caixas carry — a `tatara-check` dev
14684        // pin), a canonical two-entry list (the multi-dev-dep closure),
14685        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
14686        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
14687        // `NomeInvalid` but the accessor must ship the raw slot
14688        // verbatim) and a `[a, a]` duplicate (validate rejects through
14689        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
14690        // ship the raw slot verbatim so struct-literal fixtures continue
14691        // to expose the duplicate at the accessor).
14692        //
14693        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
14694        // pin on the substrate primitive — closes the outer-`Caixa`
14695        // dependency-slot `&[Dep]` sub-family the sibling
14696        // `deps_returns_deps_slice_verbatim_across_permutations`
14697        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
14698        // slice" projection pattern onto the sibling dev-dep axis —
14699        // pins against a future silent detour that returned an owned
14700        // `Vec<Dep>` (which would type-check but silently clone on every
14701        // accessor call, breaking the zero-cost projection every peer
14702        // sibling slice accessor carries), a `[""] → []` collapse (which
14703        // would silently absorb the `NomeEmpty` refusal case at the
14704        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
14705        // would silently absorb the `DuplicateNome` refusal case at the
14706        // accessor boundary).
14707        for deps_dev in [
14708            vec![],
14709            vec![Dep::simple("", "^0.1")],
14710            vec![Dep::simple("tatara-check", "^0.1")],
14711            vec![
14712                Dep::simple("tatara-check", "^0.1"),
14713                Dep::simple("caixa-lint", "^0.1"),
14714            ],
14715            vec![
14716                Dep::simple("tatara-check", "^0.1"),
14717                Dep::simple("tatara-check", "^0.2"),
14718            ],
14719        ] {
14720            let c = caixa_with_deps_dev(deps_dev.clone());
14721            assert_eq!(
14722                c.deps_dev(),
14723                deps_dev.as_slice(),
14724                "Caixa::deps_dev must return :deps-dev verbatim (got \
14725                 {:?}, expected {deps_dev:?})",
14726                c.deps_dev(),
14727            );
14728            assert_eq!(
14729                c.deps_dev(),
14730                c.deps_dev.as_slice(),
14731                "Caixa::deps_dev must element-equal the raw \
14732                 `self.deps_dev.as_slice()` field access across every \
14733                 value in the Vec<Dep> accept-set",
14734            );
14735        }
14736    }
14737
14738    #[test]
14739    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
14740        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
14741        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
14742        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
14743        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
14744        // Dep::simple("d", "^0.2")], .. }` must surface the
14745        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
14746        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
14747        // canonical single-entry form) must pass validate. The pair
14748        // jointly pins the accessor + validate-gate composition: any
14749        // future silent detour that had the accessor return a dedupped
14750        // slice on the `[a, a]` arm (a
14751        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
14752        // would silently absorb the `DuplicateNome` refusal at the
14753        // accessor boundary and the validate gate would accept a
14754        // struct-literal `Caixa` carrying the drift — the composition
14755        // pin catches that at caixa-core build time.
14756        //
14757        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
14758        // (ad34b4e) on the sibling `:deps` axis — same "the validate
14759        // gate must route through the substrate-primitive typed
14760        // dispatch" discipline folded onto the sibling `:deps-dev`
14761        // axis, closing the two-list dep-graph composition-pin family.
14762        // The `:deps-dev` diagnostic must carry the
14763        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
14764        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
14765        // offending list unambiguously.
14766        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
14767        let err = c.validate_deps().unwrap_err();
14768        assert!(
14769            matches!(
14770                err,
14771                DepError::DuplicateNome { ref nome, list } if nome == "d"
14772                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
14773            ),
14774            "validate_deps must reject deps_dev == \
14775             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
14776             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
14777             accessor and the validate gate must route through the \
14778             same substrate-primitive typed dispatch on the :deps-dev \
14779             within-list duplicate arm (got {err:?})",
14780        );
14781        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
14782        assert!(
14783            c.validate_deps().is_ok(),
14784            "validate_deps must accept deps_dev == \
14785             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
14786        );
14787    }
14788
14789    #[test]
14790    fn deps_dev_projects_slice_by_borrow() {
14791        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
14792        // borrow — the returned slice borrows the underlying `Vec<Dep>`
14793        // storage of the `:deps-dev` slot and the accessor must not
14794        // clone the backing `Vec` on every call. Peer of
14795        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
14796        // `:deps` axis, and of the per-`Caixa`
14797        // `autores_projects_slice_by_borrow` (b5d813f),
14798        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
14799        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
14800        // `exe_projects_slice_by_borrow` (65d9527), and
14801        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
14802        // on the sibling outer top-level [`Caixa`] `&[String]`-return
14803        // axes — the accessor's returned slice must borrow from `&self`
14804        // (the returned reference's lifetime is tied to `&self`), and
14805        // calling the accessor twice on the same [`Caixa`] must yield
14806        // slices that are pointer-equal (the underlying byte-buffer is
14807        // the storage `Vec`'s allocation, not a fresh copy) as well as
14808        // value-equal (idempotent, no side effects on `&self`).
14809        //
14810        // Pins against a future silent detour that returned an owned
14811        // `Vec<Dep>` (which would type-check but silently clone on
14812        // every call), a `&Vec<Dep>` return (which would leak the
14813        // backing `Vec`'s grow/push/reserve surface no downstream
14814        // consumer reaches for), or a one-arm-only accessor that
14815        // returned a saturating value on some sentinel input.
14816        for deps_dev in [
14817            vec![],
14818            vec![Dep::simple("tatara-check", "^0.1")],
14819            vec![
14820                Dep::simple("tatara-check", "^0.1"),
14821                Dep::simple("caixa-lint", "^0.1"),
14822            ],
14823        ] {
14824            let c = caixa_with_deps_dev(deps_dev.clone());
14825            let first = c.deps_dev();
14826            let second = c.deps_dev();
14827            assert_eq!(
14828                first, second,
14829                "Caixa::deps_dev must be idempotent — two successive \
14830                 calls on the same &self must return the same &[Dep]",
14831            );
14832            assert_eq!(
14833                first.as_ptr(),
14834                second.as_ptr(),
14835                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
14836                 storage — two successive calls must return slices \
14837                 with the same backing pointer (a fresh Vec<Dep> clone \
14838                 would change the pointer on every call)",
14839            );
14840            assert_eq!(
14841                first,
14842                deps_dev.as_slice(),
14843                "Caixa::deps_dev must return :deps-dev verbatim by \
14844                 borrow — got {first:?}, expected {deps_dev:?}",
14845            );
14846        }
14847    }
14848
14849    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
14850
14851    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
14852        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14853        c.limits = limits;
14854        c
14855    }
14856
14857    #[test]
14858    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
14859        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
14860        // composite optional-composite-reference-shape pin:
14861        // [`Caixa::limits`] must return the `:limits` typed
14862        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
14863        // reference over the same backing storage the raw
14864        // `self.limits.as_ref()` field access borrows from, byte-equal
14865        // across every representative fixture in the accept-set — the
14866        // author-omitted `None` shape (the "engine-default applies"
14867        // partition every downstream Servico M2 overlay emitter treats
14868        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
14869        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
14870        // per-axis cap is `None`, so the peer M2 overlay emitter's
14871        // `.is_empty()`-gated projection still emits nothing but the
14872        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
14873        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
14874        // fixture (only `:memory` set — the canonical shape most
14875        // memory-heavy Servicos carry), and a fully-populated composite
14876        // (every per-axis cap set — the canonical shape a
14877        // sandboxed-by-default Servico carries).
14878        //
14879        // Pins against a future silent detour that returned a fresh-
14880        // cloned [`LimitsSpec`] copy (which would type-check via the
14881        // `Clone` impl but silently break every downstream caller that
14882        // relied on the reference sharing the composite's backing
14883        // identity), a reference to an operator-resolved overlay (the
14884        // future per-cluster `:limits-overrides` slot — its resolution
14885        // must land at exactly this accessor body, not silently divert
14886        // the raw slot away from a second consumer), a
14887        // `None` → `Some(LimitsSpec::default)` cluster-default
14888        // projection (which would collapse the load-bearing
14889        // "author-omitted `:limits` ⇒ engine-default applies" partition
14890        // the peer [`crate::render::servico_m2_overlay`] emitter and
14891        // the peer [`Caixa::declared_servico_slots`] enumerator both
14892        // read), or an axis-shuffled projection (a future detour that
14893        // swapped `memory` and `fuel` through the accessor would
14894        // silently split the paired [`crate::StandardLayout::verify`]
14895        // per-`:limits` shape gate's traversal input from the peer
14896        // `servico_m2_overlay` emitter's projection input).
14897        //
14898        // First outer top-level [`Caixa`] `Option<&Composite>`-return
14899        // composite-reference accessor pin on the substrate primitive
14900        // — opens the outer-`Caixa` `Option<&Composite>` composite-
14901        // reference projection pattern the sibling `:behavior`
14902        // [`crate::BehaviorSpec`] / `:politicas`
14903        // [`crate::aplicacao::MeshPolicy`] / `:placement`
14904        // [`crate::aplicacao::Placement`] / `:entrada`
14905        // [`crate::aplicacao::Entrada`] future outer-composite lifts
14906        // fold on. Peer of the closed M3 outer-composite family the
14907        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
14908        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
14909        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
14910        // reference accessor pins already carry on the outer
14911        // [`crate::AplicacaoSpec`] altitude — extends the outer-
14912        // accessor byte-equal-projection discipline onto the outer
14913        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
14914        use crate::LimitsSpec;
14915        use std::time::Duration;
14916        let fixtures: Vec<Option<LimitsSpec>> = vec![
14917            None,
14918            Some(LimitsSpec::default()),
14919            Some(LimitsSpec {
14920                memory: Some(64 * 1024 * 1024),
14921                ..Default::default()
14922            }),
14923            Some(LimitsSpec {
14924                memory: Some(64 * 1024 * 1024),
14925                fuel: Some(1_000_000),
14926                wall_clock: Some(Duration::from_secs(30)),
14927                cpu: Some(500),
14928            }),
14929        ];
14930        for limits in fixtures {
14931            let c = caixa_with_limits(limits.clone());
14932            assert_eq!(
14933                c.limits(),
14934                limits.as_ref(),
14935                "Caixa::limits must return :limits verbatim (got {:?}, \
14936                 expected {:?})",
14937                c.limits(),
14938                limits.as_ref(),
14939            );
14940            match (c.limits(), c.limits.as_ref()) {
14941                (Some(a), Some(b)) => assert!(
14942                    std::ptr::eq(a, b),
14943                    "Caixa::limits accessor and self.limits.as_ref() \
14944                     field access must borrow the same backing storage \
14945                     — the accessor is the substrate-primitive typed \
14946                     dispatch every downstream Servico-M2-overlay \
14947                     composite consumer must route through, and a \
14948                     reference-identity split would silently break \
14949                     every consumer that relied on the borrow sharing \
14950                     the composite's storage",
14951                ),
14952                (None, None) => {}
14953                _ => panic!(
14954                    "Caixa::limits presence bit must byte-equal \
14955                     self.limits.is_some() — a presence-bit drift would \
14956                     silently split the paired StandardLayout::verify \
14957                     per-`:limits` shape gate's traversal head from \
14958                     the peer render::servico_m2_overlay M2 overlay \
14959                     emitter's traversal head from the peer \
14960                     Caixa::declared_servico_slots M2 declared-slot \
14961                     enumerator's presence probe",
14962                ),
14963            }
14964            assert_eq!(
14965                c.limits().is_some(),
14966                c.limits.is_some(),
14967                "Caixa::limits().is_some() must byte-equal \
14968                 self.limits.is_some() — a presence-bit drift would \
14969                 silently split every downstream Option<&LimitsSpec> \
14970                 consumer's partition on the engine-default arm",
14971            );
14972        }
14973    }
14974
14975    #[test]
14976    fn declared_servico_slots_limits_arm_routes_through_accessor() {
14977        // Composition pin: [`Caixa::declared_servico_slots`]'s
14978        // `:limits` presence-probe arm must key off [`Caixa::limits`],
14979        // not the raw `self.limits.is_some()` field-probe. Structurally:
14980        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
14981        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
14982        // (the presence bit is `Some`, so the M2 kind-coherence gate
14983        // must surface the slot as "declared" even when every per-axis
14984        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
14985        // push the label (the "author omitted the slot entirely"
14986        // partition). The pair jointly pins the accessor + declared-
14987        // slot enumerator composition: any future silent detour that
14988        // had the accessor collapse `Some(LimitsSpec::default())` to
14989        // `None` (a `.filter(|l| !l.is_empty())` projection) would
14990        // silently absorb the "declared but empty" arm at the
14991        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
14992        // kind-coherence gate would silently accept a
14993        // struct-literal `Caixa` carrying the drift.
14994        //
14995        // Peer of the sibling per-`Caixa`
14996        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
14997        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
14998        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
14999        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
15000        // enumerator gate must route through the substrate-primitive
15001        // typed dispatch" discipline extended onto the outer top-level
15002        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
15003        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
15004        // composition-pin family.
15005        use crate::LimitsSpec;
15006        let c = caixa_with_limits(Some(LimitsSpec::default()));
15007        let slots = c.declared_servico_slots();
15008        assert!(
15009            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
15010            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
15011             when `:limits` is Some (even for LimitsSpec::default()) \
15012             — the accessor and the enumerator gate must route through \
15013             the same substrate-primitive typed dispatch on the outer \
15014             :limits presence bit (got slots={slots:?})",
15015        );
15016        let c = caixa_with_limits(None);
15017        let slots = c.declared_servico_slots();
15018        assert!(
15019            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
15020            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
15021             when `:limits` is None — the author-omitted arm must \
15022             route through the accessor's None-return unchanged (got \
15023             slots={slots:?})",
15024        );
15025    }
15026
15027    #[test]
15028    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
15029        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15030        // per-`:limits` M2 overlay emit arm must key off
15031        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
15032        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
15033        // Some(64 MiB), .. default }), .. }` must surface the
15034        // `M2_KEY_LIMITS` key with the per-axis
15035        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
15036        // limits: Some(LimitsSpec::default()), .. }` must omit the
15037        // key entirely (the `.is_empty()`-gated inner arm elides an
15038        // empty composite even when the outer presence bit is `Some`),
15039        // and a `Caixa { limits: None, .. }` must also omit the key
15040        // (the "author omitted the slot entirely" partition). The
15041        // three-fixture family jointly pins the accessor + M2 overlay
15042        // emitter composition: any future silent detour that had the
15043        // accessor return a fresh-cloned copy on the `Some` arm (a
15044        // `LimitsSpec::clone()` projection) would silently break the
15045        // reference-identity pin the peer per-axis
15046        // `serde_yaml::to_value(limits)` projection reads from.
15047        use crate::LimitsSpec;
15048        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
15049        let c = caixa_with_limits(Some(LimitsSpec {
15050            memory: Some(64 * 1024 * 1024),
15051            ..Default::default()
15052        }));
15053        let overlay = servico_m2_overlay(&c).unwrap();
15054        assert!(
15055            overlay.contains_key(M2_KEY_LIMITS),
15056            "servico_m2_overlay must surface M2_KEY_LIMITS when \
15057             `:limits` carries a non-empty composite — the accessor \
15058             and the M2 overlay emitter must route through the same \
15059             substrate-primitive typed dispatch on the outer :limits \
15060             composite (got overlay={overlay:?})",
15061        );
15062        let c = caixa_with_limits(Some(LimitsSpec::default()));
15063        let overlay = servico_m2_overlay(&c).unwrap();
15064        assert!(
15065            !overlay.contains_key(M2_KEY_LIMITS),
15066            "servico_m2_overlay must omit M2_KEY_LIMITS when \
15067             `:limits` is Some(LimitsSpec::default()) — the empty \
15068             composite's `.is_empty()`-gated inner arm must elide \
15069             the key regardless of the outer presence bit (got \
15070             overlay={overlay:?})",
15071        );
15072        let c = caixa_with_limits(None);
15073        let overlay = servico_m2_overlay(&c).unwrap();
15074        assert!(
15075            !overlay.contains_key(M2_KEY_LIMITS),
15076            "servico_m2_overlay must omit M2_KEY_LIMITS when \
15077             `:limits` is None — the author-omitted arm must route \
15078             through the accessor's None-return unchanged (got \
15079             overlay={overlay:?})",
15080        );
15081    }
15082
15083    #[test]
15084    fn limits_projects_option_ref_by_borrow() {
15085        // The by-borrow pin: [`Caixa::limits`] returns
15086        // `Option<&LimitsSpec>` by borrow — the returned reference
15087        // borrows the underlying `Option<LimitsSpec>` storage of the
15088        // `:limits` slot and the accessor must not clone the backing
15089        // composite on every call. Peer of the sibling
15090        // `deps_projects_slice_by_borrow` (ad34b4e) /
15091        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
15092        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
15093        // extended here to the outer [`Caixa`] `Option<&Composite>`-
15094        // return axis: the accessor's returned reference must borrow
15095        // from `&self` (the returned reference's lifetime is tied to
15096        // `&self`), and calling the accessor twice on the same
15097        // [`Caixa`] must yield references that are pointer-equal (the
15098        // underlying byte-buffer is the storage `LimitsSpec`'s
15099        // allocation, not a fresh copy) as well as value-equal
15100        // (idempotent, no side effects on `&self`).
15101        //
15102        // Pins against a future silent detour that returned an owned
15103        // `LimitsSpec` (which would type-check via the `Clone` impl
15104        // but silently clone on every call), a `&LimitsSpec` panic-
15105        // return on the `None` arm (which would collapse the load-
15106        // bearing `Option` presence-bit into a runtime panic), or a
15107        // one-arm-only accessor that returned a saturating composite
15108        // on some sentinel input.
15109        use crate::LimitsSpec;
15110        use std::time::Duration;
15111        for limits in [
15112            Some(LimitsSpec::default()),
15113            Some(LimitsSpec {
15114                memory: Some(64 * 1024 * 1024),
15115                fuel: Some(1_000_000),
15116                wall_clock: Some(Duration::from_secs(30)),
15117                cpu: Some(500),
15118            }),
15119        ] {
15120            let c = caixa_with_limits(limits.clone());
15121            let first = c.limits().unwrap();
15122            let second = c.limits().unwrap();
15123            assert_eq!(
15124                first, second,
15125                "Caixa::limits must be idempotent — two successive \
15126                 calls on the same &self must return the same \
15127                 &LimitsSpec",
15128            );
15129            assert!(
15130                std::ptr::eq(first, second),
15131                "Caixa::limits must borrow the underlying \
15132                 Option<LimitsSpec> storage — two successive calls \
15133                 must return references with the same backing pointer \
15134                 (a fresh LimitsSpec clone would change the pointer \
15135                 on every call)",
15136            );
15137            assert_eq!(
15138                Some(first),
15139                limits.as_ref(),
15140                "Caixa::limits must return :limits verbatim by borrow \
15141                 — got {first:?}, expected {:?}",
15142                limits.as_ref(),
15143            );
15144        }
15145        let c = caixa_with_limits(None);
15146        assert!(
15147            c.limits().is_none(),
15148            "Caixa::limits must return None when :limits is absent — \
15149             the author-omitted arm must project through the \
15150             accessor's Option::None unchanged",
15151        );
15152    }
15153
15154    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
15155
15156    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
15157        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15158        c.behavior = behavior;
15159        c
15160    }
15161
15162    #[test]
15163    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
15164        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
15165        // composite optional-composite-reference-shape pin:
15166        // [`Caixa::behavior`] must return the `:behavior` typed
15167        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
15168        // reference over the same backing storage the raw
15169        // `self.behavior.as_ref()` field access borrows from, byte-equal
15170        // across every representative fixture in the accept-set — the
15171        // author-omitted `None` shape (the "runtime-default applies"
15172        // partition every downstream Servico M2 overlay emitter treats
15173        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
15174        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
15175        // every per-callback path is `None`, so the peer M2 overlay
15176        // emitter's `.is_empty()`-gated projection still emits nothing
15177        // but the outer presence-bit is `Some`, so
15178        // [`Caixa::declared_servico_slots`] still pushes the
15179        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
15180        // (only `:on-state-change` set — the canonical shape a caixa
15181        // that only wires the hot-upgrade migration path carries), and
15182        // a fully-populated composite (every per-callback path set —
15183        // the canonical shape a fully-instrumented gen_server-shaped
15184        // Servico carries).
15185        //
15186        // Peer of the sibling
15187        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15188        // (b2bd9d7) opening fixture-family + reference-identity +
15189        // presence-bit tetrad pin on the outer top-level [`Caixa`]
15190        // `Option<&Composite>`-return sub-family — extended here to the
15191        // second axis of that sub-family so both of the currently-lifted
15192        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
15193        // `:behavior`) carry the same "byte-equal, borrow-shared,
15194        // presence-bit-preserved" outer-accessor discipline.
15195        //
15196        // Pins against a future silent detour that returned a fresh-
15197        // cloned [`crate::BehaviorSpec`] copy (which would type-check
15198        // via the `Clone` impl but silently break every downstream
15199        // caller that relied on the reference sharing the composite's
15200        // backing identity), a reference to an operator-resolved
15201        // overlay (a future per-cluster `:behavior-overrides` slot —
15202        // its resolution must land at exactly this accessor body, not
15203        // silently divert the raw slot away from a second consumer), a
15204        // `None` → `Some(BehaviorSpec::default)` cluster-default
15205        // projection (which would collapse the load-bearing
15206        // "author-omitted `:behavior` ⇒ runtime-default applies"
15207        // partition the peer [`crate::render::servico_m2_overlay`]
15208        // emitter, the peer [`Caixa::declared_servico_slots`]
15209        // enumerator, and the cross-slot
15210        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
15211        // gate all read), or a callback-shuffled projection (a future
15212        // detour that swapped `on_init` and `on_terminate` through the
15213        // accessor would silently split the paired
15214        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
15215        // traversal input from the peer `servico_m2_overlay` emitter's
15216        // projection input from the cross-slot `:state-change`
15217        // composition gate's traversal input).
15218        use crate::BehaviorSpec;
15219        use std::path::PathBuf;
15220        let fixtures: Vec<Option<BehaviorSpec>> = vec![
15221            None,
15222            Some(BehaviorSpec::default()),
15223            Some(BehaviorSpec {
15224                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15225                ..Default::default()
15226            }),
15227            Some(BehaviorSpec {
15228                on_init: Some(PathBuf::from("lib/init.lisp")),
15229                on_call: Some(PathBuf::from("lib/handlers.lisp")),
15230                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
15231                on_info: Some(PathBuf::from("lib/handlers.lisp")),
15232                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15233                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
15234            }),
15235        ];
15236        for behavior in fixtures {
15237            let c = caixa_with_behavior(behavior.clone());
15238            assert_eq!(
15239                c.behavior(),
15240                behavior.as_ref(),
15241                "Caixa::behavior must return :behavior verbatim (got \
15242                 {:?}, expected {:?})",
15243                c.behavior(),
15244                behavior.as_ref(),
15245            );
15246            match (c.behavior(), c.behavior.as_ref()) {
15247                (Some(a), Some(b)) => assert!(
15248                    std::ptr::eq(a, b),
15249                    "Caixa::behavior accessor and self.behavior.as_ref() \
15250                     field access must borrow the same backing storage \
15251                     — the accessor is the substrate-primitive typed \
15252                     dispatch every downstream Servico-M2-overlay \
15253                     composite consumer must route through, and a \
15254                     reference-identity split would silently break \
15255                     every consumer that relied on the borrow sharing \
15256                     the composite's storage",
15257                ),
15258                (None, None) => {}
15259                _ => panic!(
15260                    "Caixa::behavior presence bit must byte-equal \
15261                     self.behavior.is_some() — a presence-bit drift \
15262                     would silently split the paired \
15263                     StandardLayout::verify per-`:behavior` shape \
15264                     gate's traversal head from the peer \
15265                     render::servico_m2_overlay M2 overlay emitter's \
15266                     traversal head from the cross-slot \
15267                     validate_upgrade_from_against_behavior \
15268                     composition gate's traversal head from the peer \
15269                     Caixa::declared_servico_slots M2 declared-slot \
15270                     enumerator's presence probe",
15271                ),
15272            }
15273            assert_eq!(
15274                c.behavior().is_some(),
15275                c.behavior.is_some(),
15276                "Caixa::behavior().is_some() must byte-equal \
15277                 self.behavior.is_some() — a presence-bit drift would \
15278                 silently split every downstream Option<&BehaviorSpec> \
15279                 consumer's partition on the runtime-default arm",
15280            );
15281        }
15282    }
15283
15284    #[test]
15285    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
15286        // Composition pin: [`Caixa::declared_servico_slots`]'s
15287        // `:behavior` presence-probe arm must key off
15288        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
15289        // field-probe. Structurally: a `Caixa { behavior:
15290        // Some(BehaviorSpec::default()), .. }` must still push
15291        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
15292        // presence bit is `Some`, so the M2 kind-coherence gate must
15293        // surface the slot as "declared" even when every per-callback
15294        // path is unset), and a `Caixa { behavior: None, .. }` must
15295        // NOT push the label (the "author omitted the slot entirely"
15296        // partition). The pair jointly pins the accessor + declared-
15297        // slot enumerator composition: any future silent detour that
15298        // had the accessor collapse `Some(BehaviorSpec::default())`
15299        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
15300        // silently absorb the "declared but empty" arm at the
15301        // accessor boundary and the
15302        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
15303        // kind-coherence gate would silently accept a struct-literal
15304        // `Caixa` carrying the drift.
15305        //
15306        // Peer of the sibling
15307        // `declared_servico_slots_limits_arm_routes_through_accessor`
15308        // (b2bd9d7) composition pin on the sibling `:limits` outer-
15309        // `Option<&LimitsSpec>` arm of the same
15310        // [`Caixa::declared_servico_slots`] M2 declared-slot
15311        // enumerator's traversal — same "the enumerator gate must
15312        // route through the substrate-primitive typed dispatch"
15313        // discipline extended onto the outer top-level [`Caixa`]
15314        // `Option<&BehaviorSpec>`-composition surface.
15315        use crate::BehaviorSpec;
15316        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
15317        let slots = c.declared_servico_slots();
15318        assert!(
15319            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
15320            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
15321             when `:behavior` is Some (even for BehaviorSpec::default()) \
15322             — the accessor and the enumerator gate must route through \
15323             the same substrate-primitive typed dispatch on the outer \
15324             :behavior presence bit (got slots={slots:?})",
15325        );
15326        let c = caixa_with_behavior(None);
15327        let slots = c.declared_servico_slots();
15328        assert!(
15329            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
15330            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
15331             when `:behavior` is None — the author-omitted arm must \
15332             route through the accessor's None-return unchanged (got \
15333             slots={slots:?})",
15334        );
15335    }
15336
15337    #[test]
15338    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
15339        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15340        // per-`:behavior` M2 overlay emit arm must key off
15341        // [`Caixa::behavior`], not the raw `&caixa.behavior`
15342        // field-borrow. Structurally: a `Caixa { behavior:
15343        // Some(BehaviorSpec { on_state_change: Some(...), .. default
15344        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
15345        // per-callback `onStateChange` sub-mapping in the overlay, a
15346        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
15347        // must omit the key entirely (the `.is_empty()`-gated inner
15348        // arm elides an empty composite even when the outer presence
15349        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
15350        // also omit the key (the "author omitted the slot entirely"
15351        // partition). The three-fixture family jointly pins the
15352        // accessor + M2 overlay emitter composition: any future
15353        // silent detour that had the accessor return a fresh-cloned
15354        // copy on the `Some` arm (a `BehaviorSpec::clone()`
15355        // projection) would silently break the reference-identity
15356        // pin the peer per-callback `serde_yaml::to_value(behavior)`
15357        // projection reads from.
15358        //
15359        // Peer of the sibling
15360        // `servico_m2_overlay_limits_arm_routes_through_accessor`
15361        // (b2bd9d7) composition pin on the sibling `:limits` outer-
15362        // `Option<&LimitsSpec>` arm of the same
15363        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
15364        // traversal — same "the emitter must route through the
15365        // substrate-primitive typed dispatch on the outer composite"
15366        // discipline extended onto the outer top-level [`Caixa`]
15367        // `Option<&BehaviorSpec>`-composition surface.
15368        use crate::BehaviorSpec;
15369        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
15370        use std::path::PathBuf;
15371        let c = caixa_with_behavior(Some(BehaviorSpec {
15372            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15373            ..Default::default()
15374        }));
15375        let overlay = servico_m2_overlay(&c).unwrap();
15376        assert!(
15377            overlay.contains_key(M2_KEY_BEHAVIOR),
15378            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
15379             `:behavior` carries a non-empty composite — the accessor \
15380             and the M2 overlay emitter must route through the same \
15381             substrate-primitive typed dispatch on the outer :behavior \
15382             composite (got overlay={overlay:?})",
15383        );
15384        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
15385        let overlay = servico_m2_overlay(&c).unwrap();
15386        assert!(
15387            !overlay.contains_key(M2_KEY_BEHAVIOR),
15388            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
15389             `:behavior` is Some(BehaviorSpec::default()) — the empty \
15390             composite's `.is_empty()`-gated inner arm must elide the \
15391             key regardless of the outer presence bit (got \
15392             overlay={overlay:?})",
15393        );
15394        let c = caixa_with_behavior(None);
15395        let overlay = servico_m2_overlay(&c).unwrap();
15396        assert!(
15397            !overlay.contains_key(M2_KEY_BEHAVIOR),
15398            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
15399             `:behavior` is None — the author-omitted arm must route \
15400             through the accessor's None-return unchanged (got \
15401             overlay={overlay:?})",
15402        );
15403    }
15404
15405    #[test]
15406    fn behavior_projects_option_ref_by_borrow() {
15407        // The by-borrow pin: [`Caixa::behavior`] returns
15408        // `Option<&BehaviorSpec>` by borrow — the returned reference
15409        // borrows the underlying `Option<BehaviorSpec>` storage of the
15410        // `:behavior` slot and the accessor must not clone the backing
15411        // composite on every call. Peer of the sibling
15412        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
15413        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
15414        // return sub-family — extended here to the second axis of the
15415        // same sub-family: the accessor's returned reference must
15416        // borrow from `&self` (the returned reference's lifetime is
15417        // tied to `&self`), and calling the accessor twice on the same
15418        // [`Caixa`] must yield references that are pointer-equal (the
15419        // underlying byte-buffer is the storage `BehaviorSpec`'s
15420        // allocation, not a fresh copy) as well as value-equal
15421        // (idempotent, no side effects on `&self`).
15422        //
15423        // Pins against a future silent detour that returned an owned
15424        // `BehaviorSpec` (which would type-check via the `Clone` impl
15425        // but silently clone on every call), a `&BehaviorSpec` panic-
15426        // return on the `None` arm (which would collapse the load-
15427        // bearing `Option` presence-bit into a runtime panic), or a
15428        // one-arm-only accessor that returned a saturating composite
15429        // on some sentinel input.
15430        use crate::BehaviorSpec;
15431        use std::path::PathBuf;
15432        for behavior in [
15433            Some(BehaviorSpec::default()),
15434            Some(BehaviorSpec {
15435                on_init: Some(PathBuf::from("lib/init.lisp")),
15436                on_call: Some(PathBuf::from("lib/handlers.lisp")),
15437                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
15438                on_info: Some(PathBuf::from("lib/handlers.lisp")),
15439                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
15440                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
15441            }),
15442        ] {
15443            let c = caixa_with_behavior(behavior.clone());
15444            let first = c.behavior().unwrap();
15445            let second = c.behavior().unwrap();
15446            assert_eq!(
15447                first, second,
15448                "Caixa::behavior must be idempotent — two successive \
15449                 calls on the same &self must return the same \
15450                 &BehaviorSpec",
15451            );
15452            assert!(
15453                std::ptr::eq(first, second),
15454                "Caixa::behavior must borrow the underlying \
15455                 Option<BehaviorSpec> storage — two successive calls \
15456                 must return references with the same backing pointer \
15457                 (a fresh BehaviorSpec clone would change the pointer \
15458                 on every call)",
15459            );
15460            assert_eq!(
15461                Some(first),
15462                behavior.as_ref(),
15463                "Caixa::behavior must return :behavior verbatim by \
15464                 borrow — got {first:?}, expected {:?}",
15465                behavior.as_ref(),
15466            );
15467        }
15468        let c = caixa_with_behavior(None);
15469        assert!(
15470            c.behavior().is_none(),
15471            "Caixa::behavior must return None when :behavior is absent \
15472             — the author-omitted arm must project through the \
15473             accessor's Option::None unchanged",
15474        );
15475    }
15476
15477    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
15478
15479    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
15480        use crate::aplicacao::{Membro, WitContract};
15481        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15482        c.kind = CaixaKind::Aplicacao;
15483        c.membros = vec![Membro {
15484            caixa: "a".into(),
15485            versao: "^0.1".into(),
15486        }];
15487        c.contratos = vec![WitContract {
15488            de: "a".into(),
15489            para: "a".into(),
15490            wit: "wasi:http/proxy".into(),
15491            endpoint: Some("/x".into()),
15492            subject: None,
15493            slot: None,
15494        }];
15495        c.politicas = politicas;
15496        c
15497    }
15498
15499    #[test]
15500    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
15501        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
15502        // composite optional-composite-reference-shape pin:
15503        // [`Caixa::politicas`] must return the `:politicas` typed
15504        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
15505        // reference over the same backing storage the raw
15506        // `self.politicas.as_ref()` field access borrows from,
15507        // byte-equal across every representative fixture in the
15508        // accept-set — the author-omitted `None` shape (the "cluster-
15509        // default applies" partition every downstream mesh-artifact
15510        // emitter treats as "emit no `:politicas` overlay"), the
15511        // empty-composite `Some(MeshPolicy { .. default })` shape
15512        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
15513        // per-axis mesh-policy scalar is `None`, so the peer inner
15514        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
15515        // caixa-mesh overlay elides every per-axis emit but the outer
15516        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
15517        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
15518        // single-axis fixture (only `:timeout` set — the canonical
15519        // shape a latency-sensitive Aplicacao carries), and a
15520        // fully-populated composite (every per-axis mesh-policy
15521        // scalar set — the canonical shape a fully-governed
15522        // Aplicacao carries).
15523        //
15524        // Pins against a future silent detour that returned a fresh-
15525        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
15526        // type-check via the `Clone` impl but silently break every
15527        // downstream caller that relied on the reference sharing the
15528        // composite's backing identity), a reference to an operator-
15529        // resolved overlay (the future per-cluster
15530        // `:politicas-overrides` slot — its resolution must land at
15531        // exactly this accessor body, not silently divert the raw
15532        // slot away from the peer [`Caixa::declared_mesh_slots`]
15533        // enumerator's presence probe), a
15534        // `None` → `Some(MeshPolicy::default)` cluster-default
15535        // projection (which would collapse the load-bearing
15536        // "author-omitted `:politicas` ⇒ cluster-default applies"
15537        // partition the peer [`Caixa::declared_mesh_slots`]
15538        // enumerator and the peer [`Caixa::aplicacao_view`]
15539        // Aplicacao-composition seed both read), or an axis-shuffled
15540        // projection (a future detour that swapped `timeout` and
15541        // `retries` through the accessor would silently split the
15542        // paired [`Caixa::aplicacao_view`] seed's fold input from the
15543        // sibling M3 mesh-artifact emitter's projection input).
15544        //
15545        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
15546        // composite-reference accessor pin on the substrate primitive
15547        // — peer of the sibling
15548        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15549        // (b2bd9d7) and
15550        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15551        // (35d8b52) opening tetrad pins on the outer top-level
15552        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15553        // here to the first of the three M3 mesh-slot axes so the
15554        // opening third of the outer `Option<&Composite>` sub-family
15555        // carries the same "byte-equal, borrow-shared, presence-bit-
15556        // preserved" outer-accessor discipline.
15557        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
15558        use std::time::Duration;
15559        let fixtures: Vec<Option<MeshPolicy>> = vec![
15560            None,
15561            Some(MeshPolicy::default()),
15562            Some(MeshPolicy {
15563                timeout: Some(Duration::from_secs(30)),
15564                ..Default::default()
15565            }),
15566            Some(MeshPolicy {
15567                timeout: Some(Duration::from_secs(30)),
15568                retries: Some(3),
15569                circuit_breaker: Some(CircuitBreaker {
15570                    max_failures: 5,
15571                    window: Duration::from_secs(60),
15572                }),
15573                mtls_required: Some(true),
15574                rate_limit: Some(RateLimit {
15575                    rate: 100,
15576                    window: Duration::from_secs(1),
15577                }),
15578            }),
15579        ];
15580        for politicas in fixtures {
15581            let c = caixa_aplicacao_with_politicas(politicas.clone());
15582            assert_eq!(
15583                c.politicas(),
15584                politicas.as_ref(),
15585                "Caixa::politicas must return :politicas verbatim (got \
15586                 {:?}, expected {:?})",
15587                c.politicas(),
15588                politicas.as_ref(),
15589            );
15590            match (c.politicas(), c.politicas.as_ref()) {
15591                (Some(a), Some(b)) => assert!(
15592                    std::ptr::eq(a, b),
15593                    "Caixa::politicas accessor and self.politicas.as_ref() \
15594                     field access must borrow the same backing storage \
15595                     — the accessor is the substrate-primitive typed \
15596                     dispatch every downstream Aplicacao-mesh-overlay \
15597                     composite consumer must route through, and a \
15598                     reference-identity split would silently break \
15599                     every consumer that relied on the borrow sharing \
15600                     the composite's storage",
15601                ),
15602                (None, None) => {}
15603                _ => panic!(
15604                    "Caixa::politicas presence bit must byte-equal \
15605                     self.politicas.is_some() — a presence-bit drift \
15606                     would silently split the paired \
15607                     Caixa::aplicacao_view Aplicacao-composition seed's \
15608                     traversal head from the peer \
15609                     Caixa::declared_mesh_slots M3 declared-slot \
15610                     enumerator's presence probe",
15611                ),
15612            }
15613            assert_eq!(
15614                c.politicas().is_some(),
15615                c.politicas.is_some(),
15616                "Caixa::politicas().is_some() must byte-equal \
15617                 self.politicas.is_some() — a presence-bit drift would \
15618                 silently split every downstream Option<&MeshPolicy> \
15619                 consumer's partition on the cluster-default arm",
15620            );
15621        }
15622    }
15623
15624    #[test]
15625    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
15626        // Composition pin: [`Caixa::declared_mesh_slots`]'s
15627        // `:politicas` presence-probe arm must key off
15628        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
15629        // field-probe. Structurally: a `Caixa { politicas:
15630        // Some(MeshPolicy::default()), .. }` must still push
15631        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
15632        // presence bit is `Some`, so the M3 kind-coherence gate must
15633        // surface the slot as "declared" even when every per-axis
15634        // scalar is unset), and a `Caixa { politicas: None, .. }` must
15635        // NOT push the label (the "author omitted the slot entirely"
15636        // partition). The pair jointly pins the accessor + declared-
15637        // slot enumerator composition: any future silent detour that
15638        // had the accessor collapse `Some(MeshPolicy::default())` to
15639        // `None` (a `.filter(|p| !p.is_empty())` projection) would
15640        // silently absorb the "declared but empty" arm at the
15641        // accessor boundary and the
15642        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
15643        // coherence gate would silently accept a struct-literal
15644        // `Caixa` carrying the drift.
15645        //
15646        // Peer of the sibling
15647        // `declared_servico_slots_limits_arm_routes_through_accessor`
15648        // (b2bd9d7) and
15649        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15650        // (35d8b52) composition pins on the sibling `:limits` /
15651        // `:behavior` outer-`Option<&Composite>` arms of the peer
15652        // [`Caixa::declared_servico_slots`] M2 declared-slot
15653        // enumerator's traversal — same "the enumerator gate must
15654        // route through the substrate-primitive typed dispatch"
15655        // discipline extended onto the outer top-level [`Caixa`] M3
15656        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
15657        // enumerator carries the same routing invariant as its M2
15658        // sibling.
15659        use crate::aplicacao::MeshPolicy;
15660        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
15661        let slots = c.declared_mesh_slots();
15662        assert!(
15663            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
15664            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
15665             when `:politicas` is Some (even for MeshPolicy::default()) \
15666             — the accessor and the enumerator gate must route through \
15667             the same substrate-primitive typed dispatch on the outer \
15668             :politicas presence bit (got slots={slots:?})",
15669        );
15670        let c = caixa_aplicacao_with_politicas(None);
15671        let slots = c.declared_mesh_slots();
15672        assert!(
15673            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
15674            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
15675             when `:politicas` is None — the author-omitted arm must \
15676             route through the accessor's None-return unchanged (got \
15677             slots={slots:?})",
15678        );
15679    }
15680
15681    #[test]
15682    fn aplicacao_view_politicas_arm_folds_through_accessor() {
15683        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
15684        // Aplicacao-composition seed must fold through
15685        // [`Caixa::politicas`], not the raw
15686        // `self.politicas.clone().unwrap_or_default()` field-borrow.
15687        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
15688        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
15689        // must surface a projected [`crate::AplicacaoSpec`] whose
15690        // `politicas().timeout()` field byte-equals the outer
15691        // composite's `timeout` scalar (the fold must project the
15692        // authored composite verbatim), a `Caixa { politicas:
15693        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
15694        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
15695        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
15696        // fold's empty-composite arm collapses to the same default the
15697        // author-omitted arm does), and a `Caixa { politicas: None,
15698        // kind: Aplicacao, .. }` must surface an
15699        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
15700        // [`crate::aplicacao::MeshPolicy::default`] (the "author
15701        // omitted the slot entirely" arm folds through the
15702        // `unwrap_or_default` onto the cluster-default). The triad
15703        // jointly pins the accessor + Aplicacao-composition seed
15704        // composition: any future silent detour that had the accessor
15705        // divert the raw slot away from the seed's fold (an operator-
15706        // resolved overlay's default-fold arm silently differing from
15707        // the raw slot's default-fold arm) would silently split the
15708        // build-time mesh-artifact emission gate from the caixa-mesh
15709        // renderer's Aplicacao-view input at the composition boundary.
15710        use crate::aplicacao::MeshPolicy;
15711        use std::time::Duration;
15712        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
15713            timeout: Some(Duration::from_secs(30)),
15714            ..Default::default()
15715        }));
15716        let view = c.aplicacao_view().unwrap();
15717        assert_eq!(
15718            view.politicas().timeout(),
15719            Some(Duration::from_secs(30)),
15720            "Caixa::aplicacao_view must fold the authored :politicas \
15721             :timeout scalar through the accessor verbatim onto the \
15722             projected AplicacaoSpec — a future silent detour at the \
15723             seed's fold arm would surface here as a projected-scalar \
15724             drift (got {:?})",
15725            view.politicas().timeout(),
15726        );
15727        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
15728        let view = c.aplicacao_view().unwrap();
15729        assert_eq!(
15730            view.politicas(),
15731            &MeshPolicy::default(),
15732            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
15733             through the accessor onto MeshPolicy::default — the empty- \
15734             composite arm collapses to the same default the author- \
15735             omitted arm does (got {:?})",
15736            view.politicas(),
15737        );
15738        let c = caixa_aplicacao_with_politicas(None);
15739        let view = c.aplicacao_view().unwrap();
15740        assert_eq!(
15741            view.politicas(),
15742            &MeshPolicy::default(),
15743            "Caixa::aplicacao_view must fold None through the accessor's \
15744             unwrap_or_default onto MeshPolicy::default — the author- \
15745             omitted arm must route through the accessor's None-return \
15746             unchanged (got {:?})",
15747            view.politicas(),
15748        );
15749    }
15750
15751    #[test]
15752    fn politicas_projects_option_ref_by_borrow() {
15753        // The by-borrow pin: [`Caixa::politicas`] returns
15754        // `Option<&MeshPolicy>` by borrow — the returned reference
15755        // borrows the underlying `Option<MeshPolicy>` storage of the
15756        // `:politicas` slot and the accessor must not clone the
15757        // backing composite on every call. Peer of the sibling
15758        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
15759        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
15760        // pins on the outer top-level [`Caixa`]
15761        // `Option<&Composite>`-return sub-family — extended here to
15762        // the third axis of the same sub-family: the accessor's
15763        // returned reference must borrow from `&self` (the returned
15764        // reference's lifetime is tied to `&self`), and calling the
15765        // accessor twice on the same [`Caixa`] must yield references
15766        // that are pointer-equal (the underlying byte-buffer is the
15767        // storage `MeshPolicy`'s allocation, not a fresh copy) as
15768        // well as value-equal (idempotent, no side effects on
15769        // `&self`).
15770        //
15771        // Pins against a future silent detour that returned an owned
15772        // `MeshPolicy` (which would type-check via the `Clone` impl
15773        // but silently clone on every call), a `&MeshPolicy` panic-
15774        // return on the `None` arm (which would collapse the load-
15775        // bearing `Option` presence-bit into a runtime panic), or a
15776        // one-arm-only accessor that returned a saturating composite
15777        // on some sentinel input.
15778        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
15779        use std::time::Duration;
15780        for politicas in [
15781            Some(MeshPolicy::default()),
15782            Some(MeshPolicy {
15783                timeout: Some(Duration::from_secs(30)),
15784                retries: Some(3),
15785                circuit_breaker: Some(CircuitBreaker {
15786                    max_failures: 5,
15787                    window: Duration::from_secs(60),
15788                }),
15789                mtls_required: Some(true),
15790                rate_limit: Some(RateLimit {
15791                    rate: 100,
15792                    window: Duration::from_secs(1),
15793                }),
15794            }),
15795        ] {
15796            let c = caixa_aplicacao_with_politicas(politicas.clone());
15797            let first = c.politicas().unwrap();
15798            let second = c.politicas().unwrap();
15799            assert_eq!(
15800                first, second,
15801                "Caixa::politicas must be idempotent — two successive \
15802                 calls on the same &self must return the same \
15803                 &MeshPolicy",
15804            );
15805            assert!(
15806                std::ptr::eq(first, second),
15807                "Caixa::politicas must borrow the underlying \
15808                 Option<MeshPolicy> storage — two successive calls \
15809                 must return references with the same backing pointer \
15810                 (a fresh MeshPolicy clone would change the pointer on \
15811                 every call)",
15812            );
15813            assert_eq!(
15814                Some(first),
15815                politicas.as_ref(),
15816                "Caixa::politicas must return :politicas verbatim by \
15817                 borrow — got {first:?}, expected {:?}",
15818                politicas.as_ref(),
15819            );
15820        }
15821        let c = caixa_aplicacao_with_politicas(None);
15822        assert!(
15823            c.politicas().is_none(),
15824            "Caixa::politicas must return None when :politicas is \
15825             absent — the author-omitted arm must project through the \
15826             accessor's Option::None unchanged",
15827        );
15828    }
15829
15830    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
15831
15832    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
15833        use crate::aplicacao::{Membro, WitContract};
15834        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15835        c.kind = CaixaKind::Aplicacao;
15836        c.membros = vec![Membro {
15837            caixa: "a".into(),
15838            versao: "^0.1".into(),
15839        }];
15840        c.contratos = vec![WitContract {
15841            de: "a".into(),
15842            para: "a".into(),
15843            wit: "wasi:http/proxy".into(),
15844            endpoint: Some("/x".into()),
15845            subject: None,
15846            slot: None,
15847        }];
15848        c.placement = placement;
15849        c
15850    }
15851
15852    #[test]
15853    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
15854        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
15855        // composite optional-composite-reference-shape pin:
15856        // [`Caixa::placement`] must return the `:placement` typed
15857        // `Option<Placement>` verbatim as an `Option<&Placement>`
15858        // reference over the same backing storage the raw
15859        // `self.placement.as_ref()` field access borrows from,
15860        // byte-equal across every representative fixture in the
15861        // accept-set — the author-omitted `None` shape (the
15862        // "cluster-default applies" partition every downstream mesh-
15863        // artifact emitter treats as "emit no `:placement` overlay"),
15864        // the empty-composite `Some(Placement { .. default })` shape
15865        // (`estrategia: SingleNode`, empty clusters, no shard-key /
15866        // affinity — the outer presence-bit is `Some` so
15867        // [`Caixa::declared_mesh_slots`] still pushes the
15868        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
15869        // `Replicated`-on-two-clusters fixture (the canonical shape a
15870        // stateless HTTP Aplicacao carries), and a fully-populated
15871        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
15872        // shape a stateful Akka-style cluster-sharding Aplicacao
15873        // carries).
15874        //
15875        // Pins against a future silent detour that returned a fresh-
15876        // cloned [`crate::aplicacao::Placement`] copy (which would
15877        // type-check via the `Clone` impl but silently break every
15878        // downstream caller that relied on the reference sharing the
15879        // composite's backing identity), a reference to an operator-
15880        // resolved overlay (the future per-cluster
15881        // `:placement-overrides` slot — its resolution must land at
15882        // exactly this accessor body, not silently divert the raw
15883        // slot away from the peer [`Caixa::declared_mesh_slots`]
15884        // enumerator's presence probe), a `None` →
15885        // `Some(Placement::default)` cluster-default projection (which
15886        // would collapse the load-bearing "author-omitted `:placement`
15887        // ⇒ cluster-default applies" partition the peer
15888        // [`Caixa::declared_mesh_slots`] enumerator and the peer
15889        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
15890        // read), or an axis-shuffled projection (a future detour that
15891        // swapped `clusters` and `affinity` through the accessor would
15892        // silently split the paired [`Caixa::aplicacao_view`] seed's
15893        // fold input from the sibling M3 mesh-artifact emitter's
15894        // projection input).
15895        //
15896        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
15897        // composite-reference accessor pin on the substrate primitive
15898        // — peer of the sibling
15899        // `limits_returns_limits_option_ref_verbatim_across_permutations`
15900        // (b2bd9d7),
15901        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
15902        // (35d8b52), and
15903        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
15904        // (5d23d29) opening triad pins on the outer top-level
15905        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
15906        // here to the second of the three M3 mesh-slot axes so the
15907        // opening four-fifths of the outer `Option<&Composite>` sub-
15908        // family carries the same "byte-equal, borrow-shared,
15909        // presence-bit-preserved" outer-accessor discipline.
15910        use crate::aplicacao::{Placement, PlacementStrategy};
15911        let fixtures: Vec<Option<Placement>> = vec![
15912            None,
15913            Some(Placement::default()),
15914            Some(Placement {
15915                estrategia: PlacementStrategy::Replicated,
15916                clusters: vec!["rio".into(), "sao-paulo".into()],
15917                affinity: None,
15918                shard_key: None,
15919            }),
15920            Some(Placement {
15921                estrategia: PlacementStrategy::Sharded,
15922                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
15923                affinity: Some("data-locality".into()),
15924                shard_key: Some("$tenantId".into()),
15925            }),
15926        ];
15927        for placement in fixtures {
15928            let c = caixa_aplicacao_with_placement(placement.clone());
15929            assert_eq!(
15930                c.placement(),
15931                placement.as_ref(),
15932                "Caixa::placement must return :placement verbatim (got \
15933                 {:?}, expected {:?})",
15934                c.placement(),
15935                placement.as_ref(),
15936            );
15937            match (c.placement(), c.placement.as_ref()) {
15938                (Some(a), Some(b)) => assert!(
15939                    std::ptr::eq(a, b),
15940                    "Caixa::placement accessor and self.placement.as_ref() \
15941                     field access must borrow the same backing storage \
15942                     — the accessor is the substrate-primitive typed \
15943                     dispatch every downstream Aplicacao-distribution- \
15944                     overlay composite consumer must route through, and \
15945                     a reference-identity split would silently break \
15946                     every consumer that relied on the borrow sharing \
15947                     the composite's storage",
15948                ),
15949                (None, None) => {}
15950                _ => panic!(
15951                    "Caixa::placement presence bit must byte-equal \
15952                     self.placement.is_some() — a presence-bit drift \
15953                     would silently split the paired \
15954                     Caixa::aplicacao_view Aplicacao-composition seed's \
15955                     traversal head from the peer \
15956                     Caixa::declared_mesh_slots M3 declared-slot \
15957                     enumerator's presence probe",
15958                ),
15959            }
15960            assert_eq!(
15961                c.placement().is_some(),
15962                c.placement.is_some(),
15963                "Caixa::placement().is_some() must byte-equal \
15964                 self.placement.is_some() — a presence-bit drift would \
15965                 silently split every downstream Option<&Placement> \
15966                 consumer's partition on the cluster-default arm",
15967            );
15968        }
15969    }
15970
15971    #[test]
15972    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
15973        // Composition pin: [`Caixa::declared_mesh_slots`]'s
15974        // `:placement` presence-probe arm must key off
15975        // [`Caixa::placement`], not the raw `self.placement.is_some()`
15976        // field-probe. Structurally: a `Caixa { placement:
15977        // Some(Placement::default()), .. }` must still push
15978        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
15979        // presence bit is `Some`, so the M3 kind-coherence gate must
15980        // surface the slot as "declared" even when every per-axis
15981        // scalar defers to the cluster-default arm), and a `Caixa {
15982        // placement: None, .. }` must NOT push the label (the "author
15983        // omitted the slot entirely" partition). The pair jointly pins
15984        // the accessor + declared-slot enumerator composition: any
15985        // future silent detour that had the accessor collapse
15986        // `Some(Placement::default())` to `None` (a `.filter(|p|
15987        // p.clusters().is_empty().not())` projection) would silently
15988        // absorb the "declared but empty" arm at the accessor boundary
15989        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
15990        // kind-coherence gate would silently accept a struct-literal
15991        // `Caixa` carrying the drift.
15992        //
15993        // Peer of the sibling
15994        // `declared_servico_slots_limits_arm_routes_through_accessor`
15995        // (b2bd9d7),
15996        // `declared_servico_slots_behavior_arm_routes_through_accessor`
15997        // (35d8b52), and
15998        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
15999        // (5d23d29) composition pins on the sibling `:limits` /
16000        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
16001        // — same "the enumerator gate must route through the
16002        // substrate-primitive typed dispatch" discipline extended onto
16003        // the second of the three M3 mesh-slot axes so the
16004        // [`Caixa::declared_mesh_slots`] enumerator carries the same
16005        // routing invariant on the `:placement` arm as the peer
16006        // `:politicas` arm.
16007        use crate::aplicacao::Placement;
16008        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
16009        let slots = c.declared_mesh_slots();
16010        assert!(
16011            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
16012            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
16013             when `:placement` is Some (even for Placement::default()) \
16014             — the accessor and the enumerator gate must route through \
16015             the same substrate-primitive typed dispatch on the outer \
16016             :placement presence bit (got slots={slots:?})",
16017        );
16018        let c = caixa_aplicacao_with_placement(None);
16019        let slots = c.declared_mesh_slots();
16020        assert!(
16021            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
16022            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
16023             when `:placement` is None — the author-omitted arm must \
16024             route through the accessor's None-return unchanged (got \
16025             slots={slots:?})",
16026        );
16027    }
16028
16029    #[test]
16030    fn aplicacao_view_placement_arm_folds_through_accessor() {
16031        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
16032        // Aplicacao-composition seed must fold through
16033        // [`Caixa::placement`], not the raw
16034        // `self.placement.clone().unwrap_or_default()` field-borrow.
16035        // Structurally: a `Caixa { placement: Some(Placement {
16036        // estrategia: Replicated, clusters: ["rio"], .. default }),
16037        // kind: Aplicacao, .. }` must surface a projected
16038        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
16039        // `placement().clusters()` byte-equal the outer composite's
16040        // authored values (the fold must project the authored
16041        // composite verbatim), a `Caixa { placement:
16042        // Some(Placement::default()), kind: Aplicacao, .. }` must
16043        // surface an [`crate::AplicacaoSpec`] whose `placement()`
16044        // byte-equals [`crate::aplicacao::Placement::default`] (the
16045        // fold's empty-composite arm collapses to the same default
16046        // the author-omitted arm does), and a `Caixa { placement:
16047        // None, kind: Aplicacao, .. }` must surface an
16048        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
16049        // [`crate::aplicacao::Placement::default`] (the "author
16050        // omitted the slot entirely" arm folds through the
16051        // `unwrap_or_default` onto the cluster-default). The triad
16052        // jointly pins the accessor + Aplicacao-composition seed
16053        // composition: any future silent detour that had the accessor
16054        // divert the raw slot away from the seed's fold (an operator-
16055        // resolved overlay's default-fold arm silently differing from
16056        // the raw slot's default-fold arm) would silently split the
16057        // build-time distribution-artifact emission gate from the
16058        // caixa-mesh renderer's Aplicacao-view input at the
16059        // composition boundary.
16060        use crate::aplicacao::{Placement, PlacementStrategy};
16061        let c = caixa_aplicacao_with_placement(Some(Placement {
16062            estrategia: PlacementStrategy::Replicated,
16063            clusters: vec!["rio".into()],
16064            affinity: None,
16065            shard_key: None,
16066        }));
16067        let view = c.aplicacao_view().unwrap();
16068        assert_eq!(
16069            view.placement().estrategia(),
16070            PlacementStrategy::Replicated,
16071            "Caixa::aplicacao_view must fold the authored :placement \
16072             :estrategia scalar through the accessor verbatim onto the \
16073             projected AplicacaoSpec — a future silent detour at the \
16074             seed's fold arm would surface here as a projected-scalar \
16075             drift (got {:?})",
16076            view.placement().estrategia(),
16077        );
16078        assert_eq!(
16079            view.placement().clusters(),
16080            &["rio"],
16081            "Caixa::aplicacao_view must fold the authored :placement \
16082             :clusters list through the accessor verbatim onto the \
16083             projected AplicacaoSpec — a future silent detour at the \
16084             seed's fold arm would surface here as a projected-list \
16085             drift (got {:?})",
16086            view.placement().clusters(),
16087        );
16088        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
16089        let view = c.aplicacao_view().unwrap();
16090        assert_eq!(
16091            view.placement(),
16092            &Placement::default(),
16093            "Caixa::aplicacao_view must fold Some(Placement::default()) \
16094             through the accessor onto Placement::default — the empty- \
16095             composite arm collapses to the same default the author- \
16096             omitted arm does (got {:?})",
16097            view.placement(),
16098        );
16099        let c = caixa_aplicacao_with_placement(None);
16100        let view = c.aplicacao_view().unwrap();
16101        assert_eq!(
16102            view.placement(),
16103            &Placement::default(),
16104            "Caixa::aplicacao_view must fold None through the accessor's \
16105             unwrap_or_default onto Placement::default — the author- \
16106             omitted arm must route through the accessor's None-return \
16107             unchanged (got {:?})",
16108            view.placement(),
16109        );
16110    }
16111
16112    #[test]
16113    fn placement_projects_option_ref_by_borrow() {
16114        // The by-borrow pin: [`Caixa::placement`] returns
16115        // `Option<&Placement>` by borrow — the returned reference
16116        // borrows the underlying `Option<Placement>` storage of the
16117        // `:placement` slot and the accessor must not clone the
16118        // backing composite on every call. Peer of the sibling
16119        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
16120        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
16121        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
16122        // pins on the outer top-level [`Caixa`]
16123        // `Option<&Composite>`-return sub-family — extended here to
16124        // the fourth axis of the same sub-family: the accessor's
16125        // returned reference must borrow from `&self` (the returned
16126        // reference's lifetime is tied to `&self`), and calling the
16127        // accessor twice on the same [`Caixa`] must yield references
16128        // that are pointer-equal (the underlying byte-buffer is the
16129        // storage `Placement`'s allocation, not a fresh copy) as well
16130        // as value-equal (idempotent, no side effects on `&self`).
16131        //
16132        // Pins against a future silent detour that returned an owned
16133        // `Placement` (which would type-check via the `Clone` impl
16134        // but silently clone on every call), a `&Placement` panic-
16135        // return on the `None` arm (which would collapse the load-
16136        // bearing `Option` presence-bit into a runtime panic), or a
16137        // one-arm-only accessor that returned a saturating composite
16138        // on some sentinel input.
16139        use crate::aplicacao::{Placement, PlacementStrategy};
16140        for placement in [
16141            Some(Placement::default()),
16142            Some(Placement {
16143                estrategia: PlacementStrategy::Sharded,
16144                clusters: vec!["rio".into(), "sao-paulo".into()],
16145                affinity: Some("data-locality".into()),
16146                shard_key: Some("$tenantId".into()),
16147            }),
16148        ] {
16149            let c = caixa_aplicacao_with_placement(placement.clone());
16150            let first = c.placement().unwrap();
16151            let second = c.placement().unwrap();
16152            assert_eq!(
16153                first, second,
16154                "Caixa::placement must be idempotent — two successive \
16155                 calls on the same &self must return the same \
16156                 &Placement",
16157            );
16158            assert!(
16159                std::ptr::eq(first, second),
16160                "Caixa::placement must borrow the underlying \
16161                 Option<Placement> storage — two successive calls \
16162                 must return references with the same backing pointer \
16163                 (a fresh Placement clone would change the pointer on \
16164                 every call)",
16165            );
16166            assert_eq!(
16167                Some(first),
16168                placement.as_ref(),
16169                "Caixa::placement must return :placement verbatim by \
16170                 borrow — got {first:?}, expected {:?}",
16171                placement.as_ref(),
16172            );
16173        }
16174        let c = caixa_aplicacao_with_placement(None);
16175        assert!(
16176            c.placement().is_none(),
16177            "Caixa::placement must return None when :placement is \
16178             absent — the author-omitted arm must project through the \
16179             accessor's Option::None unchanged",
16180        );
16181    }
16182
16183    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
16184
16185    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
16186        use crate::aplicacao::{Membro, WitContract};
16187        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16188        c.kind = CaixaKind::Aplicacao;
16189        c.membros = vec![Membro {
16190            caixa: "a".into(),
16191            versao: "^0.1".into(),
16192        }];
16193        c.contratos = vec![WitContract {
16194            de: "a".into(),
16195            para: "a".into(),
16196            wit: "wasi:http/proxy".into(),
16197            endpoint: Some("/x".into()),
16198            subject: None,
16199            slot: None,
16200        }];
16201        c.entrada = entrada;
16202        c
16203    }
16204
16205    #[test]
16206    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
16207        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
16208        // composite optional-composite-reference-shape pin:
16209        // [`Caixa::entrada`] must return the `:entrada` typed
16210        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
16211        // reference over the same backing storage the raw
16212        // `self.entrada.as_ref()` field access borrows from,
16213        // byte-equal across every representative fixture in the
16214        // accept-set — the author-omitted `None` shape (the
16215        // "cluster-internal Aplicacao" partition every downstream
16216        // Gateway-API emitter treats as "emit no listener + no
16217        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
16218        // (empty `paths` — the resolved-paths fallback the peer
16219        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
16220        // onto the substrate catch-all), and a fully-populated
16221        // multi-path-with-non-default-port fixture (the canonical
16222        // shape a public HTTP Aplicacao carries).
16223        //
16224        // Pins against a future silent detour that returned a fresh-
16225        // cloned [`crate::aplicacao::Entrada`] copy (which would
16226        // type-check via the `Clone` impl but silently break every
16227        // downstream caller that relied on the reference sharing the
16228        // composite's backing identity), a reference to an operator-
16229        // resolved overlay (the future per-cluster
16230        // `:entrada-overrides` slot — its resolution must land at
16231        // exactly this accessor body, not silently divert the raw
16232        // slot away from the peer [`Caixa::declared_mesh_slots`]
16233        // enumerator's presence probe), or an axis-shuffled projection
16234        // (a future detour that swapped `host` and `para` through the
16235        // accessor would silently split the paired
16236        // [`Caixa::aplicacao_view`] seed's forward input from the
16237        // sibling M3 gateway-artifact emitter's projection input).
16238        //
16239        // Fifth and final outer top-level [`Caixa`]
16240        // `Option<&Composite>`-return composite-reference accessor pin
16241        // on the substrate primitive — peer of the sibling
16242        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16243        // (b2bd9d7),
16244        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16245        // (35d8b52),
16246        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
16247        // (5d23d29), and
16248        // `placement_returns_placement_option_ref_verbatim_across_permutations`
16249        // (4fb8074) opening tetrad pins on the outer top-level
16250        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16251        // here to the third and final M3 mesh-slot axis so the closed
16252        // outer `Option<&Composite>` sub-family carries the same
16253        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
16254        // accessor discipline across all five arms.
16255        use crate::aplicacao::Entrada;
16256        let fixtures: Vec<Option<Entrada>> = vec![
16257            None,
16258            Some(Entrada {
16259                host: "checkout.quero.cloud".into(),
16260                para: "gateway".into(),
16261                paths: Vec::new(),
16262                port: crate::DEFAULT_SERVICO_PORT,
16263            }),
16264            Some(Entrada {
16265                host: "api.pleme.io".into(),
16266                para: "public-api".into(),
16267                paths: vec!["/v1".into(), "/v2".into()],
16268                port: 8080,
16269            }),
16270        ];
16271        for entrada in fixtures {
16272            let c = caixa_aplicacao_with_entrada(entrada.clone());
16273            assert_eq!(
16274                c.entrada(),
16275                entrada.as_ref(),
16276                "Caixa::entrada must return :entrada verbatim (got \
16277                 {:?}, expected {:?})",
16278                c.entrada(),
16279                entrada.as_ref(),
16280            );
16281            match (c.entrada(), c.entrada.as_ref()) {
16282                (Some(a), Some(b)) => assert!(
16283                    std::ptr::eq(a, b),
16284                    "Caixa::entrada accessor and self.entrada.as_ref() \
16285                     field access must borrow the same backing storage \
16286                     — the accessor is the substrate-primitive typed \
16287                     dispatch every downstream Aplicacao-external- \
16288                     gateway composite consumer must route through, and \
16289                     a reference-identity split would silently break \
16290                     every consumer that relied on the borrow sharing \
16291                     the composite's storage",
16292                ),
16293                (None, None) => {}
16294                _ => panic!(
16295                    "Caixa::entrada presence bit must byte-equal \
16296                     self.entrada.is_some() — a presence-bit drift \
16297                     would silently split the paired \
16298                     Caixa::aplicacao_view Aplicacao-composition seed's \
16299                     traversal head from the peer \
16300                     Caixa::declared_mesh_slots M3 declared-slot \
16301                     enumerator's presence probe",
16302                ),
16303            }
16304            assert_eq!(
16305                c.entrada().is_some(),
16306                c.entrada.is_some(),
16307                "Caixa::entrada().is_some() must byte-equal \
16308                 self.entrada.is_some() — a presence-bit drift would \
16309                 silently split every downstream Option<&Entrada> \
16310                 consumer's partition on the cluster-internal arm",
16311            );
16312        }
16313    }
16314
16315    #[test]
16316    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
16317        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
16318        // presence-probe arm must key off [`Caixa::entrada`], not the
16319        // raw `self.entrada.is_some()` field-probe. Structurally: a
16320        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
16321        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
16322        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
16323        // presence bit is `Some`, so the M3 kind-coherence gate must
16324        // surface the slot as "declared" even when every per-axis
16325        // scalar defers to the substrate catch-all / default port),
16326        // and a `Caixa { entrada: None, .. }` must NOT push the label
16327        // (the "author omitted the slot entirely" partition). The pair
16328        // jointly pins the accessor + declared-slot enumerator
16329        // composition: any future silent detour that had the accessor
16330        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
16331        // `.filter(|e| !e.paths.is_empty())` projection) would silently
16332        // absorb the "declared but empty-paths" arm at the accessor
16333        // boundary and the
16334        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16335        // coherence gate would silently accept a struct-literal
16336        // `Caixa` carrying the drift.
16337        //
16338        // Peer of the sibling
16339        // `declared_servico_slots_limits_arm_routes_through_accessor`
16340        // (b2bd9d7),
16341        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16342        // (35d8b52),
16343        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
16344        // (5d23d29), and
16345        // `declared_mesh_slots_placement_arm_routes_through_accessor`
16346        // (4fb8074) composition pins on the sibling `:limits` /
16347        // `:behavior` / `:politicas` / `:placement` outer-
16348        // `Option<&Composite>` arms — same "the enumerator gate must
16349        // route through the substrate-primitive typed dispatch"
16350        // discipline extended onto the third and final M3 mesh-slot
16351        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
16352        // carries the routing invariant on every M3 mesh-slot arm.
16353        use crate::aplicacao::Entrada;
16354        let c = caixa_aplicacao_with_entrada(Some(Entrada {
16355            host: "checkout.quero.cloud".into(),
16356            para: "gateway".into(),
16357            paths: Vec::new(),
16358            port: crate::DEFAULT_SERVICO_PORT,
16359        }));
16360        let slots = c.declared_mesh_slots();
16361        assert!(
16362            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
16363            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
16364             `:entrada` is Some (even for empty-paths / default-port) \
16365             — the accessor and the enumerator gate must route through \
16366             the same substrate-primitive typed dispatch on the outer \
16367             :entrada presence bit (got slots={slots:?})",
16368        );
16369        let c = caixa_aplicacao_with_entrada(None);
16370        let slots = c.declared_mesh_slots();
16371        assert!(
16372            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
16373            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
16374             when `:entrada` is None — the author-omitted arm must \
16375             route through the accessor's None-return unchanged (got \
16376             slots={slots:?})",
16377        );
16378    }
16379
16380    #[test]
16381    fn aplicacao_view_entrada_arm_folds_through_accessor() {
16382        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
16383        // Aplicacao-composition seed must fold through
16384        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
16385        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
16386        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
16387        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
16388        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
16389        // equals the outer composite's authored value (the fold must
16390        // project the authored composite verbatim), and a `Caixa {
16391        // entrada: None, kind: Aplicacao, .. }` must surface an
16392        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
16393        // "author omitted the slot entirely" arm folds through the
16394        // accessor's `Option::cloned` onto the same `None` presence
16395        // bit — unlike the peer `:politicas` / `:placement` arms
16396        // `:entrada` has no cluster-default fold, the omitted arm
16397        // stays omitted). The pair jointly pins the accessor +
16398        // Aplicacao-composition seed composition: any future silent
16399        // detour that had the accessor divert the raw slot away from
16400        // the seed's fold (an operator-resolved overlay's forward arm
16401        // silently differing from the raw slot's forward arm) would
16402        // silently split the build-time gateway-artifact emission gate
16403        // from the caixa-mesh renderer's Aplicacao-view input at the
16404        // composition boundary.
16405        use crate::aplicacao::Entrada;
16406        let authored = Entrada {
16407            host: "api.pleme.io".into(),
16408            para: "public-api".into(),
16409            paths: vec!["/v1".into()],
16410            port: 8080,
16411        };
16412        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
16413        let view = c.aplicacao_view().unwrap();
16414        assert_eq!(
16415            view.entrada(),
16416            Some(&authored),
16417            "Caixa::aplicacao_view must fold the authored :entrada \
16418             composite through the accessor verbatim onto the \
16419             projected AplicacaoSpec — a future silent detour at the \
16420             seed's fold arm would surface here as a projected- \
16421             composite drift (got {:?})",
16422            view.entrada(),
16423        );
16424        let c = caixa_aplicacao_with_entrada(None);
16425        let view = c.aplicacao_view().unwrap();
16426        assert!(
16427            view.entrada().is_none(),
16428            "Caixa::aplicacao_view must fold None through the \
16429             accessor's Option::cloned onto None — the author- \
16430             omitted arm must route through the accessor's None-return \
16431             unchanged (got {:?})",
16432            view.entrada(),
16433        );
16434    }
16435
16436    #[test]
16437    fn entrada_projects_option_ref_by_borrow() {
16438        // The by-borrow pin: [`Caixa::entrada`] returns
16439        // `Option<&Entrada>` by borrow — the returned reference
16440        // borrows the underlying `Option<Entrada>` storage of the
16441        // `:entrada` slot and the accessor must not clone the backing
16442        // composite on every call. Peer of the sibling
16443        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
16444        // `behavior_projects_option_ref_by_borrow` (35d8b52),
16445        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
16446        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
16447        // borrow pins on the outer top-level [`Caixa`]
16448        // `Option<&Composite>`-return sub-family — extended here to
16449        // the fifth and final axis of the same sub-family, closing
16450        // the discipline: the accessor's returned reference must
16451        // borrow from `&self` (the returned reference's lifetime is
16452        // tied to `&self`), and calling the accessor twice on the
16453        // same [`Caixa`] must yield references that are pointer-equal
16454        // (the underlying byte-buffer is the storage `Entrada`'s
16455        // allocation, not a fresh copy) as well as value-equal
16456        // (idempotent, no side effects on `&self`).
16457        //
16458        // Pins against a future silent detour that returned an owned
16459        // `Entrada` (which would type-check via the `Clone` impl but
16460        // silently clone on every call), a `&Entrada` panic-return on
16461        // the `None` arm (which would collapse the load-bearing
16462        // `Option` presence-bit into a runtime panic), or a one-arm-
16463        // only accessor that returned a saturating composite on some
16464        // sentinel input.
16465        use crate::aplicacao::Entrada;
16466        for entrada in [
16467            Some(Entrada {
16468                host: "checkout.quero.cloud".into(),
16469                para: "gateway".into(),
16470                paths: Vec::new(),
16471                port: crate::DEFAULT_SERVICO_PORT,
16472            }),
16473            Some(Entrada {
16474                host: "api.pleme.io".into(),
16475                para: "public-api".into(),
16476                paths: vec!["/v1".into(), "/v2".into()],
16477                port: 8080,
16478            }),
16479        ] {
16480            let c = caixa_aplicacao_with_entrada(entrada.clone());
16481            let first = c.entrada().unwrap();
16482            let second = c.entrada().unwrap();
16483            assert_eq!(
16484                first, second,
16485                "Caixa::entrada must be idempotent — two successive \
16486                 calls on the same &self must return the same &Entrada",
16487            );
16488            assert!(
16489                std::ptr::eq(first, second),
16490                "Caixa::entrada must borrow the underlying \
16491                 Option<Entrada> storage — two successive calls must \
16492                 return references with the same backing pointer (a \
16493                 fresh Entrada clone would change the pointer on every \
16494                 call)",
16495            );
16496            assert_eq!(
16497                Some(first),
16498                entrada.as_ref(),
16499                "Caixa::entrada must return :entrada verbatim by \
16500                 borrow — got {first:?}, expected {:?}",
16501                entrada.as_ref(),
16502            );
16503        }
16504        let c = caixa_aplicacao_with_entrada(None);
16505        assert!(
16506            c.entrada().is_none(),
16507            "Caixa::entrada must return None when :entrada is absent \
16508             — the author-omitted arm must project through the \
16509             accessor's Option::None unchanged",
16510        );
16511    }
16512
16513    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
16514
16515    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
16516        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16517        c.estrategia = estrategia;
16518        c
16519    }
16520
16521    #[test]
16522    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
16523        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
16524        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
16525        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
16526        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
16527        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
16528        // over the same discriminant the raw `self.estrategia` field
16529        // access carries, byte-equal across every representative fixture
16530        // in the accept-set — the author-omitted `None` shape (the
16531        // "defer to [`RestartStrategy::default`] through the
16532        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
16533        // every non-`Supervisor`-kind `defcaixa` carries by
16534        // `#[serde(default)]`), and each of the four closed-set variants
16535        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
16536        // / [`RestartStrategy::RestForOne`] /
16537        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
16538        // partitions on.
16539        //
16540        // Pins against a future silent detour that re-derived the
16541        // strategy from a peer axis (an accidental fallback to
16542        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
16543        // collapse that read the outer `:children` list-length axis into
16544        // the strategy discriminator at the accessor boundary), a
16545        // stale-derive detour that substituted [`RestartStrategy::default`]
16546        // when the outer `Option` held `None` (which would silently
16547        // collapse the load-bearing "author explicitly declared
16548        // `:estrategia OneForOne`" vs "author omitted the slot and
16549        // inherited the default" partition the [`Self::declared_supervisor_slots`]
16550        // presence-probe reads — the enumerator gate would still push
16551        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
16552        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
16553        // kind-coherence gate's traversal head from the
16554        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
16555        // composition head), a reference to an operator-resolved overlay
16556        // (the future per-cluster `:estrategia-overrides` slot — its
16557        // resolution must land at exactly this accessor body, not
16558        // silently divert the raw slot away from a second consumer), or
16559        // an axis-remap projection (a future detour that mapped
16560        // `OneForAll` through the accessor onto `OneForOne` would
16561        // silently split every downstream sibling-restart-strategy
16562        // consumer's per-arm fan-out).
16563        //
16564        // First outer top-level [`Caixa`] `Option<Copy>`-return
16565        // supervisor-tree-slot flat-spread accessor pin on the substrate
16566        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
16567        // projection pattern the sibling per-`Caixa` `:max-restarts` /
16568        // `:restart-window` future outer-scalar pins fold on. Peer of
16569        // the inner-altitude
16570        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
16571        // (eafb619) pin on the post-composition [`SupervisorSpec`]
16572        // altitude — same "the substrate-primitive accessor must byte-
16573        // equal the raw field access verbatim across every author-
16574        // declared value" discipline extended onto the pre-composition
16575        // outer author-surface [`Caixa`] altitude. Peer of the closed
16576        // outer-`Caixa` `Option<&Composite>` composite-reference family
16577        // the sibling `limits` / `behavior` / `politicas` / `placement` /
16578        // `entrada`
16579        // `..._returns_..._option_ref_verbatim_across_permutations` pins
16580        // already carry on the outer `Option<&Composite>` altitude.
16581        use crate::supervisor::RestartStrategy;
16582        let fixtures: Vec<Option<RestartStrategy>> = vec![
16583            None,
16584            Some(RestartStrategy::OneForOne),
16585            Some(RestartStrategy::OneForAll),
16586            Some(RestartStrategy::RestForOne),
16587            Some(RestartStrategy::SimpleOneForOne),
16588        ];
16589        for estrategia in fixtures {
16590            let c = caixa_with_estrategia(estrategia);
16591            assert_eq!(
16592                c.estrategia(),
16593                estrategia,
16594                "Caixa::estrategia must return :estrategia verbatim (got \
16595                 {:?}, expected {:?})",
16596                c.estrategia(),
16597                estrategia,
16598            );
16599            assert_eq!(
16600                c.estrategia(),
16601                c.estrategia,
16602                "Caixa::estrategia accessor and self.estrategia field \
16603                 access must byte-equal — the accessor is the substrate-\
16604                 primitive typed dispatch every downstream supervisor-\
16605                 tree flat-spread consumer must route through, and a \
16606                 discriminant split would silently break every consumer \
16607                 that relied on the accessor sharing the field's own \
16608                 Option<Copy> shape",
16609            );
16610            assert_eq!(
16611                c.estrategia().is_some(),
16612                c.estrategia.is_some(),
16613                "Caixa::estrategia().is_some() must byte-equal \
16614                 self.estrategia.is_some() — a presence-bit drift would \
16615                 silently split the paired Caixa::declared_supervisor_slots \
16616                 presence-probe arm from the Caixa::supervisor_view \
16617                 unwrap_or_default() fold's composition input",
16618            );
16619        }
16620    }
16621
16622    #[test]
16623    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
16624        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16625        // `:estrategia` presence-probe arm must key off
16626        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
16627        // field-probe. Structurally: every `Caixa { estrategia:
16628        // Some(RestartStrategy::_), .. }` variant must push
16629        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
16630        // (the presence bit is `Some` for every closed-set variant, so
16631        // the M2 supervisor-tree kind-coherence gate must surface the
16632        // slot as "declared" regardless of which variant the author
16633        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
16634        // the label (the "author omitted the slot entirely, deferring
16635        // to [`RestartStrategy::default`] through the supervisor_view
16636        // fold" partition). The pair jointly pins the accessor +
16637        // declared-slot enumerator composition: any future silent detour
16638        // that had the accessor collapse `Some(RestartStrategy::default())`
16639        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
16640        // projection) would silently absorb the "declared but default-
16641        // valued" arm at the accessor boundary and the
16642        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
16643        // coherence gate would silently accept a struct-literal `Caixa`
16644        // carrying the drift.
16645        //
16646        // Peer of the sibling per-`Caixa`
16647        // `declared_servico_slots_limits_arm_routes_through_accessor`
16648        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
16649        // `Option<&LimitsSpec>` composition axis — same "the enumerator
16650        // gate must route through the substrate-primitive typed
16651        // dispatch" discipline extended onto the flat-spread M2
16652        // supervisor-tree `Option<RestartStrategy>`-composition surface,
16653        // opening the outer-`Caixa` supervisor-tree-slot arm of the
16654        // composition-pin family.
16655        use crate::supervisor::RestartStrategy;
16656        for estrategia in [
16657            RestartStrategy::OneForOne,
16658            RestartStrategy::OneForAll,
16659            RestartStrategy::RestForOne,
16660            RestartStrategy::SimpleOneForOne,
16661        ] {
16662            let c = caixa_with_estrategia(Some(estrategia));
16663            let slots = c.declared_supervisor_slots();
16664            assert!(
16665                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
16666                "declared_supervisor_slots must push \
16667                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
16668                 Some({estrategia:?}) — the accessor and the enumerator \
16669                 gate must route through the same substrate-primitive \
16670                 typed dispatch on the outer :estrategia presence bit \
16671                 (got slots={slots:?})",
16672            );
16673        }
16674        let c = caixa_with_estrategia(None);
16675        let slots = c.declared_supervisor_slots();
16676        assert!(
16677            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
16678            "declared_supervisor_slots must NOT push \
16679             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
16680             — the author-omitted arm must route through the accessor's \
16681             None-return unchanged (got slots={slots:?})",
16682        );
16683    }
16684
16685    #[test]
16686    fn supervisor_view_estrategia_arm_routes_through_accessor() {
16687        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
16688        // [`SupervisorSpec`] construction arm must key off
16689        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
16690        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
16691        // for every `:kind Supervisor` `Caixa` carrying an author-
16692        // declared `Some(RestartStrategy::_)` variant, the composed
16693        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
16694        // outer accessor's declared variant unchanged; and for a
16695        // `:kind Supervisor` `Caixa` carrying `None`, the composed
16696        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
16697        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
16698        // arm the flat-spread `unwrap_or_default()` fold projects to on
16699        // the author-omitted arm — this is the *composition* between the
16700        // outer `Option<RestartStrategy>` accessor's presence-bit
16701        // surface and the inner post-composition non-`Option`
16702        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
16703        // pins the accessor + supervisor_view composition: any future
16704        // silent detour that had the accessor promote `None` to
16705        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
16706        // projection) would silently collapse the two arms into one at
16707        // the accessor boundary and the [`Self::declared_supervisor_slots`]
16708        // presence probe would silently drift from the composition site.
16709        //
16710        // Peer of the sibling M2 supervisor-slot post-composition
16711        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
16712        // pin on the [`SupervisorSpec::validate`] altitude — this pin
16713        // extends that inner-altitude accessor-routing discipline onto
16714        // the pre-composition outer author-surface [`Caixa`] altitude,
16715        // pinning the composition edge between the flat-spread outer
16716        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
16717        // `RestartStrategy` axes.
16718        use crate::CaixaKind;
16719        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
16720        for estrategia in [
16721            RestartStrategy::OneForOne,
16722            RestartStrategy::OneForAll,
16723            RestartStrategy::RestForOne,
16724            RestartStrategy::SimpleOneForOne,
16725        ] {
16726            let mut c = caixa_with_estrategia(Some(estrategia));
16727            c.kind = CaixaKind::Supervisor;
16728            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
16729            // shape partition through the [`gen_platform::IsVariant`]
16730            // derive-generated
16731            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
16732            // than the raw `matches!(estrategia, RestartStrategy::
16733            // SimpleOneForOne)` open-coded pattern-match — same closed-
16734            // set-typed-enum arm-discriminator dispatch discipline the
16735            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
16736            // convergence (915a934) extended onto its two paired positive
16737            // / negated `matches!` sites and the peer
16738            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
16739            // predicate convergence (766ec63) extended onto the M3 mesh-
16740            // slot per-`:placement` distribution-strategy discriminator
16741            // axis. See the sibling `supervisor::tests::
16742            // round_trip_all_strategies` and
16743            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
16744            // fixtures — the three sites (all test-only,
16745            // acknowledged in 915a934's Prior-commits footnote as the
16746            // outstanding follow-up) now consult one typed dispatch on
16747            // the substrate primitive.
16748            c.children = if estrategia.is_simple_one_for_one() {
16749                Vec::new()
16750            } else {
16751                vec![ChildSpec {
16752                    caixa: "worker".into(),
16753                    versao: "^0.1".into(),
16754                    restart: RestartPolicy::Permanent,
16755                }]
16756            };
16757            let view = c.supervisor_view().expect(
16758                "supervisor_view must materialize a SupervisorSpec for a \
16759                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
16760            );
16761            assert_eq!(
16762                view.estrategia(),
16763                c.estrategia().unwrap(),
16764                "supervisor_view must carry the outer Caixa::estrategia() \
16765                 declared variant onto the composed SupervisorSpec.estrategia \
16766                 field verbatim on the Some arm (got {:?}, expected {:?})",
16767                view.estrategia(),
16768                c.estrategia().unwrap(),
16769            );
16770        }
16771        // The author-omitted arm: outer `None` → composed
16772        // `RestartStrategy::default()` through the flat-spread
16773        // `unwrap_or_default()` fold.
16774        let mut c = caixa_with_estrategia(None);
16775        c.kind = CaixaKind::Supervisor;
16776        // Populate children so the sibling supervisor slots are coherent
16777        // for the [`Self::supervisor_view`] projection; the `:estrategia`
16778        // arm still defers to [`RestartStrategy::default`] on the
16779        // author-omitted arm even when the sibling slots carry values.
16780        c.children = vec![ChildSpec {
16781            caixa: "worker".into(),
16782            versao: "^0.1".into(),
16783            restart: RestartPolicy::Permanent,
16784        }];
16785        let view = c.supervisor_view().expect(
16786            "supervisor_view must materialize a SupervisorSpec for a \
16787             :kind Supervisor Caixa carrying a None `:estrategia` slot",
16788        );
16789        assert_eq!(
16790            view.estrategia(),
16791            RestartStrategy::default(),
16792            "supervisor_view must project the outer Caixa::estrategia() \
16793             None arm onto RestartStrategy::default() through the flat-\
16794             spread unwrap_or_default() fold (got {:?}, expected {:?})",
16795            view.estrategia(),
16796            RestartStrategy::default(),
16797        );
16798        assert!(
16799            c.estrategia().is_none(),
16800            "Caixa::estrategia() must remain None on the author-omitted \
16801             arm — the supervisor_view fold must not mutate the outer \
16802             flat-spread presence bit",
16803        );
16804    }
16805
16806    #[test]
16807    fn estrategia_projects_option_by_copy() {
16808        // The by-`Copy` pin: [`Caixa::estrategia`] returns
16809        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
16810        // the accessor does not borrow `&self` past the call (no
16811        // lifetime on the return type), and calling the accessor twice
16812        // on the same [`Caixa`] must yield discriminant-equal values
16813        // (idempotent, no side effects on `&self`). Peer of the sibling
16814        // outer-`Caixa` `Option<&Composite>` by-borrow
16815        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
16816        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
16817        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
16818        // `placement_projects_option_ref_by_borrow` (4fb8074) /
16819        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
16820        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
16821        // extended here to the outer-`Caixa` `Option<Copy>`-return
16822        // flat-spread axis. The `Copy` discipline replaces the pointer-
16823        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
16824        // `Copy` discriminant is definitionally the same discriminant, so
16825        // the axis reduces to discriminant equality).
16826        //
16827        // Pins against a future silent detour that returned a fresh
16828        // `Option<&RestartStrategy>` (which would type-check but silently
16829        // introduce a borrow of `&self` past the call, collapsing the
16830        // load-bearing "no lifetime on the return type" `Copy` projection
16831        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
16832        // read side effect that flipped the outer discriminant on
16833        // successive calls, or an axis-remap projection that returned a
16834        // different variant than the field storage.
16835        use crate::supervisor::RestartStrategy;
16836        for estrategia in [
16837            Some(RestartStrategy::OneForOne),
16838            Some(RestartStrategy::OneForAll),
16839            Some(RestartStrategy::RestForOne),
16840            Some(RestartStrategy::SimpleOneForOne),
16841        ] {
16842            let c = caixa_with_estrategia(estrategia);
16843            let first = c.estrategia();
16844            let second = c.estrategia();
16845            assert_eq!(
16846                first, second,
16847                "Caixa::estrategia must be idempotent — two successive \
16848                 calls on the same &self must return the same \
16849                 Option<RestartStrategy>",
16850            );
16851            assert_eq!(
16852                first, estrategia,
16853                "Caixa::estrategia must return :estrategia verbatim by \
16854                 Copy — got {first:?}, expected {estrategia:?}",
16855            );
16856        }
16857        let c = caixa_with_estrategia(None);
16858        assert!(
16859            c.estrategia().is_none(),
16860            "Caixa::estrategia must return None when :estrategia is \
16861             absent — the author-omitted arm must project through the \
16862             accessor's Option::None unchanged",
16863        );
16864    }
16865
16866    // ── Caixa::max_restarts / Caixa::restart_window —
16867    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
16868    //    (Option<u32> / Option<&str>) folding on the ed04d3c
16869    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
16870
16871    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
16872        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16873        c.max_restarts = max_restarts;
16874        c
16875    }
16876
16877    fn caixa_supervisor_with_max_restarts_and_window(
16878        max_restarts: Option<u32>,
16879        restart_window: Option<&str>,
16880    ) -> Caixa {
16881        use crate::CaixaKind;
16882        use crate::supervisor::{ChildSpec, RestartPolicy};
16883        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
16884        c.kind = CaixaKind::Supervisor;
16885        c.max_restarts = max_restarts;
16886        c.restart_window = restart_window.map(str::to_string);
16887        c.children = vec![ChildSpec {
16888            caixa: "worker".into(),
16889            versao: "^0.1".into(),
16890            restart: RestartPolicy::Permanent,
16891        }];
16892        c
16893    }
16894
16895    #[test]
16896    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
16897        // Value-shape pin: [`Caixa::max_restarts`] returns the
16898        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
16899        // from the typed slot's own storage, byte-equal across the
16900        // author-omitted `None` arm (the "defer to the
16901        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
16902        // `{intensity, 5, 60}` default" partition every
16903        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
16904        // and each of the representative fixtures in the accept-set —
16905        // `0` (the zero-floor arm the peer
16906        // [`crate::supervisor::SupervisorSpec::validate`]
16907        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
16908        // the post-composition altitude — the accessor must ship the
16909        // raw slot verbatim so struct-literal fixtures continue to
16910        // expose the zero at the accessor boundary), the OTP-canonical
16911        // `5` default (`{intensity, 5, 60}` worker-supervisor from
16912        // Learn You Some Erlang), `1000` (the
16913        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
16914        // upper-bound gate accepts on the boundary), `u32::MAX` (a
16915        // past-the-cap sentinel that the substrate-primitive accessor
16916        // must still ship verbatim). Second outer top-level
16917        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
16918        // pin — folds on the sibling
16919        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
16920        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
16921        // onto the sibling `Option<u32>` restart-budget-count arm.
16922        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
16923        for max_restarts in fixtures {
16924            let c = caixa_with_max_restarts(max_restarts);
16925            assert_eq!(
16926                c.max_restarts(),
16927                max_restarts,
16928                "Caixa::max_restarts must return :max-restarts verbatim \
16929                 (got {:?}, expected {max_restarts:?})",
16930                c.max_restarts(),
16931            );
16932            assert_eq!(
16933                c.max_restarts(),
16934                c.max_restarts,
16935                "Caixa::max_restarts accessor and self.max_restarts \
16936                 field access must byte-equal — a presence-bit or count \
16937                 drift would silently split the paired \
16938                 Caixa::declared_supervisor_slots presence-probe arm \
16939                 from the Caixa::supervisor_view unwrap_or(5) fold's \
16940                 composition input",
16941            );
16942        }
16943    }
16944
16945    #[test]
16946    fn max_restarts_projects_option_by_copy() {
16947        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
16948        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
16949        // borrow `&self` past the call (no lifetime on the return type),
16950        // and calling the accessor twice on the same [`Caixa`] must
16951        // yield equal values (idempotent, no side effects). Peer of the
16952        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
16953        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
16954        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
16955            let c = caixa_with_max_restarts(max_restarts);
16956            let first = c.max_restarts();
16957            let second = c.max_restarts();
16958            assert_eq!(
16959                first, second,
16960                "Caixa::max_restarts must be idempotent — two successive \
16961                 calls on the same &self must return the same Option<u32>",
16962            );
16963            assert_eq!(
16964                first, max_restarts,
16965                "Caixa::max_restarts must return :max-restarts verbatim \
16966                 by Copy — got {first:?}, expected {max_restarts:?}",
16967            );
16968        }
16969    }
16970
16971    #[test]
16972    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
16973        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
16974        // `:max-restarts` presence-probe arm must key off
16975        // [`Caixa::max_restarts`], not the raw
16976        // `self.max_restarts.is_some()` field-probe. Structurally: every
16977        // `Caixa { max_restarts: Some(_), .. }` variant must push
16978        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
16979        // list (the presence bit is `Some` for every representative
16980        // count, so the M2 kind-coherence gate must surface the slot as
16981        // "declared"), and a `Caixa { max_restarts: None, .. }` must
16982        // NOT push the label. Peer of the sibling
16983        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
16984        // (ed04d3c) composition pin — same routing-through-accessor
16985        // discipline extended onto the sibling flat-spread `Option<u32>`
16986        // arm.
16987        for max_restarts in [0u32, 5, 1000, u32::MAX] {
16988            let c = caixa_with_max_restarts(Some(max_restarts));
16989            let slots = c.declared_supervisor_slots();
16990            assert!(
16991                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
16992                "declared_supervisor_slots must push \
16993                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
16994                 is Some({max_restarts}) — the accessor and the \
16995                 enumerator gate must route through the same \
16996                 substrate-primitive typed dispatch on the outer \
16997                 :max-restarts presence bit (got slots={slots:?})",
16998            );
16999        }
17000        let c = caixa_with_max_restarts(None);
17001        let slots = c.declared_supervisor_slots();
17002        assert!(
17003            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
17004            "declared_supervisor_slots must NOT push \
17005             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
17006             None — the author-omitted arm must route through the \
17007             accessor's None-return unchanged (got slots={slots:?})",
17008        );
17009    }
17010
17011    #[test]
17012    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
17013        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
17014        // [`SupervisorSpec`] construction arm must key off
17015        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
17016        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
17017        // every `:kind Supervisor` `Caixa` carrying an author-declared
17018        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
17019        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
17020        // carrying `None`, the composed [`SupervisorSpec`]'s
17021        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
17022        // of the sibling
17023        // `supervisor_view_estrategia_arm_routes_through_accessor`
17024        // (ed04d3c) composition pin.
17025        for max_restarts in [1u32, 5, 1000] {
17026            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
17027            let view = c.supervisor_view().expect(
17028                "supervisor_view must materialize a SupervisorSpec for a \
17029                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
17030            );
17031            assert_eq!(
17032                view.max_restarts(),
17033                max_restarts,
17034                "supervisor_view must carry the outer \
17035                 Caixa::max_restarts() Some arm onto the composed \
17036                 SupervisorSpec.max_restarts field verbatim (got {}, \
17037                 expected {max_restarts})",
17038                view.max_restarts(),
17039            );
17040        }
17041        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
17042        let view = c.supervisor_view().expect(
17043            "supervisor_view must materialize a SupervisorSpec for a \
17044             :kind Supervisor Caixa carrying a None :max-restarts",
17045        );
17046        assert_eq!(
17047            view.max_restarts(),
17048            5,
17049            "supervisor_view must project the outer \
17050             Caixa::max_restarts() None arm onto the OTP-canonical \
17051             {{intensity, 5, 60}} default (5) through the flat-spread \
17052             unwrap_or(5) fold (got {})",
17053            view.max_restarts(),
17054        );
17055        assert!(
17056            c.max_restarts().is_none(),
17057            "Caixa::max_restarts() must remain None on the author-\
17058             omitted arm — the supervisor_view fold must not mutate \
17059             the outer flat-spread presence bit",
17060        );
17061    }
17062
17063    #[test]
17064    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
17065        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
17066        // `:estrategia` arm must degrade onto the substrate-canonical
17067        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
17068        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
17069        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
17070        // worker-supervisor default — rather than the transitively-
17071        // derived [`crate::supervisor::RestartStrategy::default`] route
17072        // the prior `.unwrap_or_default()` fold reached for. Prior to the
17073        // lift the composition site carried `.unwrap_or_default()` with
17074        // no compile-time link back to the shared OTP-canonical strategy
17075        // default that the paired [`crate::supervisor::Default for
17076        // RestartStrategy`] impl and the [`crate::supervisor::Default for
17077        // SupervisorSpec`] impl's struct-literal `estrategia` field both
17078        // (now) route through the same lifted constant — so a future
17079        // rebrand of the OTP-canonical strategy default (an OTP
17080        // `rest_for_one` widening once the substrate discovers startup-
17081        // order-coupled child cohorts as the more common worker-
17082        // supervisor shape, a per-cluster overlay the operator pins
17083        // through the MESH-COMPOSITION §III.2 supervision-canary
17084        // `:estrategia-overrides` roadmap slot) would have had to migrate
17085        // the paired `MaxIntensity` + `Period` halves through the lifted
17086        // constants and the `one_for_one` half through a
17087        // `RestartStrategy::default()` route in lockstep or a
17088        // `:kind Supervisor` caixa carrying an author-omitted
17089        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
17090        // whose `estrategia` disagreed with the paired
17091        // `SupervisorSpec::default()` view. Byte-parity against the
17092        // lifted constant closes the split. Peer of the sibling
17093        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
17094        // composition pin on the paired `MaxIntensity` half + the
17095        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
17096        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
17097        // pins on the sibling entry points onto the shared substrate
17098        // constant.
17099        use crate::CaixaKind;
17100        use crate::supervisor::{ChildSpec, RestartPolicy};
17101        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
17102        c.kind = CaixaKind::Supervisor;
17103        c.estrategia = None;
17104        c.children = vec![ChildSpec {
17105            caixa: "worker".into(),
17106            versao: "^0.1".into(),
17107            restart: RestartPolicy::Permanent,
17108        }];
17109        let view = c.supervisor_view().expect(
17110            "supervisor_view must materialize a SupervisorSpec for a \
17111             :kind Supervisor Caixa carrying a None :estrategia",
17112        );
17113        assert_eq!(
17114            view.estrategia(),
17115            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17116            "supervisor_view must degrade the outer \
17117             Caixa::estrategia() None arm onto the lifted \
17118             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
17119             expected {:?})",
17120            view.estrategia(),
17121            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17122        );
17123    }
17124
17125    #[test]
17126    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
17127        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
17128        // `:max-restarts` arm must degrade onto the substrate-canonical
17129        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
17130        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
17131        // `MaxIntensity` default — rather than a raw `5` literal. Prior
17132        // to the lift the composition site carried an inline
17133        // `.unwrap_or(5)` with no compile-time link back to the shared
17134        // OTP-canonical default that the serde-side
17135        // `#[serde(default = "default_max_restarts")]` wire-format arm
17136        // and the [`Default for crate::supervisor::SupervisorSpec`]
17137        // struct-literal default arm both key off — so a future rebrand
17138        // of the OTP-canonical default (Elixir's `Supervisor` `3`
17139        // default, a per-cluster overlay the operator pins through the
17140        // MESH-COMPOSITION §III.2 supervision-canary
17141        // `:supervisor :max-restarts-overrides` roadmap slot) would
17142        // have had to be threaded through both the serde-side helper
17143        // and this view-construction arm in lockstep or a `:kind
17144        // Supervisor` caixa carrying `:max-restarts ()` would silently
17145        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
17146        // with the same fixture's serde-side `SupervisorSpec` view (an
17147        // author-omitted slot round-tripping through
17148        // `SupervisorSpec::default()` to the lifted constant, then
17149        // splitting to a stale literal past `supervisor_view`).
17150        // Byte-parity against the lifted constant closes the split.
17151        // Peer of the sibling
17152        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
17153        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
17154        // composition pins that close the same routing on the two
17155        // sibling entry points onto the shared substrate constant.
17156        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
17157        let view = c.supervisor_view().expect(
17158            "supervisor_view must materialize a SupervisorSpec for a \
17159             :kind Supervisor Caixa carrying a None :max-restarts",
17160        );
17161        assert_eq!(
17162            view.max_restarts(),
17163            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
17164            "supervisor_view must degrade the outer \
17165             Caixa::max_restarts() None arm onto the lifted \
17166             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
17167             expected {})",
17168            view.max_restarts(),
17169            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
17170        );
17171    }
17172
17173    #[test]
17174    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
17175        // Value-shape pin: [`Caixa::restart_window`] returns the
17176        // `:restart-window` typed `Option<String>` verbatim as an
17177        // `Option<&str>`, borrowed from the typed slot's own storage,
17178        // byte-equal across the author-omitted `None` arm and each of
17179        // the representative fixtures in the accept-set — the canonical
17180        // `"60s"` from `{intensity, 5, 60}`, the sibling
17181        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
17182        // / `"0s"`) the shared codec's positive-set sweep pin covers,
17183        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
17184        // seconds drift the sibling [`Self::validate_restart_window`]
17185        // gate refuses; the accessor must ship the raw slot verbatim
17186        // so struct-literal fixtures continue to expose the drift at
17187        // the accessor boundary). Third outer top-level [`Caixa`]
17188        // supervisor-tree flat-spread pin — extends the sub-family onto
17189        // the sibling `Option<&str>` raw-duration-string arm.
17190        for window in [
17191            None,
17192            Some("60s"),
17193            Some("5m"),
17194            Some("1h"),
17195            Some("500ms"),
17196            Some("1.5s"),
17197            Some(""),
17198        ] {
17199            let c = caixa_with_restart_window(window);
17200            assert_eq!(
17201                c.restart_window(),
17202                window,
17203                "Caixa::restart_window must return :restart-window \
17204                 verbatim as Option<&str> (got {:?}, expected {window:?})",
17205                c.restart_window(),
17206            );
17207            assert_eq!(
17208                c.restart_window(),
17209                c.restart_window.as_deref(),
17210                "Caixa::restart_window accessor and \
17211                 self.restart_window.as_deref() field access must \
17212                 byte-equal — a byte-level drift would silently split \
17213                 the paired Caixa::declared_supervisor_slots \
17214                 presence-probe arm from the \
17215                 Caixa::validate_restart_window shared-codec gate and \
17216                 the Caixa::supervisor_view soft-swallowing fold",
17217            );
17218        }
17219    }
17220
17221    #[test]
17222    fn restart_window_projects_slice_by_borrow() {
17223        // The by-borrow pin: [`Caixa::restart_window`] returns
17224        // `Option<&str>` by borrow — the returned string slice borrows
17225        // the underlying `Option<String>` storage of the `:restart-window`
17226        // slot and the accessor must not clone on every call. Peer of
17227        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
17228        // by-borrow pins on the universal-axis scalar family
17229        // (`licenca_projects_option_ref_by_borrow` /
17230        // `descricao_projects_option_ref_by_borrow` and siblings) —
17231        // extended onto the M2 supervisor-tree flat-spread
17232        // `Option<&str>` raw-duration-string axis.
17233        for window in [None, Some("60s"), Some("5m"), Some("")] {
17234            let c = caixa_with_restart_window(window);
17235            let first = c.restart_window();
17236            let second = c.restart_window();
17237            assert_eq!(
17238                first, second,
17239                "Caixa::restart_window must be idempotent — two \
17240                 successive calls on the same &self must return the \
17241                 same Option<&str>",
17242            );
17243            if let (Some(a), Some(b)) = (first, second) {
17244                assert_eq!(
17245                    a.as_ptr(),
17246                    b.as_ptr(),
17247                    "Caixa::restart_window must borrow the underlying \
17248                     String storage — two successive Some-arm calls must \
17249                     return slices with the same backing pointer (a fresh \
17250                     String clone would change the pointer on every call)",
17251                );
17252            }
17253            assert_eq!(
17254                first, window,
17255                "Caixa::restart_window must return :restart-window \
17256                 verbatim by borrow — got {first:?}, expected {window:?}",
17257            );
17258        }
17259    }
17260
17261    #[test]
17262    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
17263        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17264        // `:restart-window` presence-probe arm must key off
17265        // [`Caixa::restart_window`], not the raw
17266        // `self.restart_window.is_some()` field-probe. Structurally:
17267        // every `Caixa { restart_window: Some(_), .. }` must push
17268        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
17269        // list, and a `Caixa { restart_window: None, .. }` must NOT
17270        // push the label. Peer of the sibling
17271        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
17272        // routing pin.
17273        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
17274            let c = caixa_with_restart_window(Some(window));
17275            let slots = c.declared_supervisor_slots();
17276            assert!(
17277                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
17278                "declared_supervisor_slots must push \
17279                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
17280                 `:restart-window` is Some({window:?}) — the accessor \
17281                 and the enumerator gate must route through the same \
17282                 substrate-primitive typed dispatch on the outer \
17283                 :restart-window presence bit (got slots={slots:?})",
17284            );
17285        }
17286        let c = caixa_with_restart_window(None);
17287        let slots = c.declared_supervisor_slots();
17288        assert!(
17289            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
17290            "declared_supervisor_slots must NOT push \
17291             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
17292             is None — the author-omitted arm must route through the \
17293             accessor's None-return unchanged (got slots={slots:?})",
17294        );
17295    }
17296
17297    #[test]
17298    fn validate_restart_window_arm_routes_through_accessor() {
17299        // Composition pin: [`Caixa::validate_restart_window`]'s
17300        // shared-codec fold arm must key off [`Caixa::restart_window`],
17301        // not the raw `self.restart_window.as_deref()` field-projection.
17302        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
17303        // express no reset" canonical shape); (2) a canonical `Some`
17304        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
17305        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
17306        // .. })` carrying the offending raw string verbatim. The three
17307        // arms jointly pin that the validator's raw-string binding is
17308        // the accessor's return, not a peer projection — any future
17309        // silent detour that had the accessor collapse `Some("")` to
17310        // `None` would silently absorb the empty-after-trim refusal
17311        // case at the accessor boundary.
17312        caixa_with_restart_window(None)
17313            .validate_restart_window()
17314            .expect("None :restart-window must validate through the accessor");
17315        caixa_with_restart_window(Some("60s"))
17316            .validate_restart_window()
17317            .expect("canonical :restart-window \"60s\" must validate through the accessor");
17318        let err = caixa_with_restart_window(Some("1.5s"))
17319            .validate_restart_window()
17320            .expect_err("fractional-seconds :restart-window must fail through the accessor");
17321        assert!(
17322            matches!(
17323                err,
17324                ManifestError::RestartWindowMalformed { ref restart_window, .. }
17325                    if restart_window == "1.5s"
17326            ),
17327            "validator must carry the offending raw string verbatim \
17328             from the accessor's borrowed &str (got {err:?})",
17329        );
17330    }
17331
17332    #[test]
17333    fn supervisor_view_restart_window_arm_routes_through_accessor() {
17334        // Composition pin: [`Caixa::supervisor_view`]'s
17335        // per-`:restart-window` [`SupervisorSpec`] construction arm
17336        // must key off [`Caixa::restart_window`]'s soft-swallowing
17337        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
17338        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
17339        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
17340        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
17341        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
17342        // (the shared codec's canonical parse); (3) codec-rejected
17343        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
17344        // (the soft-swallow preserving the view's best-effort shape).
17345        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
17346        let view = c.supervisor_view().expect("Supervisor kind has a view");
17347        assert_eq!(
17348            view.restart_window(),
17349            None,
17350            "supervisor_view must project outer None :restart-window \
17351             onto None on the composed SupervisorSpec (never-reset \
17352             sentinel) through the accessor's None-return unchanged",
17353        );
17354
17355        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
17356        let view = c.supervisor_view().expect("Supervisor kind has a view");
17357        assert_eq!(
17358            view.restart_window(),
17359            Some(std::time::Duration::from_secs(60)),
17360            "supervisor_view must fold outer Some(\"60s\") through the \
17361             shared duration_codec into Duration::from_secs(60) on the \
17362             composed SupervisorSpec (accessor's Some(&str) → codec \
17363             parse → Some(Duration))",
17364        );
17365
17366        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
17367        let view = c.supervisor_view().expect("Supervisor kind has a view");
17368        assert_eq!(
17369            view.restart_window(),
17370            None,
17371            "supervisor_view must soft-swallow the shared-codec parse \
17372             failure to None (the view's best-effort shape the sibling \
17373             manifest-level validate_restart_window surfaces as \
17374             RestartWindowMalformed); the accessor's raw-string return \
17375             is the single input every downstream consumer keys off",
17376        );
17377    }
17378
17379    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
17380
17381    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
17382        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17383        c.upgrade_from = upgrade_from;
17384        c
17385    }
17386
17387    #[test]
17388    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
17389        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
17390        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
17391        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
17392        // typed `Vec<UpgradeFromEntry>` verbatim as a
17393        // `&[UpgradeFromEntry]` slice-view over the same backing
17394        // buffer the raw `self.upgrade_from.as_slice()` field access
17395        // borrows from, element-equal across every representative
17396        // fixture in the accept-set — `[]` (the "no hot-upgrade path
17397        // declared" arm every `defcaixa` without an `:upgrade-from`
17398        // block carries; `#[serde(default)]` folds an omitted slot
17399        // onto `Vec::new()`), a canonical single-entry `Restart`
17400        // fixture (the shape most Servicos carry — a single prior
17401        // version with the fallback strategy), a canonical multi-
17402        // entry list carrying every typed instruction variant
17403        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
17404        // `Restart`), and a past-the-guard sentinel — a duplicate-
17405        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
17406        // ([`crate::upgrade::validate_upgrade_from`] rejects through
17407        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
17408        // ship the raw slot verbatim so struct-literal fixtures
17409        // continue to expose the duplicate at the accessor boundary).
17410        //
17411        // Pins against a future silent detour that returned an owned
17412        // `Vec<UpgradeFromEntry>` (which would type-check but silently
17413        // clone on every accessor call, breaking the zero-cost
17414        // projection every peer sibling slice accessor carries), a
17415        // `[dup, dup] → [dup]` dedup collapse (which would silently
17416        // absorb the `DuplicateFrom` refusal case at the accessor
17417        // boundary and the [`crate::StandardLayout::verify`] cross-
17418        // entry gate would silently accept a struct-literal `Caixa`
17419        // carrying the drift), a reference to an operator-resolved
17420        // overlay (the future per-cluster `:upgrade-overrides` slot
17421        // — its resolution must land at exactly this accessor body,
17422        // not silently divert the raw slot away from a second
17423        // consumer), or an axis-shuffled projection (a future detour
17424        // that reordered entries through the accessor would silently
17425        // split the paired [`crate::StandardLayout::verify`] per-
17426        // `:upgrade-from` shape gate's traversal input from the peer
17427        // [`crate::render::servico_m2_overlay`] emitter's projection
17428        // input, since the operator's hot-upgrade dispatch matches
17429        // per-`:from` and axis reordering would silently split the
17430        // per-entry script-path existence probe's iteration order
17431        // from the M2 overlay emitter's serialized-entry order).
17432        //
17433        // First outer top-level [`Caixa`] `&[Composite]`-return
17434        // slice accessor pin on the substrate primitive for M2 / M3
17435        // typed-slot vec-carry axes — opens the outer-`Caixa`
17436        // `&[Composite]` composite-slice projection pattern the
17437        // sibling `:children` [`crate::supervisor::ChildSpec`] /
17438        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
17439        // [`crate::aplicacao::WitContract`] future outer-composite-
17440        // slice pins fold on. Peer of the closed outer-`Caixa`
17441        // scalar `Option<&Composite>` composite-reference family the
17442        // sibling `limits` / `behavior` / `politicas` / `placement`
17443        // / `entrada` `..._returns_..._option_ref_verbatim_across_
17444        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
17445        // the "byte-equal, borrow-shared" outer-accessor discipline
17446        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
17447        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17448        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
17449            vec![],
17450            vec![UpgradeFromEntry {
17451                from: "0.0.1".into(),
17452                instructions: vec![UpgradeInstruction::Restart],
17453            }],
17454            vec![
17455                UpgradeFromEntry {
17456                    from: "0.0.1".into(),
17457                    instructions: vec![
17458                        UpgradeInstruction::LoadModule {
17459                            module: "demo".into(),
17460                        },
17461                        UpgradeInstruction::SoftPurge {
17462                            module: "demo".into(),
17463                        },
17464                    ],
17465                },
17466                UpgradeFromEntry {
17467                    from: "0.0.2".into(),
17468                    instructions: vec![
17469                        UpgradeInstruction::StateChange {
17470                            script: "servicos/upgrade.lisp".into(),
17471                        },
17472                        UpgradeInstruction::Purge {
17473                            module: "demo".into(),
17474                        },
17475                        UpgradeInstruction::Restart,
17476                    ],
17477                },
17478            ],
17479            vec![
17480                UpgradeFromEntry {
17481                    from: "0.1.0".into(),
17482                    instructions: vec![UpgradeInstruction::Restart],
17483                },
17484                UpgradeFromEntry {
17485                    from: "0.1.0".into(),
17486                    instructions: vec![UpgradeInstruction::Restart],
17487                },
17488            ],
17489        ];
17490        for upgrade_from in fixtures {
17491            let c = caixa_with_upgrade_from(upgrade_from.clone());
17492            assert_eq!(
17493                c.upgrade_from(),
17494                upgrade_from.as_slice(),
17495                "Caixa::upgrade_from must return :upgrade-from \
17496                 verbatim (got {:?}, expected {upgrade_from:?})",
17497                c.upgrade_from(),
17498            );
17499            assert_eq!(
17500                c.upgrade_from(),
17501                c.upgrade_from.as_slice(),
17502                "Caixa::upgrade_from must element-equal the raw \
17503                 `self.upgrade_from.as_slice()` field access across \
17504                 every value in the Vec<UpgradeFromEntry> accept-set",
17505            );
17506            assert_eq!(
17507                c.upgrade_from().is_empty(),
17508                c.upgrade_from.is_empty(),
17509                "Caixa::upgrade_from().is_empty() must byte-equal \
17510                 self.upgrade_from.is_empty() — a presence-bit drift \
17511                 would silently split the paired \
17512                 Caixa::declared_servico_slots M2 declared-slot \
17513                 enumerator's presence probe from the peer \
17514                 crate::render::servico_m2_overlay M2 overlay \
17515                 emitter's presence gate",
17516            );
17517        }
17518    }
17519
17520    #[test]
17521    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
17522        // Composition pin: [`Caixa::declared_servico_slots`]'s
17523        // `:upgrade-from` presence-probe arm must key off
17524        // [`Caixa::upgrade_from`], not the raw
17525        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
17526        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
17527        // instructions: vec![Restart] }], .. }` must push
17528        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
17529        // (the presence bit is non-empty, so the M2 kind-coherence
17530        // gate must surface the slot as "declared"), and a `Caixa {
17531        // upgrade_from: vec![], .. }` must NOT push the label (the
17532        // "author omitted the slot entirely" arm — the empty-slice
17533        // partition the serde-default folds onto). The pair jointly
17534        // pins the accessor + declared-slot enumerator composition:
17535        // any future silent detour that had the accessor collapse
17536        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
17537        // is_empty())` projection) would silently absorb the
17538        // "declared but degenerate" arm at the accessor boundary and
17539        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
17540        // coherence gate would silently accept a struct-literal
17541        // `Caixa` carrying the drift.
17542        //
17543        // Peer of the sibling
17544        // `declared_servico_slots_limits_arm_routes_through_accessor`
17545        // (b2bd9d7) and
17546        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17547        // (35d8b52) composition pins on the sibling `:limits` /
17548        // `:behavior` outer-`Option<&Composite>` arms — same "the
17549        // enumerator gate must route through the substrate-primitive
17550        // typed dispatch" discipline extended onto the third M2
17551        // Servico-runtime slot axis, closing the enumerator's routing
17552        // invariant on every M2 arm.
17553        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17554        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
17555            from: "0.0.1".into(),
17556            instructions: vec![UpgradeInstruction::Restart],
17557        }]);
17558        let slots = c.declared_servico_slots();
17559        assert!(
17560            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
17561            "declared_servico_slots must push \
17562             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
17563             non-empty — the accessor and the enumerator gate must \
17564             route through the same substrate-primitive typed \
17565             dispatch on the outer :upgrade-from presence bit (got \
17566             slots={slots:?})",
17567        );
17568        let c = caixa_with_upgrade_from(vec![]);
17569        let slots = c.declared_servico_slots();
17570        assert!(
17571            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
17572            "declared_servico_slots must NOT push \
17573             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
17574             empty — the author-omitted arm must route through the \
17575             accessor's empty-slice return unchanged (got \
17576             slots={slots:?})",
17577        );
17578    }
17579
17580    #[test]
17581    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
17582        // Composition pin: [`crate::render::servico_m2_overlay`]'s
17583        // per-`:upgrade-from` M2 overlay emit arm must key off
17584        // [`Caixa::upgrade_from`], not the raw
17585        // `!caixa.upgrade_from.is_empty()` presence gate + the
17586        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
17587        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
17588        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
17589        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
17590        // sequence in the overlay (the emitter fans onto the serde
17591        // slice-serialization), and a `Caixa { upgrade_from: vec![],
17592        // .. }` must omit the key entirely (the empty-slice
17593        // partition — the `!.is_empty()` outer gate elides the key
17594        // when the author omitted the slot). The pair jointly pins
17595        // the accessor + M2 overlay emitter composition: any future
17596        // silent detour that had the accessor return a fresh-cloned
17597        // `Vec<UpgradeFromEntry>` copy would silently break the
17598        // reference-identity pin the peer per-entry
17599        // `serde_yaml::to_value(caixa.upgrade_from())` projection
17600        // reads from — the projection would clone once per accessor
17601        // call instead of borrowing the storage buffer verbatim.
17602        //
17603        // Peer of the sibling
17604        // `servico_m2_overlay_limits_arm_routes_through_accessor`
17605        // (b2bd9d7) and
17606        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
17607        // (35d8b52) composition pins on the sibling `:limits` /
17608        // `:behavior` outer-`Option<&Composite>` arms — same "the
17609        // M2 overlay emitter must route through the substrate-
17610        // primitive typed dispatch" discipline extended onto the
17611        // third M2 Servico-runtime slot axis, closing the overlay
17612        // emitter's routing invariant on every M2 arm.
17613        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
17614        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17615        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
17616            from: "0.0.1".into(),
17617            instructions: vec![UpgradeInstruction::Restart],
17618        }]);
17619        let overlay = servico_m2_overlay(&c).unwrap();
17620        assert!(
17621            overlay.contains_key(M2_KEY_UPGRADE_FROM),
17622            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
17623             `:upgrade-from` is non-empty — the accessor and the M2 \
17624             overlay emitter must route through the same substrate- \
17625             primitive typed dispatch on the outer :upgrade-from \
17626             slice (got overlay={overlay:?})",
17627        );
17628        let c = caixa_with_upgrade_from(vec![]);
17629        let overlay = servico_m2_overlay(&c).unwrap();
17630        assert!(
17631            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
17632            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
17633             `:upgrade-from` is empty — the empty-slice partition \
17634             must route through the accessor's empty-slice return \
17635             unchanged (got overlay={overlay:?})",
17636        );
17637    }
17638
17639    #[test]
17640    fn upgrade_from_projects_slice_by_borrow() {
17641        // The by-borrow pin: [`Caixa::upgrade_from`] returns
17642        // `&[UpgradeFromEntry]` by borrow — the returned slice
17643        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
17644        // the `:upgrade-from` slot and the accessor must not clone
17645        // the backing `Vec` on every call. Peer of the sibling
17646        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
17647        // (`autores_projects_slice_by_borrow` b5d813f,
17648        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
17649        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17650        // `exe_projects_slice_by_borrow` 65d9527,
17651        // `servicos_projects_slice_by_borrow` 611f78b,
17652        // `deps_projects_slice_by_borrow` ad34b4e,
17653        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
17654        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
17655        // axes — extended here to the first outer-`Caixa`
17656        // composite-element `&[Composite]` axis: the accessor's
17657        // returned slice must borrow from `&self` (the returned
17658        // reference's lifetime is tied to `&self`), and calling the
17659        // accessor twice on the same [`Caixa`] must yield slices
17660        // that are pointer-equal (the underlying byte-buffer is the
17661        // storage `Vec`'s allocation, not a fresh copy) as well as
17662        // value-equal (idempotent, no side effects on `&self`).
17663        //
17664        // Pins against a future silent detour that returned an owned
17665        // `Vec<UpgradeFromEntry>` (which would type-check but
17666        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
17667        // return (which would leak the backing `Vec`'s
17668        // grow/push/reserve surface no downstream consumer reaches
17669        // for), or a one-arm-only accessor that returned a
17670        // saturating value on some sentinel input.
17671        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
17672        for upgrade_from in [
17673            vec![],
17674            vec![UpgradeFromEntry {
17675                from: "0.0.1".into(),
17676                instructions: vec![UpgradeInstruction::Restart],
17677            }],
17678            vec![
17679                UpgradeFromEntry {
17680                    from: "0.0.1".into(),
17681                    instructions: vec![UpgradeInstruction::Restart],
17682                },
17683                UpgradeFromEntry {
17684                    from: "0.0.2".into(),
17685                    instructions: vec![UpgradeInstruction::SoftPurge {
17686                        module: "demo".into(),
17687                    }],
17688                },
17689            ],
17690        ] {
17691            let c = caixa_with_upgrade_from(upgrade_from.clone());
17692            let first = c.upgrade_from();
17693            let second = c.upgrade_from();
17694            assert_eq!(
17695                first, second,
17696                "Caixa::upgrade_from must be idempotent — two \
17697                 successive calls on the same &self must return the \
17698                 same &[UpgradeFromEntry]",
17699            );
17700            assert_eq!(
17701                first.as_ptr(),
17702                second.as_ptr(),
17703                "Caixa::upgrade_from must borrow the underlying \
17704                 Vec<UpgradeFromEntry> storage — two successive calls \
17705                 must return slices with the same backing pointer (a \
17706                 fresh Vec<UpgradeFromEntry> clone would change the \
17707                 pointer on every call)",
17708            );
17709            assert_eq!(
17710                first,
17711                upgrade_from.as_slice(),
17712                "Caixa::upgrade_from must return :upgrade-from \
17713                 verbatim by borrow — got {first:?}, expected \
17714                 {upgrade_from:?}",
17715            );
17716        }
17717    }
17718
17719    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
17720
17721    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
17722        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17723        c.children = children;
17724        c
17725    }
17726
17727    #[test]
17728    fn children_returns_children_slice_verbatim_across_permutations() {
17729        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
17730        // outer-composite `&[ChildSpec]`-return slice-shape pin:
17731        // [`Caixa::children`] must return the `:children` typed
17732        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
17733        // the same backing buffer the raw `self.children.as_slice()`
17734        // field access borrows from, element-equal across every
17735        // representative fixture in the accept-set — `[]` (the "no
17736        // static children declared" arm every non-`Supervisor`-kind
17737        // `defcaixa` carries by `#[serde(default)]` and every
17738        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
17739        // a canonical single-child `Permanent` fixture (the shape
17740        // most `OneForOne` supervisors carry — a single long-running
17741        // worker child), a canonical multi-child list carrying every
17742        // typed restart-policy variant (`Permanent` / `Transient` /
17743        // `Temporary`), and a past-the-guard sentinel — a duplicate
17744        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
17745        // ([`crate::SupervisorSpec::validate`] rejects through
17746        // `DuplicateChildNome { nome: "w" }` but the accessor must
17747        // ship the raw slot verbatim so struct-literal fixtures
17748        // continue to expose the duplicate at the accessor boundary).
17749        //
17750        // Pins against a future silent detour that returned an owned
17751        // `Vec<ChildSpec>` (which would type-check but silently clone
17752        // on every accessor call, breaking the zero-cost projection
17753        // every peer sibling slice accessor carries), a `[dup, dup] →
17754        // [dup]` dedup collapse (which would silently absorb the
17755        // `DuplicateChildNome` refusal case at the accessor boundary
17756        // and the [`crate::StandardLayout::verify`] cross-child gate
17757        // would silently accept a struct-literal `Caixa` carrying the
17758        // drift), a reference to an operator-resolved overlay (the
17759        // future per-cluster `:children-overrides` slot — its
17760        // resolution must land at exactly this accessor body, not
17761        // silently divert the raw slot away from a second consumer),
17762        // or an axis-shuffled projection (a future detour that
17763        // reordered children through the accessor would silently
17764        // split the paired [`crate::StandardLayout::verify`] per-
17765        // supervisor gate's traversal input from the peer
17766        // [`Self::supervisor_view`] fold-in path's clone-order input,
17767        // since the OTP `RestForOne` restart strategy dispatches on
17768        // declared child order and axis reordering would silently
17769        // split the operator's per-cluster restart-fan-out order
17770        // from the caixa.lisp source-order).
17771        //
17772        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
17773        // accessor pin on the substrate primitive for M2 / M3 typed-
17774        // slot vec-carry axes — folds on the outer-`Caixa`
17775        // `&[Composite]` composite-slice sub-family the sibling
17776        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
17777        // (2a1f907) pin opened, peer at the outer altitude of the
17778        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
17779        // (bc92bce) accessor on the same OTP-supervisor static-child-
17780        // list axis.
17781        use crate::supervisor::{ChildSpec, RestartPolicy};
17782        let fixtures: Vec<Vec<ChildSpec>> = vec![
17783            vec![],
17784            vec![ChildSpec {
17785                caixa: "worker".into(),
17786                versao: "^0.1".into(),
17787                restart: RestartPolicy::Permanent,
17788            }],
17789            vec![
17790                ChildSpec {
17791                    caixa: "worker-a".into(),
17792                    versao: "^0.1".into(),
17793                    restart: RestartPolicy::Permanent,
17794                },
17795                ChildSpec {
17796                    caixa: "worker-b".into(),
17797                    versao: "^0.1".into(),
17798                    restart: RestartPolicy::Transient,
17799                },
17800                ChildSpec {
17801                    caixa: "worker-c".into(),
17802                    versao: "^0.1".into(),
17803                    restart: RestartPolicy::Temporary,
17804                },
17805            ],
17806            vec![
17807                ChildSpec {
17808                    caixa: "w".into(),
17809                    versao: "^0.1".into(),
17810                    restart: RestartPolicy::Permanent,
17811                },
17812                ChildSpec {
17813                    caixa: "w".into(),
17814                    versao: "^0.1".into(),
17815                    restart: RestartPolicy::Permanent,
17816                },
17817            ],
17818        ];
17819        for children in fixtures {
17820            let c = caixa_with_children(children.clone());
17821            assert_eq!(
17822                c.children(),
17823                children.as_slice(),
17824                "Caixa::children must return :children verbatim \
17825                 (got {:?}, expected {children:?})",
17826                c.children(),
17827            );
17828            assert_eq!(
17829                c.children(),
17830                c.children.as_slice(),
17831                "Caixa::children must element-equal the raw \
17832                 `self.children.as_slice()` field access across \
17833                 every value in the Vec<ChildSpec> accept-set",
17834            );
17835            assert_eq!(
17836                c.children().is_empty(),
17837                c.children.is_empty(),
17838                "Caixa::children().is_empty() must byte-equal \
17839                 self.children.is_empty() — a presence-bit drift \
17840                 would silently split the paired \
17841                 Caixa::declared_supervisor_slots supervisor-tree \
17842                 declared-slot enumerator's presence probe from the \
17843                 peer Caixa::supervisor_view typed-view composer's \
17844                 fold-in path",
17845            );
17846        }
17847    }
17848
17849    #[test]
17850    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
17851        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17852        // `:children` presence-probe arm must key off
17853        // [`Caixa::children`], not the raw
17854        // `!self.children.is_empty()` field-probe. Structurally: a
17855        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
17856        // "^0.1", restart: Permanent }], .. }` must push
17857        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
17858        // (the presence bit is non-empty, so the supervisor-tree
17859        // kind-coherence gate must surface the slot as "declared"),
17860        // and a `Caixa { children: vec![], .. }` must NOT push the
17861        // label (the "author omitted the slot entirely" arm — the
17862        // empty-slice partition the serde-default folds onto). The
17863        // pair jointly pins the accessor + declared-slot enumerator
17864        // composition: any future silent detour that had the accessor
17865        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
17866        // "__reserved__")` projection) would silently absorb the
17867        // "declared but degenerate" arm at the accessor boundary and
17868        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17869        // kind-coherence gate would silently accept a struct-literal
17870        // `Caixa` carrying the drift.
17871        //
17872        // Peer of the sibling
17873        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
17874        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
17875        // same "the enumerator gate must route through the substrate-
17876        // primitive typed dispatch" discipline extended onto the
17877        // supervisor-tree `:children` composite-slice arm.
17878        use crate::supervisor::{ChildSpec, RestartPolicy};
17879        let c = caixa_with_children(vec![ChildSpec {
17880            caixa: "w".into(),
17881            versao: "^0.1".into(),
17882            restart: RestartPolicy::Permanent,
17883        }]);
17884        let slots = c.declared_supervisor_slots();
17885        assert!(
17886            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
17887            "declared_supervisor_slots must push \
17888             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17889             non-empty — the accessor and the enumerator gate must \
17890             route through the same substrate-primitive typed \
17891             dispatch on the outer :children presence bit (got \
17892             slots={slots:?})",
17893        );
17894        let c = caixa_with_children(vec![]);
17895        let slots = c.declared_supervisor_slots();
17896        assert!(
17897            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
17898            "declared_supervisor_slots must NOT push \
17899             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
17900             empty — the author-omitted arm must route through the \
17901             accessor's empty-slice return unchanged (got \
17902             slots={slots:?})",
17903        );
17904    }
17905
17906    #[test]
17907    fn supervisor_view_children_arm_routes_through_accessor() {
17908        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
17909        // fold-in arm must key off [`Caixa::children`], not the raw
17910        // `self.children.clone()` field-clone. Structurally: a `Caixa {
17911        // kind: Supervisor, estrategia: Some(OneForOne), children:
17912        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
17913        // per-child list through the accessor into the typed
17914        // [`SupervisorSpec`] view's `children` field verbatim — every
17915        // entry the accessor surfaces must land in the view's
17916        // `children` slot in the same order. The pair jointly pins the
17917        // accessor + view-composer composition: any future silent
17918        // detour that had the accessor return a fresh-cloned
17919        // `Vec<ChildSpec>` copy would silently break the reference-
17920        // identity pin the peer `supervisor_view` fold-in path reads
17921        // from — the fold would clone once more per accessor call
17922        // instead of borrowing the storage buffer verbatim once.
17923        //
17924        // Peer of the sibling
17925        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
17926        // family) composition pin on the peer kind-gate arm — same
17927        // "the view composer must route through the substrate-
17928        // primitive typed dispatch" discipline extended onto the
17929        // per-`:children` fold-in arm, closing the supervisor-view
17930        // composer's routing invariant on the composite-slice input.
17931        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17932        let mut c = caixa_with_children(vec![
17933            ChildSpec {
17934                caixa: "worker-a".into(),
17935                versao: "^0.1".into(),
17936                restart: RestartPolicy::Permanent,
17937            },
17938            ChildSpec {
17939                caixa: "worker-b".into(),
17940                versao: "^0.1".into(),
17941                restart: RestartPolicy::Transient,
17942            },
17943        ]);
17944        c.kind = crate::CaixaKind::Supervisor;
17945        c.estrategia = Some(RestartStrategy::OneForOne);
17946        let view = c
17947            .supervisor_view()
17948            .expect("Supervisor kind must produce a supervisor_view");
17949        assert_eq!(
17950            view.children(),
17951            c.children(),
17952            "supervisor_view must fold Caixa::children verbatim into \
17953             SupervisorSpec::children — the accessor and the view \
17954             composer must route through the same substrate-primitive \
17955             typed dispatch on the outer :children slice (got view \
17956             children={:?}, expected {:?})",
17957            view.children(),
17958            c.children(),
17959        );
17960    }
17961
17962    #[test]
17963    fn children_projects_slice_by_borrow() {
17964        // The by-borrow pin: [`Caixa::children`] returns
17965        // `&[ChildSpec]` by borrow — the returned slice borrows the
17966        // underlying `Vec<ChildSpec>` storage of the `:children` slot
17967        // and the accessor must not clone the backing `Vec` on every
17968        // call. Peer of the sibling outer top-level [`Caixa`]
17969        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
17970        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
17971        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
17972        // `exe_projects_slice_by_borrow` 65d9527,
17973        // `servicos_projects_slice_by_borrow` 611f78b,
17974        // `deps_projects_slice_by_borrow` ad34b4e,
17975        // `deps_dev_projects_slice_by_borrow` f7fd81e,
17976        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
17977        // sibling outer top-level [`Caixa`] scalar-element and
17978        // composite-element `&[T]` axes — folds on the outer-`Caixa`
17979        // composite-element `&[Composite]` axis: the accessor's
17980        // returned slice must borrow from `&self` (the returned
17981        // reference's lifetime is tied to `&self`), and calling the
17982        // accessor twice on the same [`Caixa`] must yield slices
17983        // that are pointer-equal (the underlying byte-buffer is the
17984        // storage `Vec`'s allocation, not a fresh copy) as well as
17985        // value-equal (idempotent, no side effects on `&self`).
17986        //
17987        // Pins against a future silent detour that returned an owned
17988        // `Vec<ChildSpec>` (which would type-check but silently clone
17989        // on every call), a `&Vec<ChildSpec>` return (which would leak
17990        // the backing `Vec`'s grow/push/reserve surface no downstream
17991        // consumer reaches for), or a one-arm-only accessor that
17992        // returned a saturating value on some sentinel input.
17993        use crate::supervisor::{ChildSpec, RestartPolicy};
17994        for children in [
17995            vec![],
17996            vec![ChildSpec {
17997                caixa: "w".into(),
17998                versao: "^0.1".into(),
17999                restart: RestartPolicy::Permanent,
18000            }],
18001            vec![
18002                ChildSpec {
18003                    caixa: "worker-a".into(),
18004                    versao: "^0.1".into(),
18005                    restart: RestartPolicy::Permanent,
18006                },
18007                ChildSpec {
18008                    caixa: "worker-b".into(),
18009                    versao: "^0.1".into(),
18010                    restart: RestartPolicy::Transient,
18011                },
18012            ],
18013        ] {
18014            let c = caixa_with_children(children.clone());
18015            let first = c.children();
18016            let second = c.children();
18017            assert_eq!(
18018                first, second,
18019                "Caixa::children must be idempotent — two successive \
18020                 calls on the same &self must return the same \
18021                 &[ChildSpec]",
18022            );
18023            assert_eq!(
18024                first.as_ptr(),
18025                second.as_ptr(),
18026                "Caixa::children must borrow the underlying \
18027                 Vec<ChildSpec> storage — two successive calls must \
18028                 return slices with the same backing pointer (a fresh \
18029                 Vec<ChildSpec> clone would change the pointer on \
18030                 every call)",
18031            );
18032            assert_eq!(
18033                first,
18034                children.as_slice(),
18035                "Caixa::children must return :children verbatim by \
18036                 borrow — got {first:?}, expected {children:?}",
18037            );
18038        }
18039    }
18040
18041    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
18042
18043    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
18044        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18045        c.kind = CaixaKind::Aplicacao;
18046        c.membros = membros;
18047        c
18048    }
18049
18050    #[test]
18051    fn membros_returns_membros_slice_verbatim_across_permutations() {
18052        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
18053        // composite `&[Membro]`-return slice-shape pin:
18054        // [`Caixa::membros`] must return the `:membros` typed
18055        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
18056        // same backing buffer the raw `self.membros.as_slice()` field
18057        // access borrows from, element-equal across every
18058        // representative fixture in the accept-set — `[]` (the "no
18059        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
18060        // carries by `#[serde(default)]` and every partially-authored
18061        // Aplicacao carries before the
18062        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
18063        // canonical single-member fixture (the shape a minimal
18064        // Aplicacao carries — one Servico wrapping one contained
18065        // computation), a canonical multi-member list carrying three
18066        // distinct entries (the canonical checkout-shape Aplicacao —
18067        // cart / pricing / auth — every canonical example carries), and
18068        // a past-the-guard sentinel — a duplicate `:caixa`
18069        // `[("cart", ...), ("cart", ...)]` entry pair
18070        // ([`crate::AplicacaoSpec::validate`] rejects through
18071        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
18072        // the raw slot verbatim so struct-literal fixtures continue to
18073        // expose the duplicate at the accessor boundary).
18074        //
18075        // Pins against a future silent detour that returned an owned
18076        // `Vec<Membro>` (which would type-check but silently clone on
18077        // every accessor call, breaking the zero-cost projection every
18078        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
18079        // dedup collapse (which would silently absorb the
18080        // `DuplicateMembro` refusal case at the accessor boundary and
18081        // the [`crate::StandardLayout::verify`] cross-member gate would
18082        // silently accept a struct-literal `Caixa` carrying the drift),
18083        // a reference to an operator-resolved overlay (the future per-
18084        // cluster `:membros-overrides` slot — its resolution must land
18085        // at exactly this accessor body, not silently divert the raw
18086        // slot away from a second consumer), or an axis-shuffled
18087        // projection (a future detour that reordered members through
18088        // the accessor would silently split the paired
18089        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
18090        // traversal input from the peer [`Self::aplicacao_view`] fold-
18091        // in path's clone-order input, since the canonical `:contratos`
18092        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
18093        // read the member set through the same slice).
18094        //
18095        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
18096        // accessor pin on the substrate primitive for M2 / M3 typed-
18097        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
18098        // arm of the `&[Composite]` composite-slice sub-family the
18099        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18100        // (2a1f907) and
18101        // `children_returns_children_slice_verbatim_across_permutations`
18102        // (c17b51e) pins opened, peer at the outer altitude of the
18103        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
18104        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
18105        // list axis.
18106        use crate::aplicacao::Membro;
18107        let fixtures: Vec<Vec<Membro>> = vec![
18108            vec![],
18109            vec![Membro {
18110                caixa: "cart".into(),
18111                versao: "^0.1".into(),
18112            }],
18113            vec![
18114                Membro {
18115                    caixa: "cart".into(),
18116                    versao: "^0.1".into(),
18117                },
18118                Membro {
18119                    caixa: "pricing".into(),
18120                    versao: "^0.2".into(),
18121                },
18122                Membro {
18123                    caixa: "auth".into(),
18124                    versao: "^1.0".into(),
18125                },
18126            ],
18127            vec![
18128                Membro {
18129                    caixa: "cart".into(),
18130                    versao: "^0.1".into(),
18131                },
18132                Membro {
18133                    caixa: "cart".into(),
18134                    versao: "^0.1".into(),
18135                },
18136            ],
18137        ];
18138        for membros in fixtures {
18139            let c = caixa_aplicacao_with_membros(membros.clone());
18140            assert_eq!(
18141                c.membros(),
18142                membros.as_slice(),
18143                "Caixa::membros must return :membros verbatim \
18144                 (got {:?}, expected {membros:?})",
18145                c.membros(),
18146            );
18147            assert_eq!(
18148                c.membros(),
18149                c.membros.as_slice(),
18150                "Caixa::membros must element-equal the raw \
18151                 `self.membros.as_slice()` field access across every \
18152                 value in the Vec<Membro> accept-set",
18153            );
18154            assert_eq!(
18155                c.membros().is_empty(),
18156                c.membros.is_empty(),
18157                "Caixa::membros().is_empty() must byte-equal \
18158                 self.membros.is_empty() — a presence-bit drift would \
18159                 silently split the paired Caixa::declared_mesh_slots \
18160                 mesh declared-slot enumerator's presence probe from \
18161                 the peer Caixa::aplicacao_view typed-view composer's \
18162                 fold-in path",
18163            );
18164        }
18165    }
18166
18167    #[test]
18168    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
18169        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
18170        // presence-probe arm must key off [`Caixa::membros`], not the
18171        // raw `!self.membros.is_empty()` field-probe. Structurally: a
18172        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
18173        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
18174        // declared-slot list (the presence bit is non-empty, so the
18175        // mesh kind-coherence gate must surface the slot as
18176        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
18177        // push the label (the "author omitted the slot entirely" arm
18178        // — the empty-slice partition the serde-default folds onto).
18179        // The pair jointly pins the accessor + declared-slot
18180        // enumerator composition: any future silent detour that had
18181        // the accessor collapse `[Membro { .. }]` to `[]` (a
18182        // `.filter(|m| m.nome() != "__reserved__")` projection) would
18183        // silently absorb the "declared but degenerate" arm at the
18184        // accessor boundary and the
18185        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
18186        // coherence gate would silently accept a struct-literal
18187        // `Caixa` carrying the drift.
18188        //
18189        // Peer of the sibling
18190        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18191        // (2a1f907) and
18192        // `declared_supervisor_slots_children_arm_routes_through_accessor`
18193        // (c17b51e) composition pins on the M2 `:upgrade-from` /
18194        // `:children` composite-slice arms — same "the enumerator gate
18195        // must route through the substrate-primitive typed dispatch"
18196        // discipline extended onto the M3 `:membros` composite-slice
18197        // arm, opening the M3 arm of the declared-slot enumerator's
18198        // routing invariant.
18199        use crate::aplicacao::Membro;
18200        let c = caixa_aplicacao_with_membros(vec![Membro {
18201            caixa: "cart".into(),
18202            versao: "^0.1".into(),
18203        }]);
18204        let slots = c.declared_mesh_slots();
18205        assert!(
18206            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
18207            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
18208             `:membros` is non-empty — the accessor and the enumerator \
18209             gate must route through the same substrate-primitive \
18210             typed dispatch on the outer :membros presence bit (got \
18211             slots={slots:?})",
18212        );
18213        let c = caixa_aplicacao_with_membros(vec![]);
18214        let slots = c.declared_mesh_slots();
18215        assert!(
18216            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
18217            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
18218             when `:membros` is empty — the author-omitted arm must \
18219             route through the accessor's empty-slice return unchanged \
18220             (got slots={slots:?})",
18221        );
18222    }
18223
18224    #[test]
18225    fn aplicacao_view_membros_arm_routes_through_accessor() {
18226        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
18227        // fold-in arm must key off [`Caixa::membros`], not the raw
18228        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
18229        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
18230        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
18231        // member list through the accessor into the typed
18232        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
18233        // every entry the accessor surfaces must land in the view's
18234        // `membros` slot in the same order. The pair jointly pins the
18235        // accessor + view-composer composition: any future silent
18236        // detour that had the accessor return a fresh-cloned
18237        // `Vec<Membro>` copy would silently break the reference-
18238        // identity pin the peer `aplicacao_view` fold-in path reads
18239        // from — the fold would clone once more per accessor call
18240        // instead of borrowing the storage buffer verbatim once.
18241        //
18242        // Peer of the sibling
18243        // `aplicacao_view_politicas_arm_folds_through_accessor`
18244        // (5d23d29) /
18245        // `aplicacao_view_placement_arm_folds_through_accessor`
18246        // (4fb8074) /
18247        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
18248        // composition pins on the M3 `:politicas` / `:placement` /
18249        // `:entrada` outer-`Option<&Composite>` arms — extended here to
18250        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
18251        // closing the aplicacao-view composer's routing invariant on
18252        // the composite-slice input.
18253        use crate::aplicacao::Membro;
18254        let c = caixa_aplicacao_with_membros(vec![
18255            Membro {
18256                caixa: "cart".into(),
18257                versao: "^0.1".into(),
18258            },
18259            Membro {
18260                caixa: "pricing".into(),
18261                versao: "^0.2".into(),
18262            },
18263        ]);
18264        let view = c
18265            .aplicacao_view()
18266            .expect("Aplicacao kind must produce an aplicacao_view");
18267        assert_eq!(
18268            view.membros(),
18269            c.membros(),
18270            "aplicacao_view must fold Caixa::membros verbatim into \
18271             AplicacaoSpec::membros — the accessor and the view \
18272             composer must route through the same substrate-primitive \
18273             typed dispatch on the outer :membros slice (got view \
18274             membros={:?}, expected {:?})",
18275            view.membros(),
18276            c.membros(),
18277        );
18278    }
18279
18280    #[test]
18281    fn membros_projects_slice_by_borrow() {
18282        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
18283        // borrow — the returned slice borrows the underlying
18284        // `Vec<Membro>` storage of the `:membros` slot and the
18285        // accessor must not clone the backing `Vec` on every call.
18286        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
18287        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
18288        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18289        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18290        // `exe_projects_slice_by_borrow` 65d9527,
18291        // `servicos_projects_slice_by_borrow` 611f78b,
18292        // `deps_projects_slice_by_borrow` ad34b4e,
18293        // `deps_dev_projects_slice_by_borrow` f7fd81e,
18294        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
18295        // `children_projects_slice_by_borrow` c17b51e) on the sibling
18296        // outer top-level [`Caixa`] scalar-element and composite-
18297        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
18298        // slot composite-element `&[Composite]` axis: the accessor's
18299        // returned slice must borrow from `&self` (the returned
18300        // reference's lifetime is tied to `&self`), and calling the
18301        // accessor twice on the same [`Caixa`] must yield slices that
18302        // are pointer-equal (the underlying byte-buffer is the storage
18303        // `Vec`'s allocation, not a fresh copy) as well as value-equal
18304        // (idempotent, no side effects on `&self`).
18305        //
18306        // Pins against a future silent detour that returned an owned
18307        // `Vec<Membro>` (which would type-check but silently clone on
18308        // every call), a `&Vec<Membro>` return (which would leak the
18309        // backing `Vec`'s grow/push/reserve surface no downstream
18310        // consumer reaches for), or a one-arm-only accessor that
18311        // returned a saturating value on some sentinel input.
18312        use crate::aplicacao::Membro;
18313        for membros in [
18314            vec![],
18315            vec![Membro {
18316                caixa: "cart".into(),
18317                versao: "^0.1".into(),
18318            }],
18319            vec![
18320                Membro {
18321                    caixa: "cart".into(),
18322                    versao: "^0.1".into(),
18323                },
18324                Membro {
18325                    caixa: "pricing".into(),
18326                    versao: "^0.2".into(),
18327                },
18328            ],
18329        ] {
18330            let c = caixa_aplicacao_with_membros(membros.clone());
18331            let first = c.membros();
18332            let second = c.membros();
18333            assert_eq!(
18334                first, second,
18335                "Caixa::membros must be idempotent — two successive \
18336                 calls on the same &self must return the same &[Membro]",
18337            );
18338            assert_eq!(
18339                first.as_ptr(),
18340                second.as_ptr(),
18341                "Caixa::membros must borrow the underlying Vec<Membro> \
18342                 storage — two successive calls must return slices with \
18343                 the same backing pointer (a fresh Vec<Membro> clone \
18344                 would change the pointer on every call)",
18345            );
18346            assert_eq!(
18347                first,
18348                membros.as_slice(),
18349                "Caixa::membros must return :membros verbatim by borrow \
18350                 — got {first:?}, expected {membros:?}",
18351            );
18352        }
18353    }
18354
18355    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
18356
18357    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
18358        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18359        c.kind = CaixaKind::Aplicacao;
18360        c.contratos = contratos;
18361        c
18362    }
18363
18364    fn contrato_http_for_test(
18365        de: &str,
18366        para: &str,
18367        endpoint: &str,
18368    ) -> crate::aplicacao::WitContract {
18369        crate::aplicacao::WitContract {
18370            de: de.into(),
18371            para: para.into(),
18372            wit: "wasi:http/proxy".into(),
18373            endpoint: Some(endpoint.into()),
18374            subject: None,
18375            slot: None,
18376        }
18377    }
18378
18379    #[test]
18380    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
18381        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
18382        // composite `&[WitContract]`-return slice-shape pin:
18383        // [`Caixa::contratos`] must return the `:contratos` typed
18384        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
18385        // over the same backing buffer the raw
18386        // `self.contratos.as_slice()` field access borrows from,
18387        // element-equal across every representative fixture in the
18388        // accept-set — `[]` (the "no contracts declared" arm every
18389        // non-`Aplicacao`-kind `defcaixa` carries by
18390        // `#[serde(default)]` and every leaf-Aplicacao with a single
18391        // member carries), a canonical single-edge fixture (the
18392        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
18393        // edge), and a canonical multi-edge fixture with three distinct
18394        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
18395        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
18396        //
18397        // Pins against a future silent detour that returned an owned
18398        // `Vec<WitContract>` (which would type-check but silently clone
18399        // on every accessor call, breaking the zero-cost projection
18400        // every peer sibling slice accessor carries), an axis-shuffled
18401        // projection (a future detour that reordered edges through the
18402        // accessor would silently split the paired
18403        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
18404        // traversal input from the peer [`Self::aplicacao_view`] fold-
18405        // in path's clone-order input, since every canonical
18406        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
18407        // seed dispatch reads the edge set through the same slice),
18408        // or a reference to an operator-resolved overlay (the future
18409        // per-cluster `:contratos-overrides` slot — its resolution
18410        // must land at exactly this accessor body, not silently divert
18411        // the raw slot away from a second consumer).
18412        //
18413        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
18414        // accessor pin on the substrate primitive for M2 / M3 typed-
18415        // slot vec-carry axes — closes the outer-`Caixa`
18416        // `&[Composite]` composite-slice sub-family the sibling M2
18417        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18418        // (2a1f907) and
18419        // `children_returns_children_slice_verbatim_across_permutations`
18420        // (c17b51e) pins opened and the M3
18421        // `membros_returns_membros_slice_verbatim_across_permutations`
18422        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
18423        // slot arm of the composite-slice sub-family. Peer at the outer
18424        // altitude of the closed inner-
18425        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
18426        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
18427        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
18428            vec![],
18429            vec![contrato_http_for_test("cart", "catalog", "/items")],
18430            vec![
18431                contrato_http_for_test("cart", "catalog", "/items"),
18432                contrato_http_for_test("cart", "pricing", "/price"),
18433                contrato_http_for_test("cart", "auth", "/whoami"),
18434            ],
18435        ];
18436        for contratos in fixtures {
18437            let c = caixa_aplicacao_with_contratos(contratos.clone());
18438            assert_eq!(
18439                c.contratos(),
18440                contratos.as_slice(),
18441                "Caixa::contratos must return :contratos verbatim \
18442                 (got {:?}, expected {contratos:?})",
18443                c.contratos(),
18444            );
18445            assert_eq!(
18446                c.contratos(),
18447                c.contratos.as_slice(),
18448                "Caixa::contratos must element-equal the raw \
18449                 `self.contratos.as_slice()` field access across every \
18450                 value in the Vec<WitContract> accept-set",
18451            );
18452            assert_eq!(
18453                c.contratos().is_empty(),
18454                c.contratos.is_empty(),
18455                "Caixa::contratos().is_empty() must byte-equal \
18456                 self.contratos.is_empty() — a presence-bit drift would \
18457                 silently split the paired Caixa::declared_mesh_slots \
18458                 mesh declared-slot enumerator's presence probe from \
18459                 the peer Caixa::aplicacao_view typed-view composer's \
18460                 fold-in path",
18461            );
18462        }
18463    }
18464
18465    #[test]
18466    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
18467        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
18468        // presence-probe arm must key off [`Caixa::contratos`], not the
18469        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
18470        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
18471        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
18472        // presence bit is non-empty, so the mesh kind-coherence gate
18473        // must surface the slot as "declared"), and a `Caixa {
18474        // contratos: vec![], .. }` must NOT push the label (the "author
18475        // omitted the slot entirely" arm — the empty-slice partition
18476        // the serde-default folds onto). The pair jointly pins the
18477        // accessor + declared-slot enumerator composition: any future
18478        // silent detour that had the accessor collapse
18479        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
18480        // "__reserved__")` projection) would silently absorb the
18481        // "declared but degenerate" arm at the accessor boundary and
18482        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
18483        // coherence gate would silently accept a struct-literal
18484        // `Caixa` carrying the drift.
18485        //
18486        // Peer of the sibling
18487        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18488        // (2a1f907),
18489        // `declared_supervisor_slots_children_arm_routes_through_accessor`
18490        // (c17b51e), and
18491        // `declared_mesh_slots_membros_arm_routes_through_accessor`
18492        // (0f26987) composition pins on the M2 `:upgrade-from` /
18493        // `:children` / M3 `:membros` composite-slice arms — same "the
18494        // enumerator gate must route through the substrate-primitive
18495        // typed dispatch" discipline extended onto the M3 `:contratos`
18496        // composite-slice arm, closing the M3 mesh-slot arm of the
18497        // declared-slot enumerator's routing invariant on the
18498        // composite-slice inputs.
18499        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
18500            "cart", "catalog", "/items",
18501        )]);
18502        let slots = c.declared_mesh_slots();
18503        assert!(
18504            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
18505            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
18506             `:contratos` is non-empty — the accessor and the enumerator \
18507             gate must route through the same substrate-primitive \
18508             typed dispatch on the outer :contratos presence bit (got \
18509             slots={slots:?})",
18510        );
18511        let c = caixa_aplicacao_with_contratos(vec![]);
18512        let slots = c.declared_mesh_slots();
18513        assert!(
18514            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
18515            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
18516             when `:contratos` is empty — the author-omitted arm must \
18517             route through the accessor's empty-slice return unchanged \
18518             (got slots={slots:?})",
18519        );
18520    }
18521
18522    #[test]
18523    fn aplicacao_view_contratos_arm_routes_through_accessor() {
18524        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
18525        // fold-in arm must key off [`Caixa::contratos`], not the raw
18526        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
18527        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
18528        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
18529        // per-edge list through the accessor into the typed
18530        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
18531        // every entry the accessor surfaces must land in the view's
18532        // `contratos` slot in the same order. The pair jointly pins
18533        // the accessor + view-composer composition: a future silent
18534        // detour that had the accessor shuffle or drop an edge would
18535        // silently split the paired declared-slot enumerator's
18536        // presence bit from the typed-view composer's edge-list, a
18537        // two-consumer split at the enumerator and the view composer
18538        // far from the source `caixa.lisp`.
18539        //
18540        // Peer of the sibling
18541        // `aplicacao_view_membros_arm_routes_through_accessor`
18542        // (0f26987) composition pin on the M3 `:membros` outer-
18543        // `&[Composite]` composite-slice arm, closing the aplicacao-
18544        // view composer's routing invariant on the composite-slice
18545        // inputs at the outer altitude.
18546        let c = caixa_aplicacao_with_contratos(vec![
18547            contrato_http_for_test("cart", "catalog", "/items"),
18548            contrato_http_for_test("cart", "pricing", "/price"),
18549        ]);
18550        let view = c
18551            .aplicacao_view()
18552            .expect("Aplicacao kind must produce an aplicacao_view");
18553        assert_eq!(
18554            view.contratos(),
18555            c.contratos(),
18556            "aplicacao_view must fold Caixa::contratos verbatim into \
18557             AplicacaoSpec::contratos — the accessor and the view \
18558             composer must route through the same substrate-primitive \
18559             typed dispatch on the outer :contratos slice (got view \
18560             contratos={:?}, expected {:?})",
18561            view.contratos(),
18562            c.contratos(),
18563        );
18564    }
18565
18566    #[test]
18567    fn contratos_projects_slice_by_borrow() {
18568        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
18569        // by borrow — the returned slice borrows the underlying
18570        // `Vec<WitContract>` storage of the `:contratos` slot and the
18571        // accessor must not clone the backing `Vec` on every call.
18572        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
18573        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
18574        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18575        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18576        // `exe_projects_slice_by_borrow` 65d9527,
18577        // `servicos_projects_slice_by_borrow` 611f78b,
18578        // `deps_projects_slice_by_borrow` ad34b4e,
18579        // `deps_dev_projects_slice_by_borrow` f7fd81e,
18580        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
18581        // `children_projects_slice_by_borrow` c17b51e,
18582        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
18583        // outer top-level [`Caixa`] scalar-element and composite-
18584        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
18585        // composite-element `&[Composite]` axis on the by-borrow pin:
18586        // the accessor's returned slice must borrow from `&self` (the
18587        // returned reference's lifetime is tied to `&self`), and
18588        // calling the accessor twice on the same [`Caixa`] must yield
18589        // slices that are pointer-equal (the underlying byte-buffer is
18590        // the storage `Vec`'s allocation, not a fresh copy) as well as
18591        // value-equal (idempotent, no side effects on `&self`).
18592        //
18593        // Pins against a future silent detour that returned an owned
18594        // `Vec<WitContract>` (which would type-check but silently clone
18595        // on every call), a `&Vec<WitContract>` return (which would
18596        // leak the backing `Vec`'s grow/push/reserve surface no
18597        // downstream consumer reaches for), or a one-arm-only accessor
18598        // that returned a saturating value on some sentinel input.
18599        for contratos in [
18600            vec![],
18601            vec![contrato_http_for_test("cart", "catalog", "/items")],
18602            vec![
18603                contrato_http_for_test("cart", "catalog", "/items"),
18604                contrato_http_for_test("cart", "pricing", "/price"),
18605            ],
18606        ] {
18607            let c = caixa_aplicacao_with_contratos(contratos.clone());
18608            let first = c.contratos();
18609            let second = c.contratos();
18610            assert_eq!(
18611                first, second,
18612                "Caixa::contratos must be idempotent — two successive \
18613                 calls on the same &self must return the same \
18614                 &[WitContract]",
18615            );
18616            assert_eq!(
18617                first.as_ptr(),
18618                second.as_ptr(),
18619                "Caixa::contratos must borrow the underlying \
18620                 Vec<WitContract> storage — two successive calls must \
18621                 return slices with the same backing pointer (a fresh \
18622                 Vec<WitContract> clone would change the pointer on \
18623                 every call)",
18624            );
18625            assert_eq!(
18626                first,
18627                contratos.as_slice(),
18628                "Caixa::contratos must return :contratos verbatim by \
18629                 borrow — got {first:?}, expected {contratos:?}",
18630            );
18631        }
18632    }
18633
18634    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
18635
18636    #[test]
18637    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
18638        // Load-bearing invariant: every multi-word top-level [`Caixa`]
18639        // serde-derived JSON key routes through a lifted `&'static str`
18640        // const. The Rust field names are `snake_case`
18641        // (`deps_dev` / `upgrade_from` / `max_restarts` /
18642        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
18643        // "camelCase")]` derive attribute maps each to the camelCase
18644        // byte-string the [`Caixa::to_lisp`] round-trip's
18645        // `serde_json::to_value(self)` step lands under before
18646        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
18647        // to the kebab-case `:deps-dev` / `:upgrade-from` /
18648        // `:max-restarts` / `:restart-window` author surface. Serialize
18649        // a fully-populated [`Caixa`] and pin that each canonical
18650        // byte-sequence appears verbatim in the JSON — a future
18651        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
18652        // verbatim-field-name flip at the derive attribute (any of
18653        // which would silently break every [`Caixa::to_lisp`]
18654        // round-trip and the future M4 operator-side manifest ingest's
18655        // `Value::get(<key>)` navigation) surfaces here as a build-time
18656        // test failure at `manifest.rs`, not as an apply-time
18657        // `.get(<stale-canonical-const>)` returning `None` far from the
18658        // derive-attr drift's commit. Same discipline the sibling
18659        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
18660        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
18661        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
18662        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
18663        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
18664        // [`UpgradeFromEntry`] per-entry axes — extended here to the
18665        // enclosing M0 [`Caixa`] top-level axis so the last of the four
18666        // multi-word top-level [`Caixa`] serde-derived JSON keys
18667        // (`depsDev`) joins the substrate's "one canonical byte-string
18668        // per typed serialized-key axis" discipline.
18669        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
18670        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18671        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18672        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
18673        c.upgrade_from = vec![UpgradeFromEntry {
18674            from: "0.0.1".into(),
18675            instructions: vec![UpgradeInstruction::Restart],
18676        }];
18677        c.estrategia = Some(RestartStrategy::OneForOne);
18678        c.max_restarts = Some(3);
18679        c.restart_window = Some("60s".into());
18680        c.children = vec![ChildSpec {
18681            caixa: "child".into(),
18682            versao: "^0.1".into(),
18683            restart: RestartPolicy::Permanent,
18684        }];
18685        let json = serde_json::to_string(&c).unwrap();
18686        for key in [
18687            crate::render::CAIXA_KEY_DEPS_DEV,
18688            crate::render::M2_KEY_UPGRADE_FROM,
18689            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18690            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18691        ] {
18692            let quoted = format!("\"{key}\"");
18693            assert!(
18694                json.contains(&quoted),
18695                "serialized Caixa must carry the lifted top-level \
18696                 multi-word byte-sequence {quoted} verbatim in the JSON \
18697                 emission (got: {json})",
18698            );
18699        }
18700    }
18701
18702    #[test]
18703    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
18704        // Cross-axis drift-detection pin: a future collapse of the four
18705        // canonical [`Caixa`] top-level multi-word byte-strings onto the
18706        // same value (e.g. an accidental copy-paste flip of
18707        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
18708        // `"upgradeFrom"`) would silently reroute every downstream
18709        // `Value::get(<key>)` probe on one axis onto the sibling axis's
18710        // top-level entry and pass every propagation-probe test that
18711        // expected only the stale axis's value. Peer of the sibling
18712        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
18713        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
18714        let all = [
18715            crate::render::CAIXA_KEY_DEPS_DEV,
18716            crate::render::M2_KEY_UPGRADE_FROM,
18717            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18718            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18719        ];
18720        for (i, a) in all.iter().enumerate() {
18721            for b in all.iter().skip(i + 1) {
18722                assert_ne!(
18723                    a, b,
18724                    "Caixa top-level multi-word key consts must be \
18725                     pairwise-distinct canonical byte-sequences — got \
18726                     `{a}` == `{b}`",
18727                );
18728            }
18729        }
18730    }
18731
18732    #[test]
18733    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
18734        // Shape-pin: every [`Caixa`] top-level multi-word key const must
18735        // be a lowerCamelCase byte-sequence (no `snake_case`
18736        // underscores, no `kebab-case` hyphens, no leading colon, no
18737        // `PascalCase` leading capital, no whitespace / dots) — the
18738        // canonical shape the `#[serde(rename_all = "camelCase")]`
18739        // derive produces on [`Caixa`]. A future flip to a
18740        // non-camelCase attribute at the derive surfaces both here
18741        // (this test fails on the stale-constant shape) and at
18742        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18743        // (that test fails on the mismatch between const and derive).
18744        // Peer with `membro_key_consts_are_lower_camel_case_shape`
18745        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
18746        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
18747        for key in [
18748            crate::render::CAIXA_KEY_DEPS_DEV,
18749            crate::render::M2_KEY_UPGRADE_FROM,
18750            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
18751            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
18752        ] {
18753            assert!(
18754                !key.is_empty(),
18755                "Caixa top-level multi-word key const must be non-empty \
18756                 (got {key:?})"
18757            );
18758            let first = key.chars().next().unwrap();
18759            assert!(
18760                first.is_ascii_lowercase(),
18761                "Caixa top-level multi-word key const must lead with an \
18762                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
18763            );
18764            assert!(
18765                key.chars().all(|c| c.is_ascii_alphanumeric()),
18766                "Caixa top-level multi-word key const must be \
18767                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
18768                 whitespace (got {key:?})",
18769            );
18770        }
18771    }
18772
18773    #[test]
18774    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
18775        // Scalar-value pin: the byte-string the
18776        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
18777        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
18778        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
18779        // → `depsTest` matching a hypothetical per-test-target
18780        // vocabulary flip) lands as an edit to exactly one const AND
18781        // one derive attribute — the sibling
18782        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18783        // pin already ties the const to the derive attribute, so a
18784        // rebrand that touches only one side of the pair fails at
18785        // caixa-core build time. Same "scalar-value pin per const"
18786        // discipline the sibling
18787        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
18788        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
18789        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
18790        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
18791    }
18792
18793    #[test]
18794    fn caixa_key_deps_pins_canonical_byte_string() {
18795        // Scalar-value pin: the byte-string the
18796        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
18797        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
18798        // on the two-list dep-graph serialized-key axis — the sibling
18799        // pin covers the multi-word `deps_dev → depsDev` camelCase
18800        // arm, this pin covers the single-word `deps → deps` no-op arm
18801        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
18802        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
18803        // axis and the emitted JSON key equals the source-side field
18804        // name byte-for-byte). A future [`crate::Caixa::deps`] field
18805        // rename (`deps` → `dependencies` matching Cargo's verbatim
18806        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
18807        // hypothetical per-runtime-target vocabulary flip) OR an added
18808        // `#[serde(rename = "…")]` explicit override lands as an edit
18809        // to exactly one const AND one derive-attr / field name — the
18810        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
18811        // pin ties the const to the emitted JSON key, so a rebrand
18812        // that touches only one side of the pair fails at caixa-core
18813        // build time.
18814        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
18815    }
18816
18817    #[test]
18818    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
18819        // Load-bearing invariant on the single-word `deps` top-level
18820        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
18821        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
18822        // `serde_json::to_value(self)` step emits. Serialize a
18823        // populated [`Caixa`] whose `:deps` slot carries at least one
18824        // entry (the `#[serde(default)]` attribute on the field emits
18825        // an empty `[]` even without members, but a non-empty vec
18826        // additionally covers the codec's per-`Dep`-entry emission
18827        // path) and pin that `"deps"` appears verbatim in the JSON
18828        // emission — a future accidental `rename_all = "snake_case"` /
18829        // `"kebab-case"` flip at the derive attribute (or an added
18830        // `#[serde(rename = "…")]` explicit override on the field, or
18831        // a Rust field rename) would break every [`Caixa::to_lisp`]
18832        // round-trip and the future M4 operator-side manifest ingest's
18833        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
18834        // build-time test failure at `manifest.rs`, not as an
18835        // apply-time `.get(<stale-canonical-const>)` returning `None`
18836        // far from the drift's commit. Peer of the sibling
18837        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
18838        // multi-word pin on the same M0 [`Caixa`] top-level
18839        // serialized-key axis, extended here to the single-word arm
18840        // the multi-word test's `rename_all = "camelCase"` sweep can't
18841        // reach (single-word `deps → deps` is a no-op the multi-word
18842        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
18843        // `\"restartWindow\"` byte-scan can never observe).
18844        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18845        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
18846        let json = serde_json::to_string(&c).unwrap();
18847        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
18848        assert!(
18849            json.contains(&quoted),
18850            "serialized Caixa must carry the lifted top-level `deps` \
18851             byte-sequence {quoted} verbatim in the JSON emission (got: \
18852             {json})",
18853        );
18854    }
18855
18856    #[test]
18857    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
18858        // Cross-axis drift-detection pin on the two-list dep-graph
18859        // renderer-side wire-key axis: a future collapse of the
18860        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
18861        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
18862        // same value (e.g. an accidental copy-paste flip of
18863        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
18864        // reroute every downstream `Value::get(<key>)` probe on one
18865        // axis onto the sibling axis's dep-list and pass every
18866        // propagation-probe test that expected only the stale axis's
18867        // value — a dev-only dep would land in the runtime closure at
18868        // publish time, or a runtime dep would be excluded from the
18869        // published lacre. Peer of the sibling four-way distinct pin
18870        // on the top-level multi-word tetrad
18871        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
18872        // and the two-way pin on the sibling
18873        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
18874        // author-facing arm (4da6fba's test), extended here to the
18875        // renderer-side wire-key arm of the same two-list dep-graph
18876        // axis so both halves of the "one canonical byte-string per
18877        // typed axis per (author, wire)" grid carry the same
18878        // distinct-ness discipline.
18879        assert_ne!(
18880            crate::render::CAIXA_KEY_DEPS,
18881            crate::render::CAIXA_KEY_DEPS_DEV,
18882            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
18883             canonical byte-sequences on the two-list dep-graph \
18884             renderer-side wire-key axis"
18885        );
18886    }
18887
18888    // ── DepList / Caixa::push_dep pin ────────────────────────────────
18889    //
18890    // The compounding pin: the two-arm closed-set typed enum
18891    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
18892    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
18893    // consumer of the top-level manifest's dep-mutation surface reads
18894    // through, and the typed dispatch [`Caixa::push_dep`] on the
18895    // substrate primitive folds the "select list → check within-list
18896    // dup → push" cascade onto one method call. Prior to this landing
18897    // the two axes lived across two `&'static str` constants
18898    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
18899    // set type carrying the pair; the `feira add` mutation site's
18900    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
18901    // caixa.deps }` dispatch expressed no compile-time link back to
18902    // the substrate primitive, and a future third dep-list axis would
18903    // have silently split at every open-coded mutation site.
18904
18905    #[test]
18906    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
18907        // Every arm returns the same `&'static str` the substrate's
18908        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
18909        // constants carry. A future rebrand on either constant reaches
18910        // the enum through one edit; a regression to inline literals
18911        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
18912        // quotes from the wire-format constants every consumer routes
18913        // through and this pin flags it at build time.
18914        assert_eq!(
18915            crate::dep::DepList::Prod.as_str(),
18916            crate::render::DEP_AUTHOR_KEY_DEPS
18917        );
18918        assert_eq!(
18919            crate::dep::DepList::Dev.as_str(),
18920            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
18921        );
18922    }
18923
18924    #[test]
18925    fn dep_list_display_routes_through_as_str() {
18926        // Same as-str-through-Display convergence discipline the
18927        // sibling closed-set typed enums carry — a `format!("{list}")`
18928        // call must land byte-for-byte on the accessor's return so a
18929        // future consumer that formats the enum for a diagnostic line
18930        // reaches the same wire-format constant the wire-format
18931        // producers do.
18932        assert_eq!(
18933            format!("{}", crate::dep::DepList::Prod),
18934            crate::dep::DepList::Prod.as_str()
18935        );
18936        assert_eq!(
18937            format!("{}", crate::dep::DepList::Dev),
18938            crate::dep::DepList::Dev.as_str()
18939        );
18940    }
18941
18942    #[test]
18943    fn dep_list_all_enumerates_every_variant_once() {
18944        // Exhaustive-iteration pin — every arm appears exactly once in
18945        // `ALL`, matching the closed set the compiler enforces on the
18946        // sibling `match self` arms. A future variant addition that
18947        // extends only one method's match without extending `ALL`
18948        // would silently drop the new arm from every consumer that
18949        // iterates the slice.
18950        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
18951        assert!(variants.contains(&crate::dep::DepList::Prod));
18952        assert!(variants.contains(&crate::dep::DepList::Dev));
18953        assert_eq!(variants.len(), 2);
18954    }
18955
18956    #[test]
18957    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
18958        // Reverse projection on the two-list dep-graph axis: the
18959        // author-surface wire tag the sibling `as_str` emitter walks
18960        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
18961        // `Some(DepList::Prod)`. A regression that hand-rolled the
18962        // per-arm match without routing through the lifted
18963        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
18964        // future wire-tag rebrand and this pin flags it at build time.
18965        assert_eq!(
18966            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
18967            Some(crate::dep::DepList::Prod)
18968        );
18969    }
18970
18971    #[test]
18972    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
18973        // Peer of the `Prod`-arm pin on the dev-only axis: the
18974        // author-surface wire tag the sibling `as_str` emitter walks
18975        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
18976        // back to `Some(DepList::Dev)`. Same drift-detection posture
18977        // as the peer arm — the sibling method `match` arms are
18978        // compiler-checked exhaustive so a future variant addition
18979        // trips at build time.
18980        assert_eq!(
18981            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
18982            Some(crate::dep::DepList::Dev)
18983        );
18984    }
18985
18986    #[test]
18987    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
18988        // Every input outside the closed-set arm-string set the
18989        // sibling `as_str` emitter walks lands on the terminal `None`
18990        // fallback — no silent-accept surface. Sweeps a set of
18991        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
18992        // rebrand candidates, foreign wire tags, empty string) so a
18993        // future variant addition that widened one wire form without
18994        // extending the emitter's arm-set would trip the sibling
18995        // round-trip pin below rather than silently accepting the new
18996        // form here.
18997        for candidate in [
18998            "",
18999            "deps",
19000            "deps-dev",
19001            ":deps ",
19002            ":Deps",
19003            ":DEPS",
19004            ":build-dep",
19005            ":tool-dep",
19006            "prod",
19007            "dev",
19008        ] {
19009            assert_eq!(
19010                crate::dep::DepList::from_wire(candidate),
19011                None,
19012                "from_wire({candidate:?}) must return None; every input outside \
19013                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
19014                 the sibling as_str emitter walks lands on the terminal fallback",
19015            );
19016        }
19017    }
19018
19019    #[test]
19020    fn dep_list_round_trips_through_as_str_and_from_wire() {
19021        // Load-bearing round-trip pin: every arm the `ALL` iteration
19022        // exposes survives the `as_str` → `from_wire` composition
19023        // byte-for-byte. Same discipline the sibling closed-set enums
19024        // carry — `CaixaKind` /
19025        // `RestartStrategy` / `RestartPolicy` /
19026        // `PlacementStrategy` — extended onto the two-list dep-graph
19027        // axis. A future variant addition that extends `ALL` +
19028        // `as_str` without extending `from_wire` (or vice versa)
19029        // trips at build time on this iteration because the compiler
19030        // enforces exhaustiveness on the sibling `match self` arms.
19031        for &list in crate::dep::DepList::ALL {
19032            assert_eq!(
19033                crate::dep::DepList::from_wire(list.as_str()),
19034                Some(list),
19035                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
19036                 a silent split between the forward emitter and the reverse parser \
19037                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
19038            );
19039        }
19040    }
19041
19042    #[test]
19043    fn push_dep_routes_to_deps_slot_on_prod_arm() {
19044        // The `Prod` arm dispatches to the runtime-closure `:deps`
19045        // slot every downstream lacre-pipeline consumer resolves at
19046        // build time. A future arm that regressed to inline `&mut
19047        // self.deps_dev` on the `Prod` path would silently reroute
19048        // every runtime dep into the dev-only closure at publish time
19049        // — this pin refuses that regression.
19050        let src = Caixa::template("host");
19051        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19052        let before_deps = caixa.deps().len();
19053        let before_deps_dev = caixa.deps_dev().len();
19054        let dep = Dep {
19055            nome: "caixa-teia".to_string(),
19056            versao: "^0.1".to_string(),
19057            fonte: None,
19058            opcional: false,
19059            caracteristicas: Vec::new(),
19060        };
19061        caixa
19062            .push_dep(crate::dep::DepList::Prod, dep)
19063            .expect("first push into :deps succeeds");
19064        assert_eq!(caixa.deps().len(), before_deps + 1);
19065        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
19066        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
19067    }
19068
19069    #[test]
19070    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
19071        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
19072        // must dispatch to the dev-only-closure `:deps-dev` slot every
19073        // downstream test-facing artifact resolver reads. A future
19074        // regression that inverted the two arms would silently route
19075        // every dev-only dep into the runtime closure at publish time
19076        // and this pin catches it before the drift ships.
19077        let src = Caixa::template("host");
19078        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19079        let dep = Dep {
19080            nome: "tatara-check".to_string(),
19081            versao: "*".to_string(),
19082            fonte: None,
19083            opcional: false,
19084            caracteristicas: Vec::new(),
19085        };
19086        caixa
19087            .push_dep(crate::dep::DepList::Dev, dep)
19088            .expect("first push into :deps-dev succeeds");
19089        assert!(caixa.deps().is_empty());
19090        assert_eq!(caixa.deps_dev().len(), 1);
19091        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
19092    }
19093
19094    #[test]
19095    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
19096        // Within-list dup check routes through the canonical
19097        // [`DepError::DuplicateNome`] carrier — the substrate's typed
19098        // diagnostic for the same axis [`Caixa::validate_deps`]'s
19099        // parse-time [`crate::render::insert_first_seen`] walk raises
19100        // on. Prior to the lift the mutation site's inline
19101        // `bail!("dep '{}' already declared", …)` string-diagnostic
19102        // path expressed no through-line back to the typed error;
19103        // routing every dep-list refusal through one carrier means an
19104        // author reading a `feira add` refusal and a `feira build`
19105        // refusal reaches for the same corrective surface without
19106        // switching diagnostic idioms.
19107        let src = Caixa::template("host");
19108        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19109        let dep = Dep {
19110            nome: "caixa-teia".to_string(),
19111            versao: "^0.1".to_string(),
19112            fonte: None,
19113            opcional: false,
19114            caracteristicas: Vec::new(),
19115        };
19116        caixa
19117            .push_dep(crate::dep::DepList::Prod, dep.clone())
19118            .expect("first push succeeds");
19119        let dup = Dep {
19120            nome: "caixa-teia".to_string(),
19121            versao: "^0.2".to_string(),
19122            fonte: None,
19123            opcional: false,
19124            caracteristicas: Vec::new(),
19125        };
19126        let err = caixa
19127            .push_dep(crate::dep::DepList::Prod, dup)
19128            .expect_err("second push with same :nome refuses");
19129        assert_eq!(
19130            err,
19131            DepError::DuplicateNome {
19132                nome: "caixa-teia".to_string(),
19133                list: crate::render::DEP_AUTHOR_KEY_DEPS,
19134            }
19135        );
19136        // The refused mutation must not corrupt the target list —
19137        // exactly one entry lives past the refusal, matching the
19138        // canonical single-source-of-truth invariant `Caixa::deps()`
19139        // carries.
19140        assert_eq!(caixa.deps().len(), 1);
19141    }
19142
19143    #[test]
19144    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
19145        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
19146        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
19147        // `list` payload so a future author reading the refusal grep's
19148        // for the correct `:deps-dev` block in their `caixa.lisp`,
19149        // not the sibling `:deps` block the runtime closure resolves.
19150        let src = Caixa::template("host");
19151        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19152        let dep = Dep {
19153            nome: "tatara-check".to_string(),
19154            versao: "*".to_string(),
19155            fonte: None,
19156            opcional: false,
19157            caracteristicas: Vec::new(),
19158        };
19159        caixa
19160            .push_dep(crate::dep::DepList::Dev, dep.clone())
19161            .expect("first push succeeds");
19162        let err = caixa
19163            .push_dep(crate::dep::DepList::Dev, dep)
19164            .expect_err("second push with same :nome refuses");
19165        assert!(matches!(
19166            err,
19167            DepError::DuplicateNome {
19168                ref nome,
19169                list,
19170            } if nome == "tatara-check"
19171                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
19172        ));
19173    }
19174
19175    #[test]
19176    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
19177        // The within-list dup check is scoped to the target arm — a
19178        // caixa may legitimately carry the same `:nome` under both
19179        // `:deps` and `:deps-dev` (though the substrate's peer
19180        // [`crate::Caixa::validate_deps`] walk still refuses the
19181        // shape at parse time; the mutation-site refusal is scoped to
19182        // the mutation-site's list to match the peer parse-time
19183        // per-list [`crate::render::insert_first_seen`] discipline).
19184        // The two arms hold independent seen-sets.
19185        let src = Caixa::template("host");
19186        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19187        let dep_prod = Dep {
19188            nome: "shared".to_string(),
19189            versao: "^0.1".to_string(),
19190            fonte: None,
19191            opcional: false,
19192            caracteristicas: Vec::new(),
19193        };
19194        let dep_dev = Dep {
19195            nome: "shared".to_string(),
19196            versao: "*".to_string(),
19197            fonte: None,
19198            opcional: false,
19199            caracteristicas: Vec::new(),
19200        };
19201        caixa
19202            .push_dep(crate::dep::DepList::Prod, dep_prod)
19203            .expect("push into :deps succeeds");
19204        caixa
19205            .push_dep(crate::dep::DepList::Dev, dep_dev)
19206            .expect("push same :nome into :deps-dev succeeds");
19207        assert_eq!(caixa.deps().len(), 1);
19208        assert_eq!(caixa.deps_dev().len(), 1);
19209    }
19210
19211    #[test]
19212    fn deps_of_prod_returns_the_deps_slot_verbatim() {
19213        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
19214        // accessor must project onto the runtime-closure `:deps` slot —
19215        // element-equal and length-equal to the sibling per-slot
19216        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
19217        // A future arm that regressed to `self.deps_dev()` on the `Prod`
19218        // path would silently reroute every downstream typed-dispatch
19219        // walker (the [`Caixa::validate_deps`] per-list
19220        // [`crate::render::insert_first_seen`] dedup walk, any future
19221        // per-axis-parametrised consumer) into the sibling dev-only
19222        // closure and this pin refuses that regression.
19223        let src = Caixa::template("host");
19224        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19225        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
19226        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
19227        let dep = Dep {
19228            nome: "caixa-teia".to_string(),
19229            versao: "^0.1".to_string(),
19230            fonte: None,
19231            opcional: false,
19232            caracteristicas: Vec::new(),
19233        };
19234        caixa
19235            .push_dep(crate::dep::DepList::Prod, dep.clone())
19236            .expect("push into :deps succeeds");
19237        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
19238        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
19239        assert_eq!(
19240            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
19241            "caixa-teia"
19242        );
19243    }
19244
19245    #[test]
19246    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
19247        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
19248        // [`Caixa::deps_of`] must project onto the dev-only-closure
19249        // `:deps-dev` slot, element-equal and length-equal to the
19250        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
19251        // future regression that inverted the two arms would silently
19252        // route every dev-list walker onto the runtime closure and this
19253        // pin catches it before the drift ships.
19254        let src = Caixa::template("host");
19255        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19256        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
19257        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
19258        let dep = Dep {
19259            nome: "tatara-check".to_string(),
19260            versao: "*".to_string(),
19261            fonte: None,
19262            opcional: false,
19263            caracteristicas: Vec::new(),
19264        };
19265        caixa
19266            .push_dep(crate::dep::DepList::Dev, dep)
19267            .expect("push into :deps-dev succeeds");
19268        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
19269        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
19270        assert_eq!(
19271            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
19272            "tatara-check"
19273        );
19274    }
19275
19276    #[test]
19277    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
19278        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
19279        // [`Caixa::deps_of`] must land on the same two-slot partition the
19280        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
19281        // expose — the canonical dispatch a future per-axis-parametrised
19282        // walker (a future `feira app graph` per-list dep summary, a
19283        // future M4 per-cluster dev-closure-audit overlay the CR
19284        // materializer resolves per-CR) reads through. Prior to the
19285        // lift the two-block iteration lived open-coded at every walker,
19286        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
19287        // §I) would have had to grow a third block at every consumer.
19288        // A regression that dropped the `Dev` arm from `ALL` would flip
19289        // the collected pairs to `[(":deps", &[])]` alone and this pin
19290        // refuses that shape.
19291        let src = Caixa::template("host");
19292        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19293        let prod_dep = Dep {
19294            nome: "caixa-teia".to_string(),
19295            versao: "^0.1".to_string(),
19296            fonte: None,
19297            opcional: false,
19298            caracteristicas: Vec::new(),
19299        };
19300        let dev_dep = Dep {
19301            nome: "tatara-check".to_string(),
19302            versao: "*".to_string(),
19303            fonte: None,
19304            opcional: false,
19305            caracteristicas: Vec::new(),
19306        };
19307        caixa
19308            .push_dep(crate::dep::DepList::Prod, prod_dep)
19309            .expect("push into :deps succeeds");
19310        caixa
19311            .push_dep(crate::dep::DepList::Dev, dev_dep)
19312            .expect("push into :deps-dev succeeds");
19313        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
19314            .iter()
19315            .map(|&list| {
19316                let slice = caixa.deps_of(list);
19317                (list.as_str(), slice.len(), slice[0].nome())
19318            })
19319            .collect();
19320        assert_eq!(
19321            collected,
19322            vec![
19323                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
19324                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
19325            ]
19326        );
19327    }
19328
19329    #[test]
19330    fn caixa_deps_of_is_const_fn() {
19331        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
19332        // `const`-eval-surface posture. The typed-dispatch read
19333        // accessor forwards through the sibling `pub const fn`
19334        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
19335        // accessors on the two [`crate::dep::DepList`] enum arms —
19336        // every operator in the body is already `const`-callable
19337        // (`DepList` is a plain `#[derive(Copy)]` closed-set
19338        // discriminator so the `match` arms are const-evaluable, and
19339        // each arm dispatches through the sibling `pub const fn`
19340        // slice accessor). Any future accidental downgrade to
19341        // non-`const` fails the `deps_of_via_const_fn` wrapper below
19342        // at caixa-core build time with E0015 (`cannot call non-const
19343        // method`), strictly stronger than a runtime `assert!` and
19344        // side-stepping the destructor-in-const restriction the
19345        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
19346        // rule out on the direct-`const _: () = assert!(...)`
19347        // residence.
19348        //
19349        // Peer of the sibling outer-`Caixa` accessor family pins
19350        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
19351        // on the `&[String]` universal-axis surface,
19352        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
19353        // on the outer `&[T]` composite-slice surface,
19354        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
19355        // on the outer `Option<&Composite>` surface) — this pin
19356        // extends the `const`-eval-surface discipline onto the outer-
19357        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
19358        // dep-list axis, closing the outer-`Caixa` accessor family's
19359        // last unlifted `pub fn` on the read side.
19360        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
19361            c.deps_of(list)
19362        }
19363        let src = Caixa::template("host");
19364        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19365        // Empty-list arm: both `Prod` and `Dev` degenerate to the
19366        // empty slice with no silent `None` collapse — the
19367        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
19368        // that omits the slot lands on.
19369        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
19370        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
19371        assert_eq!(
19372            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
19373            caixa.deps()
19374        );
19375        assert_eq!(
19376            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
19377            caixa.deps_dev()
19378        );
19379        // Populated arms: each list carries its own entry, and the
19380        // wrapper / direct dispatches agree byte-for-byte on the
19381        // slice-view under both non-empty arms.
19382        let prod_dep = Dep {
19383            nome: "caixa-teia".to_string(),
19384            versao: "^0.1".to_string(),
19385            fonte: None,
19386            opcional: false,
19387            caracteristicas: Vec::new(),
19388        };
19389        let dev_dep = Dep {
19390            nome: "tatara-check".to_string(),
19391            versao: "*".to_string(),
19392            fonte: None,
19393            opcional: false,
19394            caracteristicas: Vec::new(),
19395        };
19396        caixa
19397            .push_dep(crate::dep::DepList::Prod, prod_dep)
19398            .expect("push into :deps succeeds");
19399        caixa
19400            .push_dep(crate::dep::DepList::Dev, dev_dep)
19401            .expect("push into :deps-dev succeeds");
19402        assert_eq!(
19403            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
19404            caixa.deps()
19405        );
19406        assert_eq!(
19407            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
19408            caixa.deps_dev()
19409        );
19410        assert_eq!(
19411            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
19412            "caixa-teia"
19413        );
19414        assert_eq!(
19415            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
19416            "tatara-check"
19417        );
19418    }
19419
19420    #[test]
19421    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
19422        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
19423        // must route its per-list [`crate::render::insert_first_seen`]
19424        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
19425        // rather than the pre-lift open-coded two-block iteration over
19426        // `self.deps()` + `self.deps_dev()`. A regression that dropped
19427        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
19428        // stop refusing within-list dups on the sibling arm; a
19429        // regression that flipped the arm-to-list-key mapping
19430        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
19431        // diagnostic surface. Both drifts surface here through a paired
19432        // duplicate-name refusal per arm plus an offending-list-key
19433        // check on the emitted [`DepError::DuplicateNome`] carrier.
19434        for &list in crate::dep::DepList::ALL {
19435            let src = Caixa::template("host");
19436            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19437            let dup = Dep {
19438                nome: "twin".to_string(),
19439                versao: "^0.1".to_string(),
19440                fonte: None,
19441                opcional: false,
19442                caracteristicas: Vec::new(),
19443            };
19444            match list {
19445                crate::dep::DepList::Prod => {
19446                    caixa.deps.push(dup.clone());
19447                    caixa.deps.push(dup);
19448                }
19449                crate::dep::DepList::Dev => {
19450                    caixa.deps_dev.push(dup.clone());
19451                    caixa.deps_dev.push(dup);
19452                }
19453            }
19454            let err = caixa
19455                .validate_deps()
19456                .expect_err("within-list duplicate :nome must refuse");
19457            assert_eq!(
19458                err,
19459                DepError::DuplicateNome {
19460                    nome: "twin".to_string(),
19461                    list: list.as_str(),
19462                },
19463                "validate_deps on {list} arm must emit \
19464                 DepError::DuplicateNome carrying the arm's own \
19465                 as_str() diagnostic — the arm-to-list-key mapping \
19466                 flowed through DepList::ALL + Caixa::deps_of"
19467            );
19468        }
19469    }
19470
19471    #[test]
19472    fn caixa_licenca_default_pins_canonical_mit_byte() {
19473        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
19474        // canonical SPDX-`"MIT"` byte today, the same license expression
19475        // every peer substrate-side consumer of the author-omitted
19476        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
19477        // `caixa-helm/src/lib.rs`, the future M4
19478        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
19479        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
19480        // crate's [`Caixa::validate_licenca`] docstring roadmap already
19481        // names as the second consumer) fills into its per-consumer
19482        // README/annotation emit site. Pin the literal here (peer with the
19483        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
19484        // [`crate::version::DEFAULT_GIT_REMOTE`] /
19485        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
19486        // on the sibling lifted-constant surfaces) so a future
19487        // substrate-side license-fallback rebrand surfaces here as a
19488        // coordinated edit-point: the sibling caixa-helm
19489        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
19490        // pinning test already pins the equality at the renderer-emit
19491        // axis; this pin closes the second coordinate of the pair by
19492        // anchoring the lifted constant's current byte to the canonical
19493        // CAIXA-SDLC §I license scaffold's documented shape.
19494        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
19495    }
19496
19497    // ── Caixa::validate_upgrade_from — compound per-Caixa entry gate on ──
19498    // ── the M2 `:upgrade-from` slot: folds the three top-level        ──
19499    // ── `crate::upgrade` validators (per-entry + cross-entry           ──
19500    // ── duplicate-`:from`, cross-slot `:from < :versao` precedence,   ──
19501    // ── cross-slot `:state-change` ↔ `:on-state-change` composition)  ──
19502    // ── onto one substrate primitive. Byte-for-byte equivalent to the ──
19503    // ── pre-fold three-block cascade at                               ──
19504    // ── `crate::layout::StandardLayout::verify` under the same        ──
19505    // ── canonical dispatch order.                                     ──
19506
19507    #[test]
19508    fn validate_upgrade_from_folds_per_entry_arm_matches_gate() {
19509        // Fail-before-pass-after per-arm equivalence pin on the
19510        // per-entry + cross-entry axis: a fixture whose `:upgrade-from`
19511        // carries a per-entry-invalid `:from` (git-tag shape `"v0.1.0"`,
19512        // which `semver::Version::parse` rejects) surfaces the same
19513        // [`crate::UpgradeError`] through the compound gate
19514        // [`Caixa::validate_upgrade_from`] and the standalone per-entry
19515        // gate [`crate::upgrade::validate_upgrade_from`] on the same
19516        // [`Caixa::upgrade_from`] slice. Pins the fold — a silent
19517        // regression that de-folded the per-entry arm would surface here
19518        // as a mismatch between the two dispatches. Sibling in shape to
19519        // the peer per-slot-≡-standalone equivalence pins the
19520        // [`crate::AplicacaoSpec::validate_contratos`] /
19521        // [`crate::MeshPolicy::validate`] /
19522        // [`crate::SupervisorSpec::validate_children`] compound gates
19523        // each carry on their axes.
19524        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19525        c.upgrade_from = vec![crate::UpgradeFromEntry {
19526            from: "v0.1.0".into(),
19527            instructions: vec![crate::UpgradeInstruction::Restart],
19528        }];
19529        let via_method = c.validate_upgrade_from().unwrap_err();
19530        let via_standalone = crate::upgrade::validate_upgrade_from(c.upgrade_from()).unwrap_err();
19531        assert_eq!(
19532            via_method, via_standalone,
19533            "Caixa::validate_upgrade_from must surface the per-entry \
19534             axis's diagnostic byte-equal to the standalone \
19535             `crate::upgrade::validate_upgrade_from` on the same \
19536             upgrade_from() slice"
19537        );
19538        assert!(
19539            matches!(
19540                via_method,
19541                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.1.0"
19542            ),
19543            "expected FromInvalid on the git-tag-shape `:from`, got {via_method:?}"
19544        );
19545    }
19546
19547    #[test]
19548    fn validate_upgrade_from_folds_versao_arm_matches_gate() {
19549        // Per-arm equivalence pin on the cross-slot `:from ↔ :versao`
19550        // precedence axis: a fixture with a well-formed `:from` (so the
19551        // per-entry arm passes) whose parsed semver is >= the caixa's
19552        // `:versao` under SemVer-2 precedence surfaces the same
19553        // [`crate::UpgradeError::FromNotBeforeVersao`] through both the
19554        // compound gate and the standalone
19555        // [`crate::upgrade::validate_upgrade_from_against_versao`] gate
19556        // keyed off the same `(upgrade_from, versao)` pair. Pins the
19557        // fold's second arm — reaching this arm through the compound
19558        // gate requires the per-entry arm to pass first, which itself
19559        // pins the per-arm cross-arm ordering.
19560        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19561        c.versao = "0.1.0".into();
19562        c.upgrade_from = vec![crate::UpgradeFromEntry {
19563            from: "0.2.0".into(),
19564            instructions: vec![crate::UpgradeInstruction::Restart],
19565        }];
19566        let via_method = c.validate_upgrade_from().unwrap_err();
19567        let via_standalone =
19568            crate::upgrade::validate_upgrade_from_against_versao(c.upgrade_from(), c.versao())
19569                .unwrap_err();
19570        assert_eq!(
19571            via_method, via_standalone,
19572            "Caixa::validate_upgrade_from must surface the \
19573             `:from >= :versao` diagnostic byte-equal to the standalone \
19574             `crate::upgrade::validate_upgrade_from_against_versao` on \
19575             the same (upgrade_from, versao) pair"
19576        );
19577        assert!(
19578            matches!(
19579                via_method,
19580                crate::UpgradeError::FromNotBeforeVersao { ref from, ref versao }
19581                    if from == "0.2.0" && versao == "0.1.0"
19582            ),
19583            "expected FromNotBeforeVersao carrying the offending pair, got {via_method:?}"
19584        );
19585    }
19586
19587    #[test]
19588    fn validate_upgrade_from_folds_behavior_arm_matches_gate() {
19589        // Per-arm equivalence pin on the cross-slot `:state-change ↔
19590        // :on-state-change` composition axis: a fixture with a
19591        // well-formed `:from` strictly less than `:versao` (so the
19592        // per-entry and versao arms both pass) whose `:instructions`
19593        // list carries a `(:state-change …)` instruction with no
19594        // `:behavior :on-state-change` callback declared surfaces the
19595        // same [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
19596        // through both the compound gate and the standalone
19597        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
19598        // gate keyed off the same `(upgrade_from, behavior)` pair.
19599        // Reaching this arm through the compound gate requires both
19600        // prior arms to pass first — the ordering pin below pins the
19601        // per-arm dispatch order explicitly.
19602        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19603        c.versao = "0.2.0".into();
19604        c.behavior = None;
19605        c.upgrade_from = vec![crate::UpgradeFromEntry {
19606            from: "0.1.0".into(),
19607            instructions: vec![
19608                crate::UpgradeInstruction::LoadModule {
19609                    module: "demo".into(),
19610                },
19611                crate::UpgradeInstruction::StateChange {
19612                    script: std::path::PathBuf::from("lib/m.lisp"),
19613                },
19614                crate::UpgradeInstruction::SoftPurge {
19615                    module: "demo-old".into(),
19616                },
19617            ],
19618        }];
19619        let via_method = c.validate_upgrade_from().unwrap_err();
19620        let via_standalone =
19621            crate::upgrade::validate_upgrade_from_against_behavior(c.upgrade_from(), c.behavior())
19622                .unwrap_err();
19623        assert_eq!(
19624            via_method, via_standalone,
19625            "Caixa::validate_upgrade_from must surface the \
19626             `:state-change` ↔ `:on-state-change` composition \
19627             diagnostic byte-equal to the standalone \
19628             `crate::upgrade::validate_upgrade_from_against_behavior` \
19629             on the same (upgrade_from, behavior) pair"
19630        );
19631        assert!(
19632            matches!(
19633                via_method,
19634                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback {
19635                    ref from,
19636                    ref script,
19637                } if from == "0.1.0" && script == &std::path::PathBuf::from("lib/m.lisp")
19638            ),
19639            "expected StateChangeWithoutOnStateChangeCallback carrying \
19640             the offending (from, script) pair, got {via_method:?}"
19641        );
19642    }
19643
19644    #[test]
19645    fn validate_upgrade_from_per_entry_arm_fires_before_versao_arm() {
19646        // Cross-arm ordering pin between the first two arms of the
19647        // fold: a fixture carrying BOTH a per-entry-invalid `:from`
19648        // (`"v0.0.5"` — git-tag shape rejected by
19649        // [`crate::upgrade::validate_upgrade_from`]) AND a would-be
19650        // versao-precedence violation on a second entry (`"0.2.0" >=
19651        // :versao "0.1.0"`) surfaces the per-entry diagnostic first
19652        // through the compound gate. Sanity assertion: the second
19653        // entry alone under the same `:versao` trips the versao arm
19654        // on its own via the standalone
19655        // [`crate::upgrade::validate_upgrade_from_against_versao`], so
19656        // the per-entry-first surfacing is a real ordering property,
19657        // not a case where the versao arm silently accepts the
19658        // fixture. Pins the pre-fold layout wire-up's canonical
19659        // dispatch order (per-entry → versao → behavior) as a
19660        // property of the substrate primitive rather than a
19661        // convention of the layout call site.
19662        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19663        c.versao = "0.1.0".into();
19664        c.upgrade_from = vec![
19665            crate::UpgradeFromEntry {
19666                from: "v0.0.5".into(),
19667                instructions: vec![crate::UpgradeInstruction::Restart],
19668            },
19669            crate::UpgradeFromEntry {
19670                from: "0.2.0".into(),
19671                instructions: vec![crate::UpgradeInstruction::Restart],
19672            },
19673        ];
19674        let err = c.validate_upgrade_from().unwrap_err();
19675        assert!(
19676            matches!(
19677                err,
19678                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.0.5"
19679            ),
19680            "per-entry arm must fire before versao arm — expected \
19681             FromInvalid on `v0.0.5`, got {err:?}"
19682        );
19683        // Sanity: the versao-violating second entry alone under the
19684        // same `:versao` trips the versao arm on its own — proves the
19685        // per-entry-first surfacing above is a real ordering property.
19686        let sanity = crate::upgrade::validate_upgrade_from_against_versao(
19687            &[crate::UpgradeFromEntry {
19688                from: "0.2.0".into(),
19689                instructions: vec![crate::UpgradeInstruction::Restart],
19690            }],
19691            "0.1.0",
19692        )
19693        .unwrap_err();
19694        assert!(
19695            matches!(sanity, crate::UpgradeError::FromNotBeforeVersao { .. }),
19696            "sanity: the versao-violating fixture alone must trip the \
19697             versao arm — got {sanity:?}"
19698        );
19699    }
19700
19701    #[test]
19702    fn validate_upgrade_from_versao_arm_fires_before_behavior_arm() {
19703        // Cross-arm ordering pin between the second and third arms of
19704        // the fold: a fixture carrying BOTH a versao-precedence
19705        // violation (`:from "0.2.0" >= :versao "0.1.0"`) AND a
19706        // would-be missing-callback violation (a `(:state-change …)`
19707        // instruction with no `:behavior :on-state-change`) surfaces
19708        // the versao diagnostic first through the compound gate.
19709        // Sanity assertion: the missing-callback fixture alone (with
19710        // the versao-precedence violation removed by bumping
19711        // `:versao` past `:from`) trips the behavior arm on its own
19712        // via the standalone
19713        // [`crate::upgrade::validate_upgrade_from_against_behavior`],
19714        // so the versao-first surfacing is a real ordering property,
19715        // not a case where the behavior arm silently accepts the
19716        // fixture.
19717        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19718        c.versao = "0.1.0".into();
19719        c.behavior = None;
19720        c.upgrade_from = vec![crate::UpgradeFromEntry {
19721            from: "0.2.0".into(),
19722            instructions: vec![
19723                crate::UpgradeInstruction::LoadModule {
19724                    module: "demo".into(),
19725                },
19726                crate::UpgradeInstruction::StateChange {
19727                    script: std::path::PathBuf::from("lib/m.lisp"),
19728                },
19729            ],
19730        }];
19731        let err = c.validate_upgrade_from().unwrap_err();
19732        assert!(
19733            matches!(
19734                err,
19735                crate::UpgradeError::FromNotBeforeVersao { ref from, .. } if from == "0.2.0"
19736            ),
19737            "versao arm must fire before behavior arm — expected \
19738             FromNotBeforeVersao on `0.2.0`, got {err:?}"
19739        );
19740        // Sanity: the same instructions under a `:versao` that
19741        // accepts the `:from` (so the versao arm passes) trips the
19742        // behavior arm — proves the versao-first surfacing above is a
19743        // real ordering property.
19744        let sanity = crate::upgrade::validate_upgrade_from_against_behavior(
19745            &[crate::UpgradeFromEntry {
19746                from: "0.2.0".into(),
19747                instructions: vec![
19748                    crate::UpgradeInstruction::LoadModule {
19749                        module: "demo".into(),
19750                    },
19751                    crate::UpgradeInstruction::StateChange {
19752                        script: std::path::PathBuf::from("lib/m.lisp"),
19753                    },
19754                ],
19755            }],
19756            None,
19757        )
19758        .unwrap_err();
19759        assert!(
19760            matches!(
19761                sanity,
19762                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback { .. }
19763            ),
19764            "sanity: the missing-callback fixture alone must trip the \
19765             behavior arm — got {sanity:?}"
19766        );
19767    }
19768
19769    #[test]
19770    fn validate_upgrade_from_accepts_clean_fixture() {
19771        // Positive control: a well-formed `:upgrade-from` (single entry
19772        // with `:from` strictly less than `:versao`, no
19773        // `:state-change` instruction so the behavior arm is vacuous)
19774        // passes the compound gate cleanly. A future tightening of any
19775        // one arm's accepted set surfaces here as a test failure
19776        // first. Mirrors the peer `validate_versao_accepts_canonical_forms`
19777        // positive-control posture on the sibling per-Caixa gate.
19778        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19779        c.versao = "0.2.0".into();
19780        c.upgrade_from = vec![crate::UpgradeFromEntry {
19781            from: "0.1.0".into(),
19782            instructions: vec![crate::UpgradeInstruction::Restart],
19783        }];
19784        c.validate_upgrade_from()
19785            .expect("clean fixture must pass the compound `:upgrade-from` gate");
19786    }
19787
19788    #[test]
19789    fn validate_upgrade_from_accepts_empty_upgrade_from() {
19790        // Positive control on the empty-list arm: a caixa without any
19791        // `:upgrade-from` block (the default `Vec::new()`
19792        // `#[serde(default)]` folds an omitted slot onto) passes the
19793        // compound gate cleanly regardless of `:versao` or `:behavior`
19794        // — each of the three standalone validators is vacuous on the
19795        // empty entry list. Pins the identity element of the fold on
19796        // the empty-slot side.
19797        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19798        assert!(
19799            c.upgrade_from().is_empty(),
19800            "template caixa must carry an empty :upgrade-from — got {:?}",
19801            c.upgrade_from()
19802        );
19803        c.validate_upgrade_from()
19804            .expect("empty :upgrade-from must pass the compound gate cleanly");
19805    }
19806
19807    // ── Caixa::validate_limits — compound per-Caixa entry gate on   ──
19808    // ── the M2 `:limits` slot: folds the                            ──
19809    // ── [`crate::LimitsSpec::validate`] four-axis cascade on the    ──
19810    // ── present-slot arm and the `Option::None` identity element on ──
19811    // ── the absent-slot arm onto one substrate primitive.           ──
19812    // ── Byte-for-byte equivalent to the pre-fold                    ──
19813    // ── `if let Some(l) = caixa.limits() { l.validate() }`          ──
19814    // ── unwrap-and-dispatch pattern at                              ──
19815    // ── `crate::layout::StandardLayout::verify` (`layout.rs`).      ──
19816
19817    #[test]
19818    fn validate_limits_folds_arm_matches_gate() {
19819        // Fail-before-pass-after per-arm equivalence pin on the
19820        // present-slot arm: a fixture whose `:limits` carries a
19821        // zero-floor-violating `:fuel` (`Some(0)`, which
19822        // [`crate::LimitsSpec::validate`] rejects through
19823        // [`crate::LimitsError::FuelZero`]) surfaces the same
19824        // [`crate::LimitsError`] byte-equal through both the compound
19825        // gate [`Caixa::validate_limits`] and the standalone
19826        // [`crate::LimitsSpec::validate`] gate on the same `LimitsSpec`
19827        // value. Pins the fold — a silent regression that de-folded
19828        // the present-slot arm would surface here as a mismatch
19829        // between the two dispatches. Sibling in shape to the peer
19830        // per-arm equivalence pins the
19831        // [`crate::AplicacaoSpec::validate_contratos`] /
19832        // [`crate::MeshPolicy::validate`] /
19833        // [`crate::SupervisorSpec::validate_children`] /
19834        // [`Caixa::validate_upgrade_from`] compound gates each carry
19835        // on their axes.
19836        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19837        let l = crate::LimitsSpec {
19838            memory: None,
19839            fuel: Some(0),
19840            wall_clock: None,
19841            cpu: None,
19842        };
19843        c.limits = Some(l);
19844        let via_method = c.validate_limits().unwrap_err();
19845        let via_standalone = l.validate().unwrap_err();
19846        assert_eq!(
19847            via_method, via_standalone,
19848            "Caixa::validate_limits must surface the present-slot \
19849             arm's diagnostic byte-equal to the standalone \
19850             `LimitsSpec::validate` on the same `LimitsSpec` value"
19851        );
19852        assert!(
19853            matches!(via_method, crate::LimitsError::FuelZero),
19854            "expected FuelZero on the zero-floor-violating `:fuel`, \
19855             got {via_method:?}"
19856        );
19857    }
19858
19859    #[test]
19860    fn validate_limits_accepts_none() {
19861        // Positive control on the absent-slot arm (the fold's identity
19862        // element): a caixa without any `:limits` block (the
19863        // canonical "no bound declared — engine-default applies"
19864        // author shape [`crate::LimitsSpec::is_empty`]'s per-axis
19865        // `None` cascade reads, and the shape the [`Caixa::template`]
19866        // scaffold emits by construction) passes the compound gate
19867        // cleanly, regardless of any per-axis defect a subsequent
19868        // `Some(_)` binding would surface. Pins the identity element
19869        // of the fold on the absent-slot side, matching the peer
19870        // `validate_upgrade_from_accepts_empty_upgrade_from` positive-
19871        // control posture on the sibling M2 slot.
19872        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19873        assert!(
19874            c.limits().is_none(),
19875            "template caixa must carry an absent :limits — got {:?}",
19876            c.limits()
19877        );
19878        c.validate_limits()
19879            .expect("absent :limits must pass the compound gate cleanly");
19880    }
19881
19882    #[test]
19883    fn validate_limits_accepts_clean_fixture() {
19884        // Positive control on the present-slot arm: a caixa whose
19885        // `:limits` is `Some(LimitsSpec::default())` (all four axes
19886        // `None` — every axis absent under the outer `Some(_)`
19887        // binding, so every present-slot arm on
19888        // [`crate::LimitsSpec::validate`] is vacuous) passes the
19889        // compound gate cleanly. A future tightening of any one axis
19890        // that surfaces a diagnostic on the all-`None` `LimitsSpec`
19891        // would land here as a test failure first. Pins the
19892        // present-slot arm's accept-shape on the canonical
19893        // "declared-but-empty" author fixture the
19894        // `limits_round_trip_via_json` peer already round-trips
19895        // (`caixa-core/src/manifest.rs:6971`).
19896        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19897        c.limits = Some(crate::LimitsSpec::default());
19898        c.validate_limits()
19899            .expect("Some(LimitsSpec::default()) must pass the compound gate cleanly");
19900    }
19901
19902    // ── Caixa::validate_behavior — compound per-Caixa entry gate on ──
19903    // ── the M2 `:behavior` slot's pure value-shape surface: folds   ──
19904    // ── the [`crate::BehaviorSpec::validate`] six-slot cascade on   ──
19905    // ── the present-slot arm and the `Option::None` identity        ──
19906    // ── element on the absent-slot arm onto one substrate primitive.──
19907    // ── Byte-for-byte equivalent to the pre-fold                    ──
19908    // ── `if let Some(b) = caixa.behavior() { b.validate() }`        ──
19909    // ── unwrap-and-dispatch pattern at                              ──
19910    // ── `crate::layout::StandardLayout::verify` (`layout.rs`). The  ──
19911    // ── on-disk callback-path existence walk stays open-coded at    ──
19912    // ── the layout altitude because it needs the                    ──
19913    // ── [`crate::layout::LayoutInvariants::exists`] filesystem       ──
19914    // ── oracle the pure typed-shape surface has no reference to —   ──
19915    // ── mirror of the peer M2 `:upgrade-from` per-instruction       ──
19916    // ── script-path existence probe that stayed at the layout       ──
19917    // ── altitude after the [`Caixa::validate_upgrade_from`] lift    ──
19918    // ── (d6801df) for the same reason.                              ──
19919
19920    #[test]
19921    fn validate_behavior_folds_arm_matches_gate() {
19922        // Fail-before-pass-after per-arm equivalence pin on the
19923        // present-slot arm: a fixture whose `:behavior` carries an
19924        // absolute-path `:on-init` (`"/etc/passwd"`, which
19925        // [`crate::BehaviorSpec::validate`] rejects through
19926        // [`crate::BehaviorError::AbsolutePath`]) surfaces the same
19927        // [`crate::BehaviorError`] byte-equal through both the
19928        // compound gate [`Caixa::validate_behavior`] and the standalone
19929        // [`crate::BehaviorSpec::validate`] gate on the same
19930        // `BehaviorSpec` value. Pins the fold — a silent regression
19931        // that de-folded the present-slot arm would surface here as a
19932        // mismatch between the two dispatches. Sibling in shape to the
19933        // peer per-arm equivalence pins the
19934        // [`Caixa::validate_limits`] (baa4688),
19935        // [`Caixa::validate_upgrade_from`] (d6801df),
19936        // [`crate::MeshPolicy::validate`],
19937        // [`crate::AplicacaoSpec::validate_contratos`], and
19938        // [`crate::SupervisorSpec::validate_children`] compound gates
19939        // each carry on their axes.
19940        use crate::BehaviorSpec;
19941        use std::path::PathBuf;
19942        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19943        let b = BehaviorSpec {
19944            on_init: Some(PathBuf::from("/etc/passwd")),
19945            ..Default::default()
19946        };
19947        c.behavior = Some(b.clone());
19948        let via_method = c.validate_behavior().unwrap_err();
19949        let via_standalone = b.validate().unwrap_err();
19950        assert_eq!(
19951            via_method, via_standalone,
19952            "Caixa::validate_behavior must surface the present-slot \
19953             arm's diagnostic byte-equal to the standalone \
19954             `BehaviorSpec::validate` on the same `BehaviorSpec` value"
19955        );
19956        assert!(
19957            matches!(via_method, crate::BehaviorError::AbsolutePath { .. }),
19958            "expected AbsolutePath on the absolute `:on-init` path, \
19959             got {via_method:?}"
19960        );
19961    }
19962
19963    #[test]
19964    fn validate_behavior_accepts_none() {
19965        // Positive control on the absent-slot arm (the fold's identity
19966        // element): a caixa without any `:behavior` block (the
19967        // canonical "no callback declared — the runtime falls back to
19968        // the wasm-engine's default per arm" author shape
19969        // [`crate::BehaviorSpec::is_empty`]'s per-slot `None` cascade
19970        // reads, and the shape the [`Caixa::template`] scaffold emits
19971        // by construction) passes the compound gate cleanly,
19972        // regardless of any per-slot defect a subsequent `Some(_)`
19973        // binding would surface. Pins the identity element of the fold
19974        // on the absent-slot side, matching the peer
19975        // `validate_limits_accepts_none` (baa4688) and
19976        // `validate_upgrade_from_accepts_empty_upgrade_from` (d6801df)
19977        // positive-control postures on the sibling M2 slots.
19978        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19979        assert!(
19980            c.behavior().is_none(),
19981            "template caixa must carry an absent :behavior — got {:?}",
19982            c.behavior()
19983        );
19984        c.validate_behavior()
19985            .expect("absent :behavior must pass the compound gate cleanly");
19986    }
19987
19988    #[test]
19989    fn validate_behavior_accepts_clean_fixture() {
19990        // Positive control on the present-slot arm: a caixa whose
19991        // `:behavior` is `Some(BehaviorSpec::default())` (all six
19992        // slots `None` — every slot absent under the outer `Some(_)`
19993        // binding, so every present-slot arm on
19994        // [`crate::BehaviorSpec::validate`] is vacuous) passes the
19995        // compound gate cleanly. A future tightening of any one arm
19996        // that surfaces a diagnostic on the all-`None` `BehaviorSpec`
19997        // would land here as a test failure first. Pins the
19998        // present-slot arm's accept-shape on the canonical
19999        // "declared-but-empty" author fixture the sibling
20000        // `empty_behavior_round_trip` peer already round-trips
20001        // (`caixa-core/src/behavior.rs` tests). Mirror of the peer
20002        // `validate_limits_accepts_clean_fixture` (baa4688)
20003        // positive-control posture on the sibling M2 `:limits` slot.
20004        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20005        c.behavior = Some(crate::BehaviorSpec::default());
20006        c.validate_behavior()
20007            .expect("Some(BehaviorSpec::default()) must pass the compound gate cleanly");
20008    }
20009
20010    // ── Caixa::validate_deps — compound per-Caixa entry gate on the ──
20011    // ── dep-graph axis: folds the two standalone validators         ──
20012    // ── (per-entry + within-list duplicate walk that this method    ──
20013    // ── opened on, cross-slot self-edge via                         ──
20014    // ── `crate::dep::validate_no_self_dep`) onto one substrate      ──
20015    // ── primitive. Byte-for-byte equivalent to the pre-fold         ──
20016    // ── two-block cascade at                                        ──
20017    // ── `crate::layout::StandardLayout::verify` under the same      ──
20018    // ── canonical dispatch order (per-entry → self-edge).           ──
20019
20020    #[test]
20021    fn validate_deps_folds_per_entry_arm_matches_gate() {
20022        // Fail-before-pass-after per-arm equivalence pin on the
20023        // per-entry + within-list duplicate axis: a fixture whose
20024        // `:deps` carries a per-entry-invalid `:versao` (`"^bad"`,
20025        // which [`crate::parse_requirement`] rejects) surfaces the
20026        // same [`crate::DepError`] through the compound gate
20027        // [`Caixa::validate_deps`] and the standalone per-entry walk
20028        // ([`Dep::validate`]) on the offending entry. Pins the
20029        // fold — a silent regression that de-folded the per-entry arm
20030        // would surface here as a mismatch between the two
20031        // dispatches. Sibling in shape to the peer
20032        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
20033        // per-arm equivalence pin (d6801df) on the M2
20034        // `:upgrade-from` compound gate's per-entry arm, extended
20035        // here onto the universal-axis `:deps` compound gate's
20036        // per-entry arm.
20037        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20038        c.deps = vec![Dep::simple("d", "^bad")];
20039        let via_method = c.validate_deps().unwrap_err();
20040        let via_standalone = c.deps()[0].validate().unwrap_err();
20041        assert_eq!(
20042            via_method, via_standalone,
20043            "Caixa::validate_deps must surface the per-entry arm's \
20044             diagnostic byte-equal to the standalone \
20045             `Dep::validate` on the same offending entry",
20046        );
20047        assert!(
20048            matches!(
20049                via_method,
20050                DepError::VersaoInvalid { ref nome, .. } if nome == "d"
20051            ),
20052            "expected VersaoInvalid on the malformed :versao, got {via_method:?}",
20053        );
20054    }
20055
20056    #[test]
20057    fn validate_deps_folds_self_edge_arm_matches_gate() {
20058        // Per-arm equivalence pin on the cross-slot self-edge axis:
20059        // a fixture whose `:deps` lists the caixa's own `:nome`
20060        // (a self-dep, which
20061        // [`crate::dep::validate_no_self_dep`] rejects as a
20062        // structurally-invalid one-node cycle in the lacre closure's
20063        // dep-graph) surfaces the same [`crate::DepError::DepIsSelf`]
20064        // through both the compound gate and the standalone
20065        // [`crate::dep::validate_no_self_dep`] gate keyed off the
20066        // same `(deps, deps_dev, nome)` triple. Pins the fold's
20067        // second arm — reaching this arm through the compound gate
20068        // requires the per-entry + within-list duplicate walk to
20069        // pass first, which itself pins one cross-arm ordering step.
20070        // Sibling in shape to the peer
20071        // `validate_upgrade_from_folds_versao_arm_matches_gate` /
20072        // `_folds_behavior_arm_matches_gate` cross-slot equivalence
20073        // pins (d6801df) on the M2 `:upgrade-from` compound gate's
20074        // cross-slot arms.
20075        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20076        c.deps = vec![Dep::simple("demo", "^0.1")];
20077        let via_method = c.validate_deps().unwrap_err();
20078        let via_standalone =
20079            crate::dep::validate_no_self_dep(c.deps(), c.deps_dev(), c.nome()).unwrap_err();
20080        assert_eq!(
20081            via_method, via_standalone,
20082            "Caixa::validate_deps must surface the cross-slot \
20083             self-edge diagnostic byte-equal to the standalone \
20084             `crate::dep::validate_no_self_dep` on the same \
20085             (deps, deps_dev, nome) triple",
20086        );
20087        assert!(
20088            matches!(
20089                via_method,
20090                DepError::DepIsSelf { ref nome, list }
20091                    if nome == "demo" && list == crate::render::DEP_AUTHOR_KEY_DEPS
20092            ),
20093            "expected DepIsSelf carrying (nome=\"demo\", list=\":deps\"), got {via_method:?}",
20094        );
20095    }
20096
20097    #[test]
20098    fn validate_deps_per_entry_arm_fires_before_self_edge_arm() {
20099        // Cross-arm ordering pin between the two arms of the fold:
20100        // a fixture carrying BOTH a per-entry-invalid `:versao`
20101        // (`"^bad"` — [`crate::parse_requirement`] rejects the
20102        // requirement grammar) on a non-self-dep entry AND a
20103        // would-be self-edge violation on a second entry (the
20104        // caixa's own `:nome` "demo") surfaces the per-entry
20105        // diagnostic first through the compound gate. Sanity
20106        // assertion: the second entry alone under the same parent
20107        // `:nome` trips the self-edge arm on its own via the
20108        // standalone [`crate::dep::validate_no_self_dep`], so the
20109        // per-entry-first surfacing is a real ordering property,
20110        // not a case where the self-edge arm silently accepts the
20111        // fixture. Pins the pre-fold layout wire-up's canonical
20112        // dispatch order (per-entry + within-list duplicate →
20113        // self-edge) as a property of the substrate primitive
20114        // rather than a convention of the layout call site. Sibling
20115        // in shape to
20116        // `validate_upgrade_from_per_entry_arm_fires_before_versao_arm`
20117        // (d6801df) on the M2 `:upgrade-from` compound gate's
20118        // per-arm ordering property.
20119        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20120        c.deps = vec![
20121            Dep::simple("orquestra", "^bad"),
20122            Dep::simple("demo", "^0.1"),
20123        ];
20124        let err = c.validate_deps().unwrap_err();
20125        assert!(
20126            matches!(
20127                err,
20128                DepError::VersaoInvalid { ref nome, .. } if nome == "orquestra"
20129            ),
20130            "per-entry arm must fire before self-edge arm — expected \
20131             VersaoInvalid on \"orquestra\", got {err:?}",
20132        );
20133        // Sanity: the self-referential entry alone under the same
20134        // parent `:nome` trips the self-edge arm on its own — proves
20135        // the per-entry-first surfacing above is a real ordering
20136        // property, not a case where the self-edge arm silently
20137        // accepts the fixture.
20138        let sanity = crate::dep::validate_no_self_dep(&[Dep::simple("demo", "^0.1")], &[], "demo")
20139            .unwrap_err();
20140        assert!(
20141            matches!(sanity, DepError::DepIsSelf { ref nome, .. } if nome == "demo"),
20142            "sanity: the self-referential entry alone must trip the \
20143             self-edge arm — got {sanity:?}",
20144        );
20145    }
20146
20147    #[test]
20148    fn validate_deps_accepts_clean_fixture() {
20149        // Positive control: a well-formed dep-graph (one `:deps`
20150        // entry naming a non-self DNS-1123 nome + Cargo-shaped
20151        // requirement, one `:deps-dev` entry on a distinct non-self
20152        // nome) passes the compound gate cleanly. A future
20153        // tightening of either arm's accepted set surfaces here as
20154        // a test failure first. Mirrors the peer
20155        // `validate_upgrade_from_accepts_clean_fixture` positive-
20156        // control posture on the sibling per-Caixa compound gate.
20157        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20158        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
20159        c.deps_dev = vec![Dep::simple("caixa-lint", "^0.2")];
20160        c.validate_deps()
20161            .expect("clean fixture must pass the compound `:deps` gate");
20162    }
20163
20164    #[test]
20165    fn validate_deps_accepts_empty_deps_lists() {
20166        // Positive control on the empty-list arm: a caixa without
20167        // any `:deps` or `:deps-dev` entries (the default
20168        // `Vec::new()` `#[serde(default)]` folds an omitted slot
20169        // onto) passes the compound gate cleanly regardless of
20170        // `:nome` — both the per-entry walk and the self-edge walk
20171        // are vacuous on the empty entry list. Pins the identity
20172        // element of the fold on the empty-slot side, peer with the
20173        // `validate_upgrade_from_accepts_empty_upgrade_from` empty-
20174        // arm positive control (d6801df) on the sibling
20175        // `:upgrade-from` compound gate.
20176        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20177        assert!(
20178            c.deps().is_empty(),
20179            "template caixa must carry an empty :deps — got {:?}",
20180            c.deps(),
20181        );
20182        assert!(
20183            c.deps_dev().is_empty(),
20184            "template caixa must carry an empty :deps-dev — got {:?}",
20185            c.deps_dev(),
20186        );
20187        c.validate_deps()
20188            .expect("empty :deps / :deps-dev must pass the compound gate cleanly");
20189    }
20190
20191    // ── Caixa::validate_aplicacao_shape — compound per-Caixa gate ────────
20192
20193    /// Build a minimal well-formed Aplicacao fixture on top of the
20194    /// canonical template. Every arm of the compound gate then patches
20195    /// exactly one axis away from clean so its per-arm diagnostic
20196    /// surfaces without collateral noise from a peer slot.
20197    fn aplicacao_fixture(nome: &str) -> Caixa {
20198        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
20199        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
20200        c.kind = CaixaKind::Aplicacao;
20201        c.bibliotecas = vec![];
20202        c.membros = vec![
20203            Membro {
20204                caixa: "checkout".into(),
20205                versao: "^0.1".into(),
20206            },
20207            Membro {
20208                caixa: "cart".into(),
20209                versao: "^0.1".into(),
20210            },
20211        ];
20212        // `:placement` defaults to `Replicated` with an empty
20213        // `:clusters` list which
20214        // [`crate::AplicacaoSpec::validate_placement`] refuses; every
20215        // per-strategy variant needs at least one named cluster (per
20216        // MESH-COMPOSITION §II.1). Pin a single-cluster `SingleNode`
20217        // placement so the typed-shape cascade passes cleanly and the
20218        // per-arm fixtures below can each patch exactly one axis.
20219        c.placement = Some(Placement {
20220            estrategia: PlacementStrategy::SingleNode,
20221            clusters: vec!["rio".into()],
20222            shard_key: None,
20223            affinity: None,
20224        });
20225        c
20226    }
20227
20228    #[test]
20229    fn validate_aplicacao_shape_folds_view_arm_matches_gate() {
20230        // Fail-before-pass-after per-arm equivalence pin on the
20231        // typed-shape cascade arm: a fixture whose typed
20232        // [`crate::AplicacaoSpec`] view fails
20233        // [`crate::AplicacaoSpec::validate`] (here — empty `:membros`,
20234        // which [`crate::AplicacaoSpec::validate_membros`] rejects as
20235        // [`crate::AplicacaoError::NoMembros`] at the first per-slot
20236        // gate) surfaces the same [`crate::AplicacaoError`] diagnostic
20237        // through both the compound gate
20238        // [`Caixa::validate_aplicacao_shape`] and the standalone
20239        // [`crate::AplicacaoSpec::validate`] on the same folded view.
20240        // Pins the fold — a silent regression that de-folded the
20241        // typed-shape arm would surface here as a mismatch between the
20242        // two dispatches. Sibling in shape to the peer
20243        // `validate_deps_folds_per_entry_arm_matches_gate` (b5dd55e) /
20244        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
20245        // (d6801df) per-arm equivalence pins on the sibling per-slot
20246        // compound gates.
20247        let mut c = aplicacao_fixture("demo");
20248        c.membros = vec![];
20249        let via_method = c.validate_aplicacao_shape().unwrap_err();
20250        let via_standalone = c.aplicacao_view().unwrap().validate().unwrap_err();
20251        assert_eq!(
20252            via_method, via_standalone,
20253            "Caixa::validate_aplicacao_shape must surface the typed-\
20254             shape arm's diagnostic byte-equal to the standalone \
20255             `AplicacaoSpec::validate` on the same folded view",
20256        );
20257        assert!(
20258            matches!(via_method, crate::AplicacaoError::NoMembros),
20259            "expected NoMembros on the empty :membros, got {via_method:?}",
20260        );
20261    }
20262
20263    #[test]
20264    fn validate_aplicacao_shape_folds_self_membership_arm_matches_gate() {
20265        // Per-arm equivalence pin on the cross-slot self-edge axis: a
20266        // fixture whose `:membros` names the Aplicacao's own `:nome`
20267        // (which [`crate::aplicacao::validate_no_self_membership`]
20268        // rejects as [`crate::AplicacaoError::MembroIsSelfAplicacao`],
20269        // a one-node lacre-closure recursion in the Aplicacao's
20270        // mesh-graph) surfaces the same
20271        // [`crate::AplicacaoError::MembroIsSelfAplicacao`] through both
20272        // the compound gate and the standalone
20273        // [`crate::aplicacao::validate_no_self_membership`] keyed off
20274        // the same `(membros, nome)` pair. Pins the fold's second arm
20275        // — reaching this arm through the compound gate requires the
20276        // typed-shape cascade to pass first, which itself pins one
20277        // cross-arm ordering step. Sibling in shape to the peer
20278        // `validate_deps_folds_self_edge_arm_matches_gate` (b5dd55e)
20279        // cross-slot equivalence pin on the sibling per-slot compound
20280        // gate.
20281        use crate::aplicacao::Membro;
20282        let mut c = aplicacao_fixture("demo");
20283        c.membros = vec![Membro {
20284            caixa: "demo".into(),
20285            versao: "^0.1".into(),
20286        }];
20287        let via_method = c.validate_aplicacao_shape().unwrap_err();
20288        let via_standalone =
20289            crate::aplicacao::validate_no_self_membership(c.membros(), c.nome()).unwrap_err();
20290        assert_eq!(
20291            via_method, via_standalone,
20292            "Caixa::validate_aplicacao_shape must surface the cross-\
20293             slot self-edge diagnostic byte-equal to the standalone \
20294             `aplicacao::validate_no_self_membership` on the same \
20295             (membros, nome) pair",
20296        );
20297        assert!(
20298            matches!(
20299                via_method,
20300                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa } if caixa == "demo"
20301            ),
20302            "expected MembroIsSelfAplicacao carrying (caixa=\"demo\"), \
20303             got {via_method:?}",
20304        );
20305    }
20306
20307    #[test]
20308    fn validate_aplicacao_shape_view_arm_fires_before_self_membership_arm() {
20309        // Cross-arm ordering pin between the two arms of the fold: a
20310        // fixture carrying BOTH a typed-shape violation (a `:contratos`
20311        // edge whose `:para` is not a declared member — rejected by
20312        // [`crate::AplicacaoSpec::validate_contratos`] as
20313        // [`crate::AplicacaoError::ContratoMemberMissing`]) AND a
20314        // would-be self-edge violation (a `:membros` entry naming the
20315        // caixa's own `:nome`) surfaces the typed-shape diagnostic
20316        // first through the compound gate. Sanity assertion: the
20317        // self-referential `:membros` entry alone under the same
20318        // parent `:nome` trips the self-edge arm on its own via the
20319        // standalone [`crate::aplicacao::validate_no_self_membership`],
20320        // so the typed-shape-first surfacing is a real ordering
20321        // property, not a case where the self-edge arm silently
20322        // accepts the fixture. Pins the pre-fold layout wire-up's
20323        // canonical dispatch order (typed-shape cascade → cross-slot
20324        // self-edge) as a property of the substrate primitive rather
20325        // than a convention of the layout call site. Sibling in shape
20326        // to `validate_deps_per_entry_arm_fires_before_self_edge_arm`
20327        // (b5dd55e) on the sibling per-slot compound gate's per-arm
20328        // ordering property.
20329        use crate::aplicacao::{Membro, WitContract};
20330        let mut c = aplicacao_fixture("demo");
20331        c.membros = vec![Membro {
20332            caixa: "demo".into(),
20333            versao: "^0.1".into(),
20334        }];
20335        c.contratos = vec![WitContract {
20336            de: "demo".into(),
20337            para: "orphan".into(),
20338            wit: "wasi:http/proxy".into(),
20339            endpoint: Some("/x".into()),
20340            subject: None,
20341            slot: None,
20342        }];
20343        let err = c.validate_aplicacao_shape().unwrap_err();
20344        assert!(
20345            matches!(
20346                err,
20347                crate::AplicacaoError::ContratoMemberMissing { ref caixa }
20348                    if caixa == "orphan"
20349            ),
20350            "typed-shape arm must fire before self-edge arm — expected \
20351             ContratoMemberMissing on \"orphan\", got {err:?}",
20352        );
20353        // Sanity: the self-referential `:membros` entry alone under
20354        // the same parent `:nome` trips the self-edge arm on its own
20355        // — proves the typed-shape-first surfacing above is a real
20356        // ordering property, not a case where the self-edge arm
20357        // silently accepts the fixture.
20358        let sanity = crate::aplicacao::validate_no_self_membership(
20359            &[Membro {
20360                caixa: "demo".into(),
20361                versao: "^0.1".into(),
20362            }],
20363            "demo",
20364        )
20365        .unwrap_err();
20366        assert!(
20367            matches!(
20368                sanity,
20369                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa }
20370                    if caixa == "demo"
20371            ),
20372            "sanity: the self-referential :membros entry alone must \
20373             trip the self-edge arm — got {sanity:?}",
20374        );
20375    }
20376
20377    #[test]
20378    fn validate_aplicacao_shape_accepts_non_aplicacao_kind() {
20379        // Positive control on the identity-element arm: every non-
20380        // Aplicacao kind passes the compound gate trivially — the
20381        // paired [`Caixa::aplicacao_view`] accessor returns `None`
20382        // off the Aplicacao arm (by construction, keyed on
20383        // `caixa.kind().is_aplicacao()`), so the fold short-circuits
20384        // to `Ok(())` without touching the mesh slots. Pins the
20385        // identity element on every non-Aplicacao kind — a future
20386        // refactor that made the mesh-slot cascade fire on the wrong
20387        // kind (say, on a `Servico` whose mesh slots happen to be
20388        // populated in a mis-authored manifest, which the peer
20389        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
20390        // coherence gate would refuse upstream anyway) surfaces here
20391        // as a test failure first. Peer with the
20392        // `validate_limits_accepts_none` / `validate_behavior_accepts_none`
20393        // identity-element pins on the sibling M2 `Option`-shaped
20394        // per-Caixa compound gates.
20395        for kind in [
20396            CaixaKind::Biblioteca,
20397            CaixaKind::Binario,
20398            CaixaKind::Servico,
20399            CaixaKind::Supervisor,
20400            CaixaKind::Acao,
20401        ] {
20402            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20403            c.kind = kind;
20404            assert!(
20405                c.aplicacao_view().is_none(),
20406                "aplicacao_view must return None off the Aplicacao arm \
20407                 for kind {kind:?}",
20408            );
20409            c.validate_aplicacao_shape().expect(
20410                "non-Aplicacao kinds must pass the compound gate as the fold's identity element",
20411            );
20412        }
20413    }
20414
20415    #[test]
20416    fn validate_aplicacao_shape_accepts_clean_fixture() {
20417        // Positive control: a well-formed Aplicacao (two DNS-1123
20418        // members with valid semver constraints, no `:contratos` /
20419        // `:entrada` / `:placement` / `:politicas` set — every
20420        // per-slot gate accepts the vacuous / omitted arm) passes the
20421        // compound gate cleanly. A future tightening of either arm's
20422        // accepted set surfaces here as a test failure first. Mirrors
20423        // the peer `validate_deps_accepts_clean_fixture` (b5dd55e) /
20424        // `validate_upgrade_from_accepts_clean_fixture` (d6801df)
20425        // positive-control postures on the sibling per-Caixa
20426        // compound gates.
20427        let c = aplicacao_fixture("demo");
20428        c.validate_aplicacao_shape()
20429            .expect("clean Aplicacao fixture must pass the compound gate");
20430    }
20431
20432    // ── Caixa::validate_supervisor_shape — compound per-Caixa gate ───────
20433
20434    /// Build a minimal well-formed Supervisor fixture on top of the
20435    /// canonical template. Every arm of the compound gate then patches
20436    /// exactly one axis away from clean so its per-arm diagnostic
20437    /// surfaces without collateral noise from a peer slot. Peer of
20438    /// [`aplicacao_fixture`] on the sibling per-Aplicacao compound
20439    /// gate's pin family.
20440    fn supervisor_fixture(nome: &str) -> Caixa {
20441        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
20442        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
20443        c.kind = CaixaKind::Supervisor;
20444        // Supervisors don't run code — clear the biblioteca slot the
20445        // template seeds so the fold's per-arm diagnostics surface
20446        // without the peer `SupervisorOwnsCode` kind-coherence gate
20447        // firing upstream at the layout altitude.
20448        c.bibliotecas = vec![];
20449        // `:estrategia` defaults to `OneForOne` at the typed view level,
20450        // and `OneForOne` requires at least one `:children` entry — pin
20451        // a single-child `Permanent` worker so the typed-shape cascade
20452        // passes cleanly and the per-arm fixtures below can each patch
20453        // exactly one axis.
20454        c.estrategia = Some(RestartStrategy::OneForOne);
20455        c.children = vec![ChildSpec {
20456            caixa: "worker".into(),
20457            versao: "^0.1".into(),
20458            restart: RestartPolicy::Permanent,
20459        }];
20460        c
20461    }
20462
20463    #[test]
20464    fn validate_supervisor_shape_folds_view_arm_matches_gate() {
20465        // Fail-before-pass-after per-arm equivalence pin on the
20466        // typed-shape cascade arm: a fixture whose typed
20467        // [`crate::SupervisorSpec`] view fails
20468        // [`crate::SupervisorSpec::validate`] (here — a duplicate
20469        // `:children` `:caixa` entry, which
20470        // [`crate::SupervisorSpec::validate`]'s set-not-multiset gate
20471        // rejects as [`crate::SupervisorError::DuplicateChildCaixa`])
20472        // surfaces the same [`crate::SupervisorError`] diagnostic
20473        // through both the compound gate
20474        // [`Caixa::validate_supervisor_shape`] and the standalone
20475        // [`crate::SupervisorSpec::validate`] on the same folded view.
20476        // Pins the fold — a silent regression that de-folded the
20477        // typed-shape arm would surface here as a mismatch between the
20478        // two dispatches. Sibling in shape to the peer
20479        // `validate_aplicacao_shape_folds_view_arm_matches_gate`
20480        // (949a7a0) on the sibling per-Aplicacao compound gate.
20481        use crate::supervisor::{ChildSpec, RestartPolicy};
20482        let mut c = supervisor_fixture("demo");
20483        c.children = vec![
20484            ChildSpec {
20485                caixa: "worker".into(),
20486                versao: "^0.1".into(),
20487                restart: RestartPolicy::Permanent,
20488            },
20489            ChildSpec {
20490                caixa: "worker".into(),
20491                versao: "^0.1".into(),
20492                restart: RestartPolicy::Permanent,
20493            },
20494        ];
20495        let via_method = c.validate_supervisor_shape().unwrap_err();
20496        let via_standalone = c.supervisor_view().unwrap().validate().unwrap_err();
20497        assert_eq!(
20498            via_method, via_standalone,
20499            "Caixa::validate_supervisor_shape must surface the typed-\
20500             shape arm's diagnostic byte-equal to the standalone \
20501             `SupervisorSpec::validate` on the same folded view",
20502        );
20503        assert!(
20504            matches!(
20505                via_method,
20506                crate::SupervisorError::DuplicateChildCaixa { ref caixa }
20507                    if caixa == "worker"
20508            ),
20509            "expected DuplicateChildCaixa on the duplicate 'worker' \
20510             child, got {via_method:?}",
20511        );
20512    }
20513
20514    #[test]
20515    fn validate_supervisor_shape_folds_self_supervision_arm_matches_gate() {
20516        // Per-arm equivalence pin on the cross-slot self-edge axis: a
20517        // fixture whose `:children :caixa` names the Supervisor's own
20518        // `:nome` (which
20519        // [`crate::supervisor::validate_no_self_supervision`] rejects
20520        // as [`crate::SupervisorError::ChildSupervisesSelf`], a
20521        // one-node reconciliation cycle in the supervisor's
20522        // supervision-tree) surfaces the same
20523        // [`crate::SupervisorError::ChildSupervisesSelf`] through both
20524        // the compound gate and the standalone
20525        // [`crate::supervisor::validate_no_self_supervision`] keyed
20526        // off the same `(children, nome)` pair. Pins the fold's
20527        // second arm — reaching this arm through the compound gate
20528        // requires the typed-shape cascade to pass first, which itself
20529        // pins one cross-arm ordering step. Sibling in shape to the
20530        // peer
20531        // `validate_aplicacao_shape_folds_self_membership_arm_matches_gate`
20532        // (949a7a0) cross-slot equivalence pin on the sibling
20533        // per-Aplicacao compound gate.
20534        use crate::supervisor::{ChildSpec, RestartPolicy};
20535        let mut c = supervisor_fixture("demo");
20536        c.children = vec![ChildSpec {
20537            caixa: "demo".into(),
20538            versao: "^0.1".into(),
20539            restart: RestartPolicy::Permanent,
20540        }];
20541        let via_method = c.validate_supervisor_shape().unwrap_err();
20542        let via_standalone =
20543            crate::supervisor::validate_no_self_supervision(c.children(), c.nome()).unwrap_err();
20544        assert_eq!(
20545            via_method, via_standalone,
20546            "Caixa::validate_supervisor_shape must surface the cross-\
20547             slot self-edge diagnostic byte-equal to the standalone \
20548             `supervisor::validate_no_self_supervision` on the same \
20549             (children, nome) pair",
20550        );
20551        assert!(
20552            matches!(
20553                via_method,
20554                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
20555            ),
20556            "expected ChildSupervisesSelf carrying (caixa=\"demo\"), \
20557             got {via_method:?}",
20558        );
20559    }
20560
20561    #[test]
20562    fn validate_supervisor_shape_view_arm_fires_before_self_supervision_arm() {
20563        // Cross-arm ordering pin between the two arms of the fold: a
20564        // fixture carrying BOTH a typed-shape violation (a per-child
20565        // empty `:caixa` name — rejected by
20566        // [`crate::SupervisorSpec::validate`] as
20567        // [`crate::SupervisorError::EmptyChildName`]) AND a would-be
20568        // self-edge violation (a `:children` entry naming the
20569        // supervisor's own `:nome`) surfaces the typed-shape
20570        // diagnostic first through the compound gate. Sanity
20571        // assertion: the self-referential `:children` entry alone
20572        // under the same parent `:nome` trips the self-edge arm on
20573        // its own via the standalone
20574        // [`crate::supervisor::validate_no_self_supervision`], so the
20575        // typed-shape-first surfacing is a real ordering property, not
20576        // a case where the self-edge arm silently accepts the fixture.
20577        // Pins the pre-fold layout wire-up's canonical dispatch order
20578        // (typed-shape cascade → cross-slot self-edge) as a property
20579        // of the substrate primitive rather than a convention of the
20580        // layout call site. Sibling in shape to
20581        // `validate_aplicacao_shape_view_arm_fires_before_self_membership_arm`
20582        // (949a7a0) on the sibling per-Aplicacao compound gate.
20583        use crate::supervisor::{ChildSpec, RestartPolicy};
20584        let mut c = supervisor_fixture("demo");
20585        c.children = vec![
20586            ChildSpec {
20587                caixa: String::new(),
20588                versao: "^0.1".into(),
20589                restart: RestartPolicy::Permanent,
20590            },
20591            ChildSpec {
20592                caixa: "demo".into(),
20593                versao: "^0.1".into(),
20594                restart: RestartPolicy::Permanent,
20595            },
20596        ];
20597        let err = c.validate_supervisor_shape().unwrap_err();
20598        assert!(
20599            matches!(err, crate::SupervisorError::EmptyChildName),
20600            "typed-shape arm must fire before self-edge arm — expected \
20601             EmptyChildName on the empty :caixa child, got {err:?}",
20602        );
20603        // Sanity: the self-referential `:children` entry alone under
20604        // the same parent `:nome` trips the self-edge arm on its own
20605        // — proves the typed-shape-first surfacing above is a real
20606        // ordering property, not a case where the self-edge arm
20607        // silently accepts the fixture.
20608        let sanity = crate::supervisor::validate_no_self_supervision(
20609            &[ChildSpec {
20610                caixa: "demo".into(),
20611                versao: "^0.1".into(),
20612                restart: RestartPolicy::Permanent,
20613            }],
20614            "demo",
20615        )
20616        .unwrap_err();
20617        assert!(
20618            matches!(
20619                sanity,
20620                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
20621            ),
20622            "sanity: the self-referential :children entry alone must \
20623             trip the self-edge arm — got {sanity:?}",
20624        );
20625    }
20626
20627    #[test]
20628    fn validate_supervisor_shape_accepts_non_supervisor_kind() {
20629        // Positive control on the identity-element arm: every non-
20630        // Supervisor kind passes the compound gate trivially — the
20631        // paired [`Caixa::supervisor_view`] accessor returns `None`
20632        // off the Supervisor arm (by construction, keyed on
20633        // `caixa.kind().is_supervisor()`), so the fold short-circuits
20634        // to `Ok(())` without touching the supervision-tree slots.
20635        // Pins the identity element on every non-Supervisor kind — a
20636        // future refactor that made the supervision-tree cascade fire
20637        // on the wrong kind (say, on a `Servico` whose supervision
20638        // slots happen to be populated in a mis-authored manifest,
20639        // which the peer
20640        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
20641        // kind-coherence gate would refuse upstream anyway) surfaces
20642        // here as a test failure first. Peer with the
20643        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
20644        // (949a7a0) / `validate_limits_accepts_none` /
20645        // `validate_behavior_accepts_none` identity-element pins on
20646        // the sibling per-Caixa compound gates.
20647        for kind in [
20648            CaixaKind::Biblioteca,
20649            CaixaKind::Binario,
20650            CaixaKind::Servico,
20651            CaixaKind::Aplicacao,
20652            CaixaKind::Acao,
20653        ] {
20654            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20655            c.kind = kind;
20656            assert!(
20657                c.supervisor_view().is_none(),
20658                "supervisor_view must return None off the Supervisor \
20659                 arm for kind {kind:?}",
20660            );
20661            c.validate_supervisor_shape().expect(
20662                "non-Supervisor kinds must pass the compound gate as the fold's identity element",
20663            );
20664        }
20665    }
20666
20667    #[test]
20668    fn validate_supervisor_shape_accepts_clean_fixture() {
20669        // Positive control: a well-formed Supervisor (single
20670        // DNS-1123-valid `Permanent` worker child under the
20671        // `OneForOne` strategy — the OTP MaxIntensity/Period defaults
20672        // accept the vacuous `:max-restarts` / `:restart-window`
20673        // arms) passes the compound gate cleanly. A future tightening
20674        // of either arm's accepted set surfaces here as a test
20675        // failure first. Mirrors the peer
20676        // `validate_aplicacao_shape_accepts_clean_fixture` (949a7a0)
20677        // positive-control posture on the sibling per-Caixa compound
20678        // gate.
20679        let c = supervisor_fixture("demo");
20680        c.validate_supervisor_shape()
20681            .expect("clean Supervisor fixture must pass the compound gate");
20682    }
20683}