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::duplicate_nome(dep.nome(), list.as_str()));
2319        }
2320        target.push(dep);
2321        Ok(())
2322    }
2323
2324    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2325    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2326    /// composite-reference accessor every consumer of the top-level
2327    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2328    /// off — returns the author-declared `:limits` typed composite
2329    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2330    /// backing storage the raw `self.limits.as_ref()` field access
2331    /// borrows from, with `None` naming the "no `:limits` block
2332    /// authored — every per-axis Lunatic-sandbox cap defers to the
2333    /// wasm-engine-default arm named on the per-axis
2334    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2335    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2336    /// docstrings" partition every downstream Servico-M2-overlay
2337    /// emitter treats as "emit nothing" and the sibling
2338    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2339    /// treats as "skip the per-axis
2340    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2341    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2342    ///
2343    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2344    /// composite — the load-bearing container of every Lunatic-shaped
2345    /// per-process wasm32-sandbox cap axis every long-running wasm
2346    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2347    /// Lunatic per-process linear-memory / fuel / wall-clock /
2348    /// millicore cap primitives translated onto pleme-io's typed
2349    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2350    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2351    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2352    /// chart both fan on). Every per-`:limits` axis threads through a
2353    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2354    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2355    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2356    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2357    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2358    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2359    /// consumer that reaches for a limits axis first passes through
2360    /// this outer accessor onto the composite and then dispatches
2361    /// onto the per-axis accessor — the two-level dispatch means
2362    /// every per-`:limits` reader now routes through a typed dispatch
2363    /// on the substrate primitive at both altitudes.
2364    ///
2365    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2366    /// was accessed inline at three production sites — the
2367    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2368    /// `if let Some(l) = &caixa.limits { … }` traversal head
2369    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2370    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2371    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2372    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2373    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2374    /// [`LimitsSpec::validate`] fans onto), the
2375    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2376    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2377    /// head (caixa-core/src/render.rs:18504, which drives the
2378    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2379    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2380    /// block emitter fans on), and the
2381    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2382    /// set enumerator's `self.limits.is_some()` presence probe
2383    /// (caixa-core/src/manifest.rs:1788, which drives the
2384    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2385    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2386    /// gate reads) — three open-coded outer-field accesses that
2387    /// expressed no compile-time link back to the typed slot at the
2388    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2389    /// axis to a richer author surface (a multi-`:limits` list the M4
2390    /// CR materializer resolves per-CR at admission time so a Servico
2391    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2392    /// cluster `:limits-overrides` slot the operator pins so a
2393    /// cluster-specific policy can tighten a caixa-declared cap
2394    /// without re-authoring the `caixa.lisp`, a promotion of the
2395    /// plain `Option<LimitsSpec>` to a richer
2396    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2397    /// resolved dynamic-cap surface lands) would have had to be
2398    /// threaded through all three open-coded copies in lockstep or
2399    /// one consumer would silently disagree with the peers on which
2400    /// limits composite a given Caixa resolves to — the layout gate's
2401    /// per-axis bracket-dispatch seed reading the raw slot while the
2402    /// peer `servico_m2_overlay` emitter read an operator-resolved
2403    /// slot would silently split the build-time sandbox-shape gate
2404    /// from the runtime `ComputeUnit` CR emission gate, a three-
2405    /// consumer split at the layout gate, the M2 overlay emitter, and
2406    /// the declared-slot enumerator far from the source `caixa.lisp`
2407    /// with no field naming the limits-drift root cause. Lifting the
2408    /// resolution rule to a typed method on the substrate primitive
2409    /// means every downstream consumer of the caixa's per-`Caixa`
2410    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2411    /// one typed dispatch — the resolver's accept-set migrates as a
2412    /// unit on any future axis addition.
2413    ///
2414    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2415    /// composite-reference accessor — opens the outer-`Caixa`
2416    /// `Option<&Composite>` composite-reference projection pattern the
2417    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2418    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2419    /// [`crate::aplicacao::Placement`] / `:entrada`
2420    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2421    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2422    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2423    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2424    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2425    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2426    /// altitude — extends that "one typed dispatch on the substrate
2427    /// primitive, thin projections at each consumer" discipline onto
2428    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2429    /// runtime slot family's outer-composite axis. Returns
2430    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2431    /// clone) because every downstream consumer of the limits
2432    /// composite treats it as a read-only per-axis dispatch source —
2433    /// the reference-view is the narrowest borrow that supports every
2434    /// present + roadmapped consumer (per-axis accessor dispatch,
2435    /// `.is_empty()`-gated overlay projection, presence-probe early
2436    /// return on the "author-omitted `:limits` ⇒ engine-default
2437    /// applies" partition) without cloning the composite through
2438    /// every consumer's fast path. The `Option` half of the return-
2439    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2440    /// engine-default applies" partition (not a default composite the
2441    /// downstream must reject on emptiness) — the accessor projects
2442    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2443    /// reference-return unchanged. Named `limits()` to match the
2444    /// storage field's name verbatim and the tatara-lisp author-
2445    /// surface term (`:limits`) the field's own docstring already
2446    /// carries.
2447    #[must_use]
2448    pub const fn limits(&self) -> Option<&LimitsSpec> {
2449        self.limits.as_ref()
2450    }
2451
2452    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2453    /// composite OTP-`gen_server`-shaped callback-table optional-
2454    /// composite-reference accessor every consumer of the top-level
2455    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2456    /// keys off — returns the author-declared `:behavior` typed
2457    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2458    /// the same backing storage the raw `self.behavior.as_ref()` field
2459    /// access borrows from, with `None` naming the "no `:behavior`
2460    /// block authored — every per-callback OTP-shaped hook defers to
2461    /// the wasm-engine's runtime default arm named on the per-axis
2462    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2463    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2464    /// [`BehaviorSpec::on_state_change`] /
2465    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2466    /// partition every downstream Servico-M2-overlay emitter treats as
2467    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2468    /// per-`:behavior` shape gate treats as "skip the per-arm
2469    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2470    /// per-callback on-disk `MissingEntry` existence check".
2471    ///
2472    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2473    /// composite — the load-bearing container of every OTP-shaped
2474    /// per-Servico lifecycle-callback path axis every long-running wasm
2475    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2476    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2477    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2478    /// translated onto pleme-io's typed `:behavior :on-init` /
2479    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2480    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2481    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2482    /// chart both fan on). Every per-`:behavior` axis threads through a
2483    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2484    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2485    /// Every downstream consumer that reaches for a behavior axis
2486    /// first passes through this outer accessor onto the composite
2487    /// and then dispatches onto the per-callback accessor — the
2488    /// two-level dispatch means every per-`:behavior` reader now
2489    /// routes through a typed dispatch on the substrate primitive at
2490    /// both altitudes.
2491    ///
2492    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2493    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2494    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2495    /// keys the "per-version `:state-change` instruction must have a
2496    /// `:on-state-change` callback" precondition off this accessor's
2497    /// composite (the callback-side counterpart to the
2498    /// `:upgrade-from :instructions :state-change :script` refusal at
2499    /// the appup-side). Threading that gate's traversal input through
2500    /// this accessor closes the cross-slot invariant on the substrate
2501    /// primitive, not on the raw field.
2502    ///
2503    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2504    /// composite was accessed inline at four production sites — the
2505    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2506    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2507    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2508    /// `BehaviorError` refusal cascade + the per-callback on-disk
2509    /// [`crate::LayoutError::MissingEntry`] existence check under
2510    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2511    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2512    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2513    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2514    /// drives the `:state-change` ↔ `:on-state-change` precondition
2515    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2516    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2517    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2518    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2519    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2520    /// Servico values-block emitter fans on), and the
2521    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2522    /// set enumerator's `self.behavior.is_some()` presence probe
2523    /// (caixa-core/src/manifest.rs:1919, which drives the
2524    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2525    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2526    /// gate reads) — four open-coded outer-field accesses that
2527    /// expressed no compile-time link back to the typed slot at the
2528    /// [`Caixa`] altitude. A future extension of the `:behavior`
2529    /// outer axis to a richer author surface (a per-callback overlay
2530    /// resolver the operator materializes at admission time so a
2531    /// cluster-specific policy can inject a per-callback tracing
2532    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2533    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2534    /// dynamic}` partition once a runtime-resolved behavior-swap
2535    /// surface lands, the M4 per-callback middleware chain the
2536    /// caixa-operator's per-Servico admission webhook keys off) would
2537    /// have had to be threaded through all four open-coded copies in
2538    /// lockstep or one consumer would silently disagree with the
2539    /// peers on which behavior composite a given Caixa resolves to —
2540    /// the layout gate's per-callback existence-check seed reading
2541    /// the raw slot while the peer `servico_m2_overlay` emitter read
2542    /// an operator-resolved slot would silently split the build-time
2543    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2544    /// gate from the cross-slot `:state-change` composition gate from
2545    /// the M2 declared-slot enumerator, a four-consumer split far
2546    /// from the source `caixa.lisp` with no field naming the
2547    /// behavior-drift root cause. Lifting the resolution rule to a
2548    /// typed method on the substrate primitive means every downstream
2549    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2550    /// composite surface reaches for exactly one typed dispatch — the
2551    /// resolver's accept-set migrates as a unit on any future axis
2552    /// addition.
2553    ///
2554    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2555    /// composite-reference accessor — sibling to the opening
2556    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2557    /// `Option<&Composite>` composite-reference sub-family, extends
2558    /// the "one typed dispatch on the substrate primitive, thin
2559    /// projections at each consumer" discipline onto the second of
2560    /// the three M2 Servico-runtime slots. The remaining
2561    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2562    /// altitude — the M3 mesh-slot family (`:politicas`,
2563    /// `:placement`, `:entrada` — already closed on the inner
2564    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2565    /// d32111c) — remain the future sibling lifts on the outer
2566    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2567    /// the owning composite by copy or clone) because every
2568    /// downstream consumer of the behavior composite treats it as a
2569    /// read-only per-callback dispatch source — the reference-view is
2570    /// the narrowest borrow that supports every present + roadmapped
2571    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2572    /// overlay projection, presence-probe early return on the
2573    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2574    /// partition, cross-slot `:state-change` composition input)
2575    /// without cloning the composite through every consumer's fast
2576    /// path. The `Option` half of the return-type preserves the
2577    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2578    /// applies" partition (not a default composite the downstream
2579    /// must reject on emptiness) — the accessor projects the raw
2580    /// `Option<BehaviorSpec>` slot's presence bit through the
2581    /// reference-return unchanged. Named `behavior()` to match the
2582    /// storage field's name verbatim and the tatara-lisp author-
2583    /// surface term (`:behavior`) the field's own docstring already
2584    /// carries.
2585    #[must_use]
2586    pub const fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2587        self.behavior.as_ref()
2588    }
2589
2590    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2591    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2592    /// reference accessor every consumer of the top-level manifest's
2593    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2594    /// reader keys off — returns the author-declared `:politicas` typed
2595    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2596    /// same backing storage the raw `self.politicas.as_ref()` field
2597    /// access borrows from, with `None` naming the "no `:politicas`
2598    /// block authored — every per-axis mesh-policy scalar defers to the
2599    /// cluster-default arm named on the per-axis
2600    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2601    /// [`crate::aplicacao::MeshPolicy::retries`] /
2602    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2603    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2604    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2605    /// docstrings" partition every downstream caixa-mesh /
2606    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2607    /// "emit no per-`:politicas` overlay" and the sibling
2608    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2609    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2610    /// arm.
2611    ///
2612    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2613    /// Aplicacao typed composite — the load-bearing container of every
2614    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2615    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2616    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2617    /// composite; §V — the "no infinite blocking" per-call deadline +
2618    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2619    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2620    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2621    /// threads through a lifted per-slot accessor on the
2622    /// [`crate::aplicacao::MeshPolicy`] type: the
2623    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2624    /// mTLS-enforcement toggle, the
2625    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2626    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2627    /// (7073d0f) Gateway-API per-call deadline, the
2628    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2629    /// Envoy-outlier-detection composite. Every downstream consumer
2630    /// that reaches for a mesh-policy axis first passes through this
2631    /// outer accessor onto the composite and then dispatches onto the
2632    /// per-axis accessor — the two-level dispatch means every per-
2633    /// `:politicas` reader now routes through a typed dispatch on the
2634    /// substrate primitive at both altitudes.
2635    ///
2636    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2637    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2638    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2639    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2640    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2641    /// composite whether or not the author declared the outer slot.
2642    /// The outer accessor preserves the "author-omitted vs authored-
2643    /// empty" partition the inner accessor's `is_empty()`-gated
2644    /// renderer overlay collapses — routing the presence bit through
2645    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2646    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2647    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2648    ///
2649    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2650    /// composite was accessed inline at two production sites — the
2651    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2652    /// `self.politicas.clone().unwrap_or_default()` traversal head
2653    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2654    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2655    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2656    /// then observes), and the [`Self::declared_mesh_slots`] M3
2657    /// declared-slot-set enumerator's `self.politicas.is_some()`
2658    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2659    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2660    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2661    /// coherence gate reads) — two open-coded outer-field accesses
2662    /// that expressed no compile-time link back to the typed slot at
2663    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2664    /// outer axis to a richer author surface (a per-cluster
2665    /// `:politicas-overrides` slot the operator materializes at
2666    /// admission time so a cluster-specific policy can tighten the
2667    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2668    /// promotion of the plain `Option<MeshPolicy>` to a richer
2669    /// `{static, dynamic}` partition once the M4 per-edge
2670    /// contrato-scoped policy-override surface lands, the M5 traffic-
2671    /// shaping composition the caixa-operator's per-Aplicacao mesh
2672    /// admission webhook keys off) would have had to be threaded
2673    /// through both open-coded copies in lockstep or the Aplicacao-
2674    /// composition seed's default-fold arm would silently disagree
2675    /// with the M3 declared-slot enumerator on which policy composite
2676    /// a given Caixa resolves to — the seed reading an operator-
2677    /// resolved slot while the enumerator's presence probe read the
2678    /// raw slot would silently split the build-time mesh-artifact
2679    /// emission gate from the M3 declared-slot enumerator's kind-
2680    /// coherence gate, a two-consumer split far from the source
2681    /// `caixa.lisp` with no field naming the policy-drift root cause.
2682    /// Lifting the resolution rule to a typed method on the substrate
2683    /// primitive means every downstream consumer of the caixa's per-
2684    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2685    /// reaches for exactly one typed dispatch — the resolver's
2686    /// accept-set migrates as a unit on any future axis addition.
2687    ///
2688    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2689    /// composite-reference accessor — sibling to the opening
2690    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2691    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2692    /// reference sub-family, extends the "one typed dispatch on the
2693    /// substrate primitive, thin projections at each consumer"
2694    /// discipline onto the first of the three M3 mesh-slot axes.
2695    /// Peer of the closed inner mesh-slot outer-composite family the
2696    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2697    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2698    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2699    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2700    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2701    /// mesh-slot arm of the composite-reference family the remaining
2702    /// two axes (`:placement`, `:entrada`) fold onto in future
2703    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2704    /// composite by copy or clone) because every downstream consumer
2705    /// of the mesh-policy composite treats it as a read-only per-axis
2706    /// dispatch source — the reference-view is the narrowest borrow
2707    /// that supports every present + roadmapped consumer (per-axis
2708    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2709    /// presence-probe early return on the "author-omitted `:politicas`
2710    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2711    /// seed's default-fold arm) without cloning the composite through
2712    /// every consumer's fast path. The `Option` half of the return-
2713    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2714    /// cluster-default applies" partition (not a default composite
2715    /// the downstream must reject on emptiness) — the accessor
2716    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2717    /// through the reference-return unchanged. Named `politicas()` to
2718    /// match the storage field's name verbatim and the tatara-lisp
2719    /// author-surface term (`:politicas`) the field's own docstring
2720    /// already carries.
2721    #[must_use]
2722    pub const fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2723        self.politicas.as_ref()
2724    }
2725
2726    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2727    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2728    /// reference accessor every consumer of the top-level manifest's
2729    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2730    /// reader keys off — returns the author-declared `:placement` typed
2731    /// composite verbatim as an `Option<&Placement>` reference over the
2732    /// same backing storage the raw `self.placement.as_ref()` field
2733    /// access borrows from, with `None` naming the "no `:placement`
2734    /// block authored — every per-axis placement scalar defers to the
2735    /// cluster-default arm named on the per-axis
2736    /// [`crate::aplicacao::Placement::estrategia`] /
2737    /// [`crate::aplicacao::Placement::clusters`] /
2738    /// [`crate::aplicacao::Placement::affinity`] /
2739    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2740    /// docstrings" partition every downstream caixa-mesh /
2741    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2742    /// "emit no per-`:placement` overlay" and the sibling
2743    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2744    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2745    ///
2746    /// The outer `:placement` slot carries the M3 mesh-slot per-
2747    /// Aplicacao typed distribution composite — the load-bearing
2748    /// container of every where-does-this-Aplicacao-run axis every
2749    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2750    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2751    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2752    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2753    /// Aplicacao's typed distribution composite; §V CSE invariants —
2754    /// "distribution is a first-class typed composite, not a runtime
2755    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2756    /// typed inter-Servico contrato-edge overlay the per-cluster
2757    /// mesh renderer keys off). Every per-`:placement` axis threads
2758    /// through a lifted per-slot accessor on the
2759    /// [`crate::aplicacao::Placement`] type: the
2760    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2761    /// MESH-COMPOSITION distribution-strategy scalar, the
2762    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2763    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2764    /// M3-Adaptive-compression-hint optional-scalar, and the
2765    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2766    /// sharding extractor-expression optional-scalar. Every downstream
2767    /// consumer that reaches for a placement axis first passes through
2768    /// this outer accessor onto the composite and then dispatches onto
2769    /// the per-axis accessor — the two-level dispatch means every per-
2770    /// `:placement` reader now routes through a typed dispatch on the
2771    /// substrate primitive at both altitudes.
2772    ///
2773    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2774    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2775    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2776    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2777    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2778    /// whether or not the author declared the outer slot. The outer
2779    /// accessor preserves the "author-omitted vs authored-empty" partition
2780    /// the inner accessor collapses at the cluster-default fold —
2781    /// routing the presence bit through this accessor keeps the
2782    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2783    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2784    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2785    /// dispatch.
2786    ///
2787    /// Prior to this lift the `.placement` `Option<Placement>`
2788    /// composite was accessed inline at two production sites — the
2789    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2790    /// `self.placement.clone().unwrap_or_default()` traversal head
2791    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2792    /// the [`crate::aplicacao::Placement::default`] cluster-default
2793    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2794    /// then observes), and the [`Self::declared_mesh_slots`] M3
2795    /// declared-slot-set enumerator's `self.placement.is_some()`
2796    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2797    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2798    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2799    /// coherence gate reads) — two open-coded outer-field accesses
2800    /// that expressed no compile-time link back to the typed slot at
2801    /// the [`Caixa`] altitude. A future extension of the `:placement`
2802    /// outer axis to a richer author surface (a per-cluster
2803    /// `:placement-overrides` slot the operator materializes at
2804    /// admission time so a cluster-specific placement can tighten the
2805    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2806    /// per-tenant placement-alias table the M4
2807    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2808    /// per-CR at admission time, a promotion of the plain
2809    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2810    /// once Orleans-style virtual-actor dynamic placement comes into
2811    /// typed scope) would have had to be threaded through both open-
2812    /// coded copies in lockstep or the Aplicacao-composition seed's
2813    /// default-fold arm would silently disagree with the M3 declared-
2814    /// slot enumerator on which distribution composite a given Caixa
2815    /// resolves to — the seed reading an operator-resolved slot while
2816    /// the enumerator's presence probe read the raw slot would
2817    /// silently split the build-time distribution-artifact emission
2818    /// gate from the M3 declared-slot enumerator's kind-coherence
2819    /// gate, a two-consumer split far from the source `caixa.lisp`
2820    /// with no field naming the distribution-drift root cause.
2821    /// Lifting the resolution rule to a typed method on the substrate
2822    /// primitive means every downstream consumer of the caixa's per-
2823    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2824    /// reaches for exactly one typed dispatch — the resolver's
2825    /// accept-set migrates as a unit on any future axis addition.
2826    ///
2827    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2828    /// composite-reference accessor — sibling to the opening
2829    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2830    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2831    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2832    /// composite-reference sub-family, folds on the "one typed
2833    /// dispatch on the substrate primitive, thin projections at each
2834    /// consumer" discipline extended onto the second of the three M3
2835    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2836    /// composite family the sibling
2837    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2838    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2839    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2840    /// accessor pins already close on the inner
2841    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2842    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2843    /// [`Self::politicas`] opened, extending the discipline onto the
2844    /// second of the three M3 mesh-slot axes. The remaining M3
2845    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2846    /// discipline in the final sibling lift, closing the outer top-
2847    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2848    /// Returns `Option<&Placement>` (not the owning composite by copy
2849    /// or clone) because every downstream consumer of the placement
2850    /// composite treats it as a read-only per-axis dispatch source —
2851    /// the reference-view is the narrowest borrow that supports every
2852    /// present + roadmapped consumer (per-axis accessor dispatch,
2853    /// serde composite-serialization on the programs.yaml overlay,
2854    /// presence-probe early return on the "author-omitted `:placement`
2855    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2856    /// seed's default-fold arm) without cloning the composite through
2857    /// every consumer's fast path. The `Option` half of the return-
2858    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2859    /// cluster-default applies" partition (not a default composite
2860    /// the downstream must reject on emptiness) — the accessor
2861    /// projects the raw `Option<Placement>` slot's presence bit
2862    /// through the reference-return unchanged. Named `placement()` to
2863    /// match the storage field's name verbatim and the tatara-lisp
2864    /// author-surface term (`:placement`) the field's own docstring
2865    /// already carries.
2866    #[must_use]
2867    pub const fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2868        self.placement.as_ref()
2869    }
2870
2871    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2872    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2873    /// composite-reference accessor every consumer of the top-level
2874    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2875    /// composite reader keys off — returns the author-declared
2876    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2877    /// reference over the same backing storage the raw
2878    /// `self.entrada.as_ref()` field access borrows from, with `None`
2879    /// naming the "no `:entrada` block authored — this Aplicacao is
2880    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2881    /// partition every downstream caixa-mesh Gateway-API artifact
2882    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2883    /// backend for this Aplicacao" and the sibling
2884    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2885    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2886    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2887    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2888    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2889    /// the same `Option<&Entrada>` presence bit unchanged).
2890    ///
2891    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2892    /// Aplicacao typed external-gateway composite — the load-bearing
2893    /// container of every how-does-the-outside-world-reach-this-
2894    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2895    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2896    /// external-entry composite; §V CSE invariants — "the external
2897    /// gateway is a first-class typed composite, not a per-Servico
2898    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2899    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2900    /// API renderer keys off). Every per-`:entrada` axis threads
2901    /// through a lifted per-slot accessor on the
2902    /// [`crate::aplicacao::Entrada`] type: the
2903    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2904    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2905    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2906    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2907    /// backend `trigger.service.port` scalar, and the
2908    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2909    /// resolver every HTTPRoute-aware renderer consumes. Every
2910    /// downstream consumer that reaches for an entry axis first passes
2911    /// through this outer accessor onto the composite and then
2912    /// dispatches onto the per-axis accessor — the two-level dispatch
2913    /// means every per-`:entrada` reader now routes through a typed
2914    /// dispatch on the substrate primitive at both altitudes.
2915    ///
2916    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2917    /// seed: the Aplicacao-view builder forwards the outer `Option`
2918    /// arm verbatim (no default fold — `:entrada` is inherently
2919    /// optional; a cluster-internal Aplicacao has no external gateway
2920    /// at all, not "an external gateway that defaults to nothing"), so
2921    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2922    /// `Option<&Entrada>`-return accessor observes the same presence
2923    /// bit whether or not the author declared the outer slot. Routing
2924    /// the presence bit through this accessor keeps the
2925    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2926    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2927    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2928    /// hostname/backend/path emission dispatch.
2929    ///
2930    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2931    /// was accessed inline at two production sites — the
2932    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2933    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2934    /// which drives the forward onto the peer inner
2935    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2936    /// Gateway-API fan-out then observes), and the
2937    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2938    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2939    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2940    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2941    /// kind-coherence gate reads) — two open-coded outer-field
2942    /// accesses that expressed no compile-time link back to the typed
2943    /// slot at the [`Caixa`] altitude. A future extension of the
2944    /// `:entrada` outer axis to a richer author surface (a per-cluster
2945    /// `:entrada-overrides` slot the operator materializes at admission
2946    /// time so a cluster-specific hostname can pin the caixa-declared
2947    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2948    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2949    /// CR materializer resolves per-CR at admission time, a promotion
2950    /// of the plain `Option<Entrada>` to a richer
2951    /// `{public, private, internal}` partition once Cilium-identity-
2952    /// scoped internal gateways come into typed scope) would have had
2953    /// to be threaded through both open-coded copies in lockstep or the
2954    /// Aplicacao-composition seed's forward arm would silently
2955    /// disagree with the M3 declared-slot enumerator on which external-
2956    /// gateway composite a given Caixa resolves to — the seed reading
2957    /// an operator-resolved slot while the enumerator's presence probe
2958    /// read the raw slot would silently split the build-time gateway-
2959    /// artifact emission gate from the M3 declared-slot enumerator's
2960    /// kind-coherence gate, a two-consumer split far from the source
2961    /// `caixa.lisp` with no field naming the entry-drift root cause.
2962    /// Lifting the resolution rule to a typed method on the substrate
2963    /// primitive means every downstream consumer of the caixa's per-
2964    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2965    /// surface reaches for exactly one typed dispatch — the resolver's
2966    /// accept-set migrates as a unit on any future axis addition.
2967    ///
2968    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2969    /// return composite-reference accessor — closes the outer-`Caixa`
2970    /// `Option<&Composite>` composite-reference sub-family opened by
2971    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2972    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2973    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2974    /// folds on the "one typed dispatch on the substrate primitive,
2975    /// thin projections at each consumer" discipline extended onto the
2976    /// third and final M3 mesh-slot axis. Peer of the closed inner
2977    /// mesh-slot outer-composite family the sibling
2978    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2979    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2980    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2981    /// accessor pins already close on the inner
2982    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2983    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2984    /// altitudes of the outer-composite reference-return discipline
2985    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2986    /// slot presence) now carry the full five-arm accept-set behind a
2987    /// typed dispatch on the substrate primitive. Returns
2988    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2989    /// because every downstream consumer of the entrada composite
2990    /// treats it as a read-only per-axis dispatch source — the
2991    /// reference-view is the narrowest borrow that supports every
2992    /// present + roadmapped consumer (per-axis accessor dispatch,
2993    /// serde composite-serialization on the programs.yaml overlay,
2994    /// presence-probe early return on the "author-omitted `:entrada`
2995    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2996    /// seed's forward arm) without cloning the composite through every
2997    /// consumer's fast path. The `Option` half of the return-type
2998    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2999    /// cluster-internal Aplicacao" partition (not a default composite
3000    /// the downstream must reject on emptiness — a cluster-internal
3001    /// Aplicacao has no external gateway at all, not "a default gateway
3002    /// that emits nothing"); the accessor projects the raw
3003    /// `Option<Entrada>` slot's presence bit through the reference-
3004    /// return unchanged. Named `entrada()` to match the storage field's
3005    /// name verbatim and the tatara-lisp author-surface term
3006    /// (`:entrada`) the field's own docstring already carries.
3007    #[must_use]
3008    pub const fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
3009        self.entrada.as_ref()
3010    }
3011
3012    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
3013    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
3014    /// an `Option<&CiRun>`, borrowed from the typed slot's own
3015    /// `Option<CiRun>` storage. `None` when the slot is absent (every
3016    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
3017    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
3018    /// not silently accepted).
3019    ///
3020    /// Named `ci()` to match the storage field's name and the
3021    /// tatara-lisp author surface (`:ci`); mirrors the sibling
3022    /// `Option<&Composite>` accessors on this same `Caixa` altitude
3023    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
3024    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
3025    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
3026    /// at every consumer.
3027    #[must_use]
3028    pub const fn ci(&self) -> Option<&canteiro_types::CiRun> {
3029        self.ci.as_ref()
3030    }
3031
3032    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
3033    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
3034    /// accessor every consumer of the top-level manifest's per-Supervisor
3035    /// restart-strategy axis keys off — returns the author-declared
3036    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
3037    /// `Copy`-projected from the typed slot's own
3038    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
3039    /// (`:estrategia` is a flat-spread supervisor-only slot every
3040    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
3041    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3042    /// still omit to defer to [`RestartStrategy::default`] —
3043    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
3044    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
3045    /// [`SupervisorSpec::default`]-inherited strategy without any silent
3046    /// promotion to a fresh explicit variant at the accessor boundary).
3047    ///
3048    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
3049    /// restart-strategy discriminant every substrate-side per-Supervisor
3050    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
3051    /// closed-set `one_for_one | one_for_all | rest_for_one |
3052    /// simple_one_for_one` algebra translated onto pleme-io's typed
3053    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
3054    /// slot algebra the operator's hierarchical reconciliation scheduler
3055    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
3056    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
3057    /// supervisor slots are flat on Caixa (vs nested under a
3058    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
3059    /// level of nesting"), so the accessor's altitude is the outer
3060    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
3061    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
3062    /// (eafb619) accessor keys off. The two typed axes — the outer
3063    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
3064    /// (author-omitted arm carried as `None`) and the inner post-
3065    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
3066    /// (`Option` collapsed through the [`Self::supervisor_view`]
3067    /// `unwrap_or_default()` fold) — now share one accessor discipline for
3068    /// the shared substrate concept "the author-declared OTP-shaped
3069    /// sibling-restart-strategy variant that partitions the downstream
3070    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
3071    /// `None` arm is the pre-composition presence bit every declared-slot
3072    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
3073    /// inner-altitude non-`Option` `RestartStrategy` is the post-
3074    /// composition partition-dispatch input every strategy-arm consumer
3075    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
3076    /// Supervisor sibling-restart branch, the future M4
3077    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
3078    /// webhook) fans on.
3079    ///
3080    /// Prior to this lift the `.estrategia` field was accessed inline at
3081    /// two production sites in `caixa-core/src/manifest.rs` — the
3082    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
3083    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
3084    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3085    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
3086    /// `SupervisorSpec` construction site at `estrategia:
3087    /// self.estrategia.unwrap_or_default()` (which composes the flat-
3088    /// spread outer author-surface `Option<RestartStrategy>` onto the
3089    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
3090    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
3091    /// coded field-accesses that expressed no compile-time link back to
3092    /// the typed slot. A future extension of the outer `:estrategia` axis
3093    /// to a richer author surface (a per-cluster strategy override the
3094    /// operator pins through a future `:estrategia-overrides` overlay the
3095    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
3096    /// a per-tenant strategy-alias table the M4 CR materializer resolves
3097    /// per-CR, a per-Supervisor dynamic strategy derivation the future
3098    /// adaptive-supervision engine computes from child-failure-history
3099    /// topology, a per-child-cohort strategy split the future
3100    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
3101    /// absorption roadmap acknowledges, a promotion of the plain
3102    /// `Option<RestartStrategy>` to a richer
3103    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
3104    /// operator-resolved overlay lands) would have had to be threaded
3105    /// through both open-coded copies in lockstep or the enumerator's
3106    /// presence probe and the composition site's `unwrap_or_default()`
3107    /// fold would silently disagree on which strategy a given [`Caixa`]
3108    /// resolves to (an author's `:estrategia OneForAll` would satisfy
3109    /// the enumerator's presence probe while the composition site
3110    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3111    /// the resolution rule to a typed method on the substrate primitive
3112    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3113    /// altitude sibling-restart-strategy surface reaches for exactly one
3114    /// typed dispatch — the resolver's accept-set migrates as a unit on
3115    /// any future axis addition.
3116    ///
3117    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3118    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3119    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3120    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3121    /// `Option<u32>` and (through the future duration-newtype landing)
3122    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3123    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3124    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3125    /// the post-composition [`SupervisorSpec`] altitude — same "one
3126    /// typed dispatch on the substrate primitive, thin projections at
3127    /// each consumer" discipline extended onto the pre-composition outer
3128    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3129    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3130    /// `Option<&Composite>` composite-reference family the sibling
3131    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3132    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3133    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3134    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3135    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3136    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3137    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3138    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3139    /// pins on the inner-altitude per-`:placement` composite. Named
3140    /// `estrategia()` to match the storage field's name and the
3141    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3142    /// / per-[`crate::aplicacao::Placement`] peer
3143    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3144    /// verbatim; the accessor's identity name maps onto the canonical
3145    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3146    /// docstring already carries.
3147    #[must_use]
3148    pub const fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3149        self.estrategia
3150    }
3151
3152    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3153    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3154    /// scalar accessor every consumer of the top-level manifest's per-
3155    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3156    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3157    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3158    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3159    /// accessor returns by value; no borrow of `&self` past the call).
3160    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3161    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3162    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3163    /// still omit to defer to the [`Self::supervisor_view`]
3164    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3165    ///
3166    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3167    /// `MaxIntensity` restart-budget count that pairs with the sibling
3168    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3169    /// restart-intensity ratio the supervisor trips its own escalation on
3170    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3171    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3172    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3173    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3174    /// the outer top-level `Caixa` (per the field-shape docstring at
3175    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3176    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3177    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3178    /// composed [`SupervisorSpec`] altitude the sibling
3179    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3180    /// off. The two typed axes — the outer author-surface `Option<u32>`
3181    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3182    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3183    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3184    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3185    /// shared substrate concept "the author-declared OTP-shaped
3186    /// restart-budget count every downstream per-Supervisor consumer's
3187    /// restart-intensity budget-vs-count comparator fans on".
3188    ///
3189    /// Prior to this lift the `.max_restarts` field was accessed inline
3190    /// at two production sites in `caixa-core/src/manifest.rs` — the
3191    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3192    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3193    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3194    /// kind-coherence gate's per-slot label push) and the
3195    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3196    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3197    /// flat-spread outer author-surface `Option<u32>` onto the inner
3198    /// post-composition [`SupervisorSpec`] `u32` field the
3199    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3200    /// coded field-accesses that expressed no compile-time link back to
3201    /// the typed slot. A future extension of the outer `:max-restarts`
3202    /// axis to a richer author surface (a per-cluster restart-budget
3203    /// override the operator pins through a future `:max-restarts-overrides`
3204    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3205    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3206    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3207    /// budget derivation the future adaptive-supervision engine computes
3208    /// from child-failure-history topology, a promotion of the plain
3209    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3210    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3211    /// per-child-cohort roadmap lands) would have had to be threaded
3212    /// through both open-coded copies in lockstep or the enumerator's
3213    /// presence probe and the composition site's `unwrap_or(5)` fold
3214    /// would silently disagree on which restart-budget a given [`Caixa`]
3215    /// resolves to (an author's `:max-restarts 10` would satisfy the
3216    /// enumerator's presence probe while the composition site silently
3217    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3218    /// resolution rule to a typed method on the substrate primitive means
3219    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3220    /// restart-budget-count surface reaches for exactly one typed dispatch
3221    /// — the resolver's accept-set migrates as a unit on any future axis
3222    /// addition.
3223    ///
3224    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3225    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3226    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3227    /// projection pattern the sibling per-`Caixa`
3228    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3229    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3230    /// Peer of the inner-altitude
3231    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3232    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3233    /// typed dispatch on the substrate primitive, thin projections at
3234    /// each consumer" discipline extended onto the pre-composition outer
3235    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3236    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3237    /// the storage field's name and the per-[`SupervisorSpec`] peer
3238    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3239    /// discipline verbatim; the accessor's identity maps onto the
3240    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3241    /// field's docstring already carries.
3242    #[must_use]
3243    pub const fn max_restarts(&self) -> Option<u32> {
3244        self.max_restarts
3245    }
3246
3247    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3248    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3249    /// denominator raw-duration-string scalar accessor every consumer of
3250    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3251    /// window axis keys off — returns the author-declared `:restart-window`
3252    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3253    /// from the typed slot's own `Option<String>` storage. `None` when
3254    /// the slot is absent (the canonical "never reset — every restart
3255    /// across the supervisor's lifetime counts against the sibling
3256    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3257    /// `defcaixa` carries by `#[serde(default)]` and every
3258    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3259    /// [`Self::supervisor_view`] `restart_window: None` composition
3260    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3261    /// swallow `.and_then(|s| … .ok())` fold).
3262    ///
3263    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3264    /// shaped `Period` sliding-observation-interval duration string that
3265    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3266    /// budget count to form the `MaxIntensity / Period` restart-intensity
3267    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3268    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3269    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3270    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3271    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3272    /// holds an `Option<Duration>` routed through the shared
3273    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3274    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3275    /// authoring surface) while the inner altitude's
3276    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3277    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3278    /// is closed by the sibling [`Self::validate_restart_window`] gate
3279    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3280    /// the offending value; the view-construction path
3281    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3282    /// `None` to keep the view best-effort.
3283    ///
3284    /// Prior to this lift the `.restart_window` field was accessed inline
3285    /// at three production sites in `caixa-core/src/manifest.rs` — the
3286    /// [`Self::declared_supervisor_slots`]
3287    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3288    /// `if self.restart_window.is_some()` (which drives the
3289    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3290    /// coherence gate's per-slot label push), the
3291    /// [`Self::validate_restart_window`] `let Some(s) =
3292    /// self.restart_window.as_deref()` empty-and-shape gate binding
3293    /// (which folds the raw string through the shared
3294    /// [`crate::supervisor::duration_codec::parse`] to surface
3295    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3296    /// value), and the [`Self::supervisor_view`] `self.restart_window
3297    /// .as_deref().and_then(…)` view-construction fold (which composes
3298    /// the flat-spread outer author-surface `Option<String>` onto the
3299    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3300    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3301    /// three open-coded field-accesses that expressed no compile-time
3302    /// link back to the typed slot. A future extension of the outer
3303    /// `:restart-window` axis to a richer author surface (a per-cluster
3304    /// window override, a per-tenant window-alias table, a per-Supervisor
3305    /// dynamic window derivation the future adaptive-supervision engine
3306    /// computes from child-failure-history topology, a promotion of the
3307    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3308    /// once the future author-surface parser lands at the [`Caixa`]
3309    /// altitude and the raw-string form is retired) would have had to be
3310    /// threaded through every open-coded copy in lockstep or the three
3311    /// consumers would silently disagree on which raw string a given
3312    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3313    /// method on the substrate primitive means every downstream consumer
3314    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3315    /// string surface reaches for exactly one typed dispatch — the
3316    /// resolver's accept-set migrates as a unit on any future axis
3317    /// addition.
3318    ///
3319    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3320    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3321    /// spread projection pattern the sibling per-`Caixa`
3322    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3323    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3324    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3325    /// string arm (the outer altitude's raw-string form; the inner
3326    /// altitude's parsed [`Duration`] form is the peer
3327    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3328    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3329    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3330    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3331    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3332    /// sub-family already carries — same "one typed dispatch on the
3333    /// substrate primitive, thin projections at each consumer"
3334    /// discipline extended onto the M2 supervisor-tree flat-spread
3335    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3336    /// to match the storage field's name and the per-[`SupervisorSpec`]
3337    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3338    /// method-name discipline verbatim; the accessor's identity maps
3339    /// onto the canonical OTP-shape supervision vocabulary the
3340    /// `:restart-window` field's docstring already carries.
3341    #[must_use]
3342    pub const fn restart_window(&self) -> Option<&str> {
3343        match &self.restart_window {
3344            Some(s) => Some(s.as_str()),
3345            None => None,
3346        }
3347    }
3348
3349    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3350    /// outer-composite OTP-appup-shaped per-prior-version migration-
3351    /// entry-list slice accessor every consumer of the top-level
3352    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3353    /// slice-view keys off — returns the author-declared `:upgrade-from`
3354    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3355    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3356    /// the raw `self.upgrade_from.as_slice()` field access borrows
3357    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3358    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3359    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3360    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3361    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3362    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3363    /// degenerates to an empty slice on that arm without any silent
3364    /// `None` collapse).
3365    ///
3366    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3367    /// migration block — the load-bearing container of every per-
3368    /// prior-`:versao` migration-instruction list the wasm-operator
3369    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3370    /// `.appup` per-prior-version `LoadModule | StateChange |
3371    /// SoftPurge | Purge | Restart` instruction algebra translated
3372    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3373    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3374    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3375    /// threads through a lifted per-entry accessor on the
3376    /// [`UpgradeFromEntry`] type: the
3377    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3378    /// version scalar accessor and the
3379    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3380    /// return per-entry instruction-list accessor (0137e5a). Every
3381    /// downstream consumer of the hot-upgrade path first passes
3382    /// through this outer accessor onto the slice and then dispatches
3383    /// per-entry through the inner accessors — the two-level dispatch
3384    /// means every per-`:upgrade-from` reader now routes through a
3385    /// typed dispatch on the substrate primitive at both altitudes.
3386    ///
3387    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3388    /// slot was accessed inline at production sites across three
3389    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3390    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3391    /// (caixa-core/src/manifest.rs, which drives the
3392    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3393    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3394    /// gate reads), the [`crate::StandardLayout::verify`] per-
3395    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3396    /// layout.rs, which fans onto the
3397    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3398    /// cross-entry duplicate gate, the
3399    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3400    /// SemVer-precedence cross-slot gate, the
3401    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3402    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3403    /// gate, and the per-instruction script-path existence-probe walk
3404    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3405    /// resolve every declared migration script against the layout
3406    /// root), and the [`crate::render::servico_m2_overlay`] per-
3407    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3408    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3409    /// projection (caixa-core/src/render.rs, which drives the
3410    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3411    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3412    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3413    /// A future extension of the outer `:upgrade-from` axis (a per-
3414    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3415    /// resolves at admission time so a cluster-specific migration
3416    /// policy can tighten a caixa-declared step without re-authoring
3417    /// the `caixa.lisp`, promotion of the plain
3418    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3419    /// partition once runtime-resolved hot-upgrade instructions land,
3420    /// per-entry priority annotation once multi-strategy fan-out
3421    /// lands) would have had to be threaded through all six open-
3422    /// coded copies in lockstep or one consumer would silently
3423    /// disagree with the peers on which upgrade slice a given Caixa
3424    /// resolves to — a six-consumer split at the enumerator, the
3425    /// three-stage validate pass, the script-path probe walk, and the
3426    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3427    /// field naming the upgrade-drift root cause. Lifting the
3428    /// resolution rule to a typed method on the substrate primitive
3429    /// means every downstream consumer of the caixa's per-`Caixa`
3430    /// OTP-appup outer-slice surface reaches for exactly one typed
3431    /// dispatch — the resolver's accept-set migrates as a unit on any
3432    /// future axis addition.
3433    ///
3434    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3435    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3436    /// outer-`Caixa` `&[Composite]` composite-slice projection
3437    /// pattern the sibling `:children`
3438    /// [`crate::supervisor::ChildSpec`] / `:membros`
3439    /// [`crate::aplicacao::Membro`] / `:contratos`
3440    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3441    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3442    /// `Option<&Composite>` composite-reference family the sibling
3443    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3444    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3445    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3446    /// `Option<&Composite>` altitude, extended here to the outer-
3447    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3448    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3449    /// (0137e5a) — same "one typed dispatch on the substrate
3450    /// primitive, thin projections at each consumer" discipline
3451    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3452    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3453    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3454    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3455    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3456    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3457    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3458    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3459    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3460    /// slice" projection pattern onto the sibling M2 typed-composite-
3461    /// element axis (`UpgradeFromEntry` composite, matching the
3462    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3463    /// different altitude).
3464    ///
3465    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3466    /// because every downstream consumer of the hot-upgrade list
3467    /// treats it as a read-only sequence — the slice-view is the
3468    /// narrowest borrow that supports every present + roadmapped
3469    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3470    /// serialization through
3471    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3472    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3473    /// the typed view reaches for (the storage-side `Vec` remains
3474    /// reachable through the `pub upgrade_from` field for the
3475    /// mutation-carrying serde round-trip and per-test fixture-
3476    /// mutation paths). Named `upgrade_from()` to match the storage
3477    /// field's `snake_case` name; the kebab-case author-surface tag
3478    /// `:upgrade-from` is the same axis after tatara-lisp's
3479    /// kebab↔snake fold and the accessor's identity maps onto the
3480    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3481    /// already carries.
3482    #[must_use]
3483    pub const fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3484        self.upgrade_from.as_slice()
3485    }
3486
3487    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3488    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3489    /// slice accessor every consumer of the top-level manifest's per-
3490    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3491    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3492    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3493    /// the same backing buffer the raw `self.children.as_slice()` field
3494    /// access borrows from. Empty-slice-carrying (the "no static children
3495    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3496    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3497    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3498    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3499    /// on those arms without any silent `None` collapse).
3500    ///
3501    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3502    /// static-child list — the load-bearing container of every per-
3503    /// child `{caixa, versao, restart}` triple the wasm-operator's
3504    /// hierarchical reconciler dispatches on at supervisor-tree
3505    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3506    /// static-child list translated onto pleme-io's typed
3507    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3508    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3509    /// dispatch fans on). Every per-child axis threads through a lifted
3510    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3511    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3512    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3513    /// version-requirement scalar accessor, and the
3514    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3515    /// per-child post-exit restart-decision-policy discriminant
3516    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3517    /// tree path first passes through this outer accessor onto the
3518    /// slice and then dispatches per-child through the inner accessors
3519    /// — the two-level dispatch means every per-`:children` reader now
3520    /// routes through a typed dispatch on the substrate primitive at
3521    /// both altitudes.
3522    ///
3523    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3524    /// accessed inline at three production sites across two files —
3525    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3526    /// declared-slot enumerator's `!self.children.is_empty()` presence
3527    /// probe (caixa-core/src/manifest.rs, which drives the
3528    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3529    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3530    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3531    /// per-supervisor typed-view composer's `self.children.clone()`
3532    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3533    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3534    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3535    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3536    /// `:children :caixa` self-parent refusal probe's
3537    /// `&caixa.children`-borrowed
3538    /// [`crate::supervisor::validate_no_self_supervision`] input
3539    /// (caixa-core/src/layout.rs, which pins the "no child names the
3540    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3541    /// extension of the outer `:children` axis (a per-cluster
3542    /// `:children-overrides` overlay the wasm-engine operator resolves
3543    /// at admission time so a cluster-specific child-set can tighten
3544    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3545    /// promotion of the plain `Vec<ChildSpec>` to a richer
3546    /// `{static, dynamic}` partition once Erlang/OTP's
3547    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3548    /// axis, per-child priority annotation once multi-strategy fan-out
3549    /// lands) would have had to be threaded through all three open-
3550    /// coded copies in lockstep or one consumer would silently
3551    /// disagree with the peers on which child slice a given Caixa
3552    /// resolves to — the enumerator's presence probe reading the raw
3553    /// slot while the peer view-composer's fold-in path read an
3554    /// operator-resolved slot would silently split the paired
3555    /// declared-slot enumerator and typed-view composition, and the
3556    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3557    /// refusal probe reading a third borrow would silently drift the
3558    /// cross-slot coherence gate's traversal input from the two peers,
3559    /// a three-consumer split at the enumerator, the view composer,
3560    /// and the self-parent gate far from the source `caixa.lisp` with
3561    /// no field naming the child-set-drift root cause. Lifting the
3562    /// resolution rule to a typed method on the substrate primitive
3563    /// means every downstream consumer of the caixa's per-`Caixa`
3564    /// OTP-supervisor outer-slice surface reaches for exactly one
3565    /// typed dispatch — the resolver's accept-set migrates as a unit
3566    /// on any future axis addition.
3567    ///
3568    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3569    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3570    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3571    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3572    /// at the outer altitude of the closed inner-`SupervisorSpec`
3573    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3574    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3575    /// borrow-shared" outer-accessor discipline extended onto the
3576    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3577    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3578    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3579    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3580    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3581    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3582    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3583    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3584    /// M2 typed-composite-element axis
3585    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3586    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3587    /// different altitude).
3588    ///
3589    /// Returns `&[crate::supervisor::ChildSpec]` (not
3590    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3591    /// child list treats it as a read-only sequence — the slice-view
3592    /// is the narrowest borrow that supports every present +
3593    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3594    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3595    /// input, `serde` slice-serialization) without leaking the backing
3596    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3597    /// reaches for (the storage-side `Vec` remains reachable through
3598    /// the `pub children` field for the mutation-carrying serde round-
3599    /// trip and per-test fixture-mutation paths, including the
3600    /// [`Self::supervisor_view`] fold-in path that clones the slot
3601    /// into the typed view). Named `children()` to match the storage
3602    /// field's name verbatim and the tatara-lisp author-surface term
3603    /// (`:children`) the field's own docstring already carries; the
3604    /// accessor's identity maps onto the canonical OTP supervision
3605    /// vocabulary the [`Caixa::children`] field's docstring already
3606    /// reaches for ("Static children of a supervisor").
3607    #[must_use]
3608    pub const fn children(&self) -> &[crate::supervisor::ChildSpec] {
3609        self.children.as_slice()
3610    }
3611
3612    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3613    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3614    /// accessor every consumer of the top-level manifest's per-Aplicacao
3615    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3616    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3617    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3618    /// same backing buffer the raw `self.membros.as_slice()` field access
3619    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3620    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3621    /// and every partially-authored Aplicacao carries before the
3622    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3623    /// `&[Membro]` degenerates to an empty slice on those arms without any
3624    /// silent `None` collapse).
3625    ///
3626    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3627    /// per-Aplicacao member list — the load-bearing container of every
3628    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3629    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3630    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3631    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3632    /// the `:entrada :para` external-gateway destination validates
3633    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3634    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3635    /// threads through a lifted per-entry accessor on the
3636    /// [`crate::aplicacao::Membro`] type: the
3637    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3638    /// identity scalar accessor (4a32abf) and the peer
3639    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3640    /// version-requirement scalar accessor (a40b0e3). Every downstream
3641    /// consumer of the mesh-graph path first passes through this outer
3642    /// accessor onto the slice and then dispatches per-member through
3643    /// the inner accessors — the two-level dispatch means every per-
3644    /// `:membros` reader now routes through a typed dispatch on the
3645    /// substrate primitive at both altitudes.
3646    ///
3647    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3648    /// inline at three production sites across two files — the
3649    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3650    /// enumerator's `!self.membros.is_empty()` presence probe
3651    /// (caixa-core/src/manifest.rs, which drives the
3652    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3653    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3654    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3655    /// composer's `self.membros.clone()` per-member fold-in path
3656    /// (caixa-core/src/manifest.rs, which materializes the typed
3657    /// [`crate::aplicacao::AplicacaoSpec`] view every
3658    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3659    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3660    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3661    /// [`crate::aplicacao::validate_no_self_membership`] input
3662    /// (caixa-core/src/layout.rs, which pins the "no member names the
3663    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3664    /// extension of the outer `:membros` axis (a per-cluster
3665    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3666    /// admission time so a cluster-specific member-set can tighten a
3667    /// caixa-declared list without re-authoring the `caixa.lisp`,
3668    /// promotion of the plain `Vec<Membro>` to a richer
3669    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3670    /// members land as a typed axis, per-member priority annotation once
3671    /// multi-strategy fan-out lands) would have had to be threaded
3672    /// through all three open-coded copies in lockstep or one consumer
3673    /// would silently disagree with the peers on which member slice a
3674    /// given Caixa resolves to — the enumerator's presence probe reading
3675    /// the raw slot while the peer view-composer's fold-in path read an
3676    /// operator-resolved slot would silently split the paired
3677    /// declared-slot enumerator and typed-view composition, and the
3678    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3679    /// refusal probe reading a third borrow would silently drift the
3680    /// cross-slot coherence gate's traversal input from the two peers, a
3681    /// three-consumer split at the enumerator, the view composer, and
3682    /// the self-membership gate far from the source `caixa.lisp` with no
3683    /// field naming the member-set-drift root cause. Lifting the
3684    /// resolution rule to a typed method on the substrate primitive
3685    /// means every downstream consumer of the caixa's per-`Caixa`
3686    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3687    /// typed dispatch — the resolver's accept-set migrates as a unit on
3688    /// any future axis addition.
3689    ///
3690    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3691    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3692    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3693    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3694    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3695    /// altitude. Peer at the outer altitude of the closed inner-
3696    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3697    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3698    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3699    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3700    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3701    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3702    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3703    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3704    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3705    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3706    /// pattern onto the sibling M3 typed-composite-element axis
3707    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3708    /// [`crate::AplicacaoSpec::membros`] element type at a different
3709    /// altitude).
3710    ///
3711    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3712    /// because every downstream consumer of the member list treats it
3713    /// as a read-only sequence — the slice-view is the narrowest borrow
3714    /// that supports every present + roadmapped consumer (`.iter()`,
3715    /// `.len()`, `.is_empty()`, the
3716    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3717    /// input, `serde` slice-serialization) without leaking the backing
3718    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3719    /// reaches for (the storage-side `Vec` remains reachable through the
3720    /// `pub membros` field for the mutation-carrying serde round-trip
3721    /// and per-test fixture-mutation paths, including the
3722    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3723    /// the typed view). Named `membros()` to match the storage field's
3724    /// name verbatim and the tatara-lisp author-surface term
3725    /// (`:membros`) the field's own docstring already carries; the
3726    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3727    /// vocabulary the [`Caixa::membros`] field's docstring already
3728    /// reaches for ("Member Servicos that make up this Aplicacao").
3729    #[must_use]
3730    pub const fn membros(&self) -> &[crate::aplicacao::Membro] {
3731        self.membros.as_slice()
3732    }
3733
3734    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3735    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3736    /// inter-Servico contract-list slice accessor every consumer of the
3737    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3738    /// slice-view keys off — returns the author-declared `:contratos`
3739    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3740    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3741    /// backing buffer the raw `self.contratos.as_slice()` field access
3742    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3743    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3744    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3745    /// single member with no inter-Servico edge carries; the returned
3746    /// `&[WitContract]` degenerates to an empty slice on those arms
3747    /// without any silent `None` collapse).
3748    ///
3749    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3750    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3751    /// container of every per-edge `{de, para, wit, endpoint | subject |
3752    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3753    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3754    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3755    /// adjacency-list seed dispatch on at mesh-artifact materialization
3756    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3757    /// `:membros` vertex set resolves against, closed by the
3758    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3759    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3760    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3761    /// per-edge axis threads through a lifted per-entry accessor on the
3762    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3763    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3764    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3765    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3766    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3767    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3768    /// and the WIT-world discriminant. Every downstream consumer of the
3769    /// mesh-graph edge path first passes through this outer accessor
3770    /// onto the slice and then dispatches per-contract through the
3771    /// inner accessors — the two-level dispatch means every
3772    /// per-`:contratos` reader now routes through a typed dispatch on
3773    /// the substrate primitive at both altitudes.
3774    ///
3775    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3776    /// accessed inline at two production sites in
3777    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3778    /// mesh-slot declared-slot enumerator's
3779    /// `!self.contratos.is_empty()` presence probe (which drives the
3780    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3781    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3782    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3783    /// typed-view composer's `self.contratos.clone()` per-contract
3784    /// fold-in path (which materializes the typed
3785    /// [`crate::aplicacao::AplicacaoSpec`] view every
3786    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3787    /// downstream `caixa-mesh` renderer dispatches on). A future
3788    /// extension of the outer `:contratos` axis (a per-cluster
3789    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3790    /// at admission time so a cluster-specific edge-set can tighten a
3791    /// caixa-declared list without re-authoring the `caixa.lisp`,
3792    /// promotion of the plain `Vec<WitContract>` to a richer
3793    /// `{static, dynamic}` partition once runtime-resolved contract
3794    /// edges land, per-edge policy annotation once the M4 per-edge
3795    /// policy overlay axis lands) would have had to be threaded through
3796    /// both open-coded copies in lockstep or one consumer would
3797    /// silently disagree with the peer on which edge slice a given
3798    /// Caixa resolves to — the enumerator's presence probe reading the
3799    /// raw slot while the peer view-composer's fold-in path read an
3800    /// operator-resolved slot would silently split the paired
3801    /// declared-slot enumerator and typed-view composition, a
3802    /// two-consumer split at the enumerator and the view composer far
3803    /// from the source `caixa.lisp` with no field naming the edge-set-
3804    /// drift root cause. Lifting the resolution rule to a typed method
3805    /// on the substrate primitive means every downstream consumer of
3806    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3807    /// reaches for exactly one typed dispatch — the resolver's
3808    /// accept-set migrates as a unit on any future axis addition.
3809    ///
3810    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3811    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3812    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3813    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3814    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3815    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3816    /// mesh-slot arm of the composite-slice sub-family the sibling
3817    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3818    /// Peer at the outer altitude of the closed inner-
3819    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3820    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3821    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3822    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3823    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3824    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3825    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3826    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3827    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3828    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3829    /// pattern onto the sibling M3 typed-composite-element axis
3830    /// ([`crate::aplicacao::WitContract`] composite, matching the
3831    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3832    /// different altitude).
3833    ///
3834    /// Returns `&[crate::aplicacao::WitContract]` (not
3835    /// `&Vec<WitContract>`) because every downstream consumer of the
3836    /// contract list treats it as a read-only sequence — the slice-view
3837    /// is the narrowest borrow that supports every present + roadmapped
3838    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3839    /// discriminant dispatch, `serde` slice-serialization) without
3840    /// leaking the backing `Vec`'s grow/push/reserve surface no
3841    /// consumer of the typed view reaches for (the storage-side `Vec`
3842    /// remains reachable through the `pub contratos` field for the
3843    /// mutation-carrying serde round-trip and per-test fixture-mutation
3844    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3845    /// clones the slot into the typed view). Named `contratos()` to
3846    /// match the storage field's name verbatim and the tatara-lisp
3847    /// author-surface term (`:contratos`) the field's own docstring
3848    /// already carries; the accessor's identity maps onto the canonical
3849    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3850    /// docstring already reaches for ("WIT-typed inter-Servico
3851    /// contracts").
3852    #[must_use]
3853    pub const fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3854        self.contratos.as_slice()
3855    }
3856
3857    /// Compose the Aplicacao-related flat slots into a single typed
3858    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3859    /// downstream renderer consumption. Returns `None` when the
3860    /// caixa isn't a `:kind Aplicacao`.
3861    #[must_use]
3862    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3863        if !self.kind().is_aplicacao() {
3864            return None;
3865        }
3866        Some(crate::aplicacao::AplicacaoSpec {
3867            membros: self.membros().to_vec(),
3868            contratos: self.contratos().to_vec(),
3869            politicas: self.politicas().cloned().unwrap_or_default(),
3870            placement: self.placement().cloned().unwrap_or_default(),
3871            entrada: self.entrada().cloned(),
3872        })
3873    }
3874
3875    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3876    /// *declares* a value on, in canonical declaration order
3877    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3878    /// `:entrada`). A slot counts as declared when its backing field
3879    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3880    ///
3881    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3882    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3883    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3884    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3885    /// caixa-flux / caixa-helm renderers only emit them for an
3886    /// Aplicacao. On any *other* kind a declared mesh slot is the
3887    /// manifest field's documented "ignored otherwise" (see the
3888    /// `:membros` … `:entrada` field docs): it silently passes
3889    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3890    /// rendered — far from the source caixa.lisp.
3891    /// [`crate::StandardLayout::verify`] consults this to reject that
3892    /// silent-drop at caixa-build time
3893    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3894    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3895    /// a slot foreign to the kind is a build error, not a silent drop.
3896    ///
3897    /// Lifted as a typed method (rather than an inline disjunction at
3898    /// the verify call site) so the mesh-slot set lives in one place —
3899    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3900    /// overlay, distributed-app takeover config) is one push here, and
3901    /// every consumer reaching for "which mesh slots are set" (the
3902    /// verify gate, a future `feira lint` kind-coherence advisory)
3903    /// inherits the canonical order without rolling its own.
3904    ///
3905    /// Each per-arm kebab-case label is routed through the peer
3906    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3907    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3908    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3909    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3910    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3911    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3912    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3913    /// kebab-case label + renderer-side artifact key) route through one
3914    /// canonical declaration per arm — same discipline the peer
3915    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3916    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3917    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3918    /// axis, extended here to close the M3 mesh-slot author-facing-label
3919    /// axis so both altitudes of the typed-slot algebra
3920    /// (per-Servico M2 + per-Aplicacao M3) share the same
3921    /// "one canonical byte-string per arm, next to the axis" discipline.
3922    #[must_use]
3923    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3924        let mut slots = Vec::new();
3925        if !self.membros().is_empty() {
3926            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3927        }
3928        if !self.contratos().is_empty() {
3929            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3930        }
3931        if self.politicas().is_some() {
3932            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3933        }
3934        if self.placement().is_some() {
3935            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3936        }
3937        if self.entrada().is_some() {
3938            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3939        }
3940        slots
3941    }
3942
3943    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3944    /// caixa *declares* a value on, in canonical declaration order
3945    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3946    /// `:children`). A slot counts as declared when its backing field
3947    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3948    ///
3949    /// The supervisor-tree slots compose the typed OTP supervisor of a
3950    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3951    /// `:children` field docs above). [`Self::supervisor_view`] only
3952    /// folds them into a validatable [`SupervisorSpec`] when the kind
3953    /// matches (returns `None` otherwise), and the wasm-operator's
3954    /// hierarchical reconciler only consumes them for a Supervisor. On
3955    /// any *other* kind a declared supervisor slot is the manifest
3956    /// field's documented "ignored otherwise" (see the `:estrategia` …
3957    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3958    /// and then vanishes — never validated, never reconciled — far from
3959    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3960    /// this to reject that silent-drop at caixa-build time
3961    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3962    /// exact mirror of the [`Self::declared_mesh_slots`] /
3963    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3964    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3965    /// error, not a silent drop.
3966    #[must_use]
3967    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3968        let mut slots = Vec::new();
3969        if self.estrategia().is_some() {
3970            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3971        }
3972        if self.max_restarts().is_some() {
3973            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3974        }
3975        if self.restart_window().is_some() {
3976            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3977        }
3978        if !self.children().is_empty() {
3979            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3980        }
3981        slots
3982    }
3983
3984    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3985    /// caixa *declares* a value on, in canonical declaration order
3986    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3987    /// declared when its backing field carries a value — a `Some(...)`,
3988    /// or a non-empty `Vec`.
3989    ///
3990    /// The M2 slots configure the runtime of a long-running wasm
3991    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3992    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3993    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3994    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3995    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3996    /// emit these slots for a Servico; on any *other* kind a declared M2
3997    /// slot is the manifest field's documented "ignored otherwise": its
3998    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3999    /// but the value is never rendered into a chart / programs.yaml entry
4000    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
4001    /// vanishes, far from the source caixa.lisp.
4002    /// [`crate::StandardLayout::verify`] consults this to reject that
4003    /// silent-drop at caixa-build time
4004    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
4005    /// mirror of the [`Self::declared_mesh_slots`] /
4006    /// [`Self::declared_supervisor_slots`] gates on the peer
4007    /// kind-exclusive slot sets: a slot foreign to the kind is a build
4008    /// error, not a silent drop.
4009    ///
4010    /// Each per-arm kebab-case label is routed through the peer
4011    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
4012    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
4013    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
4014    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
4015    /// both halves of the M2 top-level slot's dual axis (author-facing
4016    /// kebab-case label + renderer-side camelCase overlay-container wire
4017    /// key) route through one canonical declaration per arm — same
4018    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
4019    /// author-label consts (889dc18) establish on the sibling
4020    /// per-callback axis inside the `:behavior` overlay block.
4021    #[must_use]
4022    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
4023        let mut slots = Vec::new();
4024        if self.limits().is_some() {
4025            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
4026        }
4027        if self.behavior().is_some() {
4028            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
4029        }
4030        if !self.upgrade_from().is_empty() {
4031            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
4032        }
4033        slots
4034    }
4035
4036    /// The kebab-case `:slot` tags of every code-surface slot this caixa
4037    /// declares a value on that its [`CaixaKind`] doesn't natively own,
4038    /// in canonical declaration order (`:exe` → `:servicos`). A
4039    /// code-surface slot is owned by exactly one kind: `:exe` by
4040    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
4041    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
4042    /// `ComputeUnit` daemon surface).
4043    ///
4044    /// Each is silently ignored when declared on the wrong kind: the
4045    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
4046    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
4047    /// code-running kind a declared `:exe` / `:servicos` is the manifest
4048    /// field's documented "ignored otherwise" — its path is checked for
4049    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
4050    /// (which run after [`Caixa::from_lisp`]), but the value is never
4051    /// rendered into a build target or programs.yaml entry. It silently
4052    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
4053    /// caixa.lisp, with no field naming which slot is foreign.
4054    ///
4055    /// [`crate::StandardLayout::verify`] consults this to reject that
4056    /// silent-drop at caixa-build time
4057    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
4058    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
4059    /// gates ([`Self::declared_servico_slots`] /
4060    /// [`Self::declared_supervisor_slots`] /
4061    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
4062    /// axis to be closed on the typed surface. The Supervisor /
4063    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
4064    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
4065    /// diagnostics — they fire ahead of this gate on the same `verify`
4066    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
4067    /// and this method is moot. For Biblioteca / Binario / Servico, this
4068    /// gate fires when a code-running kind declares another code-running
4069    /// kind's exclusive code surface.
4070    ///
4071    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
4072    /// may legitimately ship a `lib/` helper that the underlying
4073    /// substrate (the nix flake for Binario, the wasm component build
4074    /// for Servico) bundles into its build, so the slot's
4075    /// declared-on-wrong-kind cardinality isn't a structural error on
4076    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
4077    /// is the native case (the slot's owning kind). Supervisor /
4078    /// Aplicacao declaring `:bibliotecas` is gated upstream by
4079    /// [`crate::LayoutError::SupervisorOwnsCode`] /
4080    /// [`crate::LayoutError::AplicacaoOwnsCode`].
4081    ///
4082    /// Lifted as a typed method (rather than an inline disjunction at
4083    /// the verify call site) so the foreign-code-slot set lives in one
4084    /// place — a future kind that gains its own code-surface slot is
4085    /// one push here, and every consumer reaching for "which code
4086    /// surfaces are foreign to this kind" (the verify gate, a future
4087    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
4088    /// per-caixa build-target classifier) inherits the canonical order
4089    /// without rolling its own.
4090    #[must_use]
4091    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
4092        let mut slots = Vec::new();
4093        if !self.exe().is_empty() && !self.kind().requires_exe() {
4094            slots.push(":exe");
4095        }
4096        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
4097            slots.push(":servicos");
4098        }
4099        slots
4100    }
4101
4102    /// Validate every entry of `:deps` and `:deps-dev` through
4103    /// [`Dep::validate`] — closing the parity loop with the per-axis
4104    /// `:versao` gates already wired into the typed-graph
4105    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
4106    /// 9888b13) and typed supervisor tree
4107    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
4108    ///
4109    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
4110    /// were the only `:versao` axes still untyped past
4111    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
4112    /// as a String without parsing it, so a malformed-but-non-empty
4113    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4114    /// silently passed parse and the `semver::Error` surfaced at
4115    /// lacre-resolve time, far from the source caixa.lisp, with no
4116    /// field naming which `:deps` entry carried the typo. Lifting the
4117    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4118    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4119    /// every requirement string past `validate_deps` is round-trippable
4120    /// through [`crate::parse_requirement`] without re-checking at the
4121    /// resolver layer.
4122    ///
4123    /// Both lists run through the same per-entry validator so a typo
4124    /// in `:deps-dev` surfaces with the same diagnostic as one in
4125    /// `:deps` — neither axis is a second-class citizen of the typed
4126    /// surface.
4127    ///
4128    /// Within each list, [`DepError::DuplicateNome`] closes the
4129    /// set-not-multiset discipline on the `:nome` axis: two entries
4130    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4131    /// triples that the caixa-resolver's lacre pipeline collapses to one
4132    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4133    /// silently overwrites the first at `concrete_versao`-resolve time
4134    /// (the same "second wins / one silently overwrites the other"
4135    /// shape the peer typed-graph duplicate gates already close on every
4136    /// other Vec-shaped authoring surface that keys by name). The
4137    /// duplicate check fires per-list and runs *after* each per-entry
4138    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4139    /// surfaces its narrower per-entry diagnostic
4140    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4141    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4142    /// diagnostic — the canonical "per-entry shape before cross-entry
4143    /// uniqueness" precedence the peer `:children :caixa`
4144    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4145    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4146    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4147    /// ([`crate::AplicacaoSpec::validate_placement`]),
4148    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4149    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4150    /// and the within-`:upgrade-from`-entry per-instruction-class
4151    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4152    /// [`crate::UpgradeError::DuplicateStateChange`],
4153    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4154    ///
4155    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4156    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4157    /// same name in both tables (the dev table's pin overrides the
4158    /// runtime table's pin in test/dev contexts), and caixa's surface
4159    /// mirrors that convention until a deliberate choice retires the
4160    /// override pattern. Only within-list duplicates are structurally
4161    /// incoherent — those are what this gate closes.
4162    ///
4163    /// Compound per-`Caixa` entry gate on the dep-graph axis: folds the
4164    /// two standalone dep-list validators — the per-entry + within-list
4165    /// duplicate-`:nome` walk (the [`Dep::validate`] +
4166    /// [`crate::render::insert_first_seen`] cascade this method opened
4167    /// on) and the cross-slot self-edge gate
4168    /// ([`crate::dep::validate_no_self_dep`]) — onto one substrate
4169    /// primitive on [`Caixa`]. The two arms run in the same canonical
4170    /// order the layout pipeline
4171    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4172    /// author-time gate) has always sequenced them (per-entry +
4173    /// cross-entry duplicate → cross-slot self-edge), so the fold is
4174    /// byte-for-byte equivalent to the pre-fold two-block cascade at
4175    /// that call site (pinned by the paired
4176    /// `validate_deps_folds_per_entry_arm_matches_gate` /
4177    /// `validate_deps_folds_self_edge_arm_matches_gate` equivalence
4178    /// pins and the `validate_deps_per_entry_arm_fires_before_self_edge_arm`
4179    /// ordering pin). Self-contained on `&self` — resolves its three
4180    /// inputs ([`Self::deps`], [`Self::deps_dev`], [`Self::nome`])
4181    /// through the substrate primitives' own accessor family, the same
4182    /// posture every peer per-slot compound gate
4183    /// ([`crate::AplicacaoSpec::validate_contratos`],
4184    /// [`crate::MeshPolicy::validate`],
4185    /// [`crate::SupervisorSpec::validate_children`],
4186    /// [`Self::validate_upgrade_from`]) already carries.
4187    ///
4188    /// Prior to this lift [`crate::dep::validate_no_self_dep`] lived
4189    /// only open-coded at the layout wire-up site
4190    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs)
4191    /// as a standalone two-arg dispatch immediately after this method's
4192    /// per-entry + cross-entry walk, both wrapped through the same
4193    /// [`crate::LayoutError::DepsViolation`] envelope: every future
4194    /// consumer that wanted to gate the dep-graph as a whole — the
4195    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4196    /// per-CR admission webhook re-checking `:deps` / `:deps-dev` after
4197    /// a per-entry patch, a future `feira validate --deps` per-caixa
4198    /// admission verb, a per-`:deps` overlay resolver a per-cluster
4199    /// overlay lift would materialize (each the deferred consumer this
4200    /// method's peer [`Self::deps`] / [`Self::deps_dev`] accessors'
4201    /// docstrings already name) — was structurally forced to either
4202    /// re-inline the two-dispatch cascade in lockstep with the layout
4203    /// wire-up (the duplication the PRIME DIRECTIVE names as a bug) or
4204    /// call the whole [`crate::layout::StandardLayout::verify`] pipeline
4205    /// and pay every peer per-Caixa gate to re-check one slot. Post-fold
4206    /// each such consumer reaches the two-arm compound gate through one
4207    /// call on the substrate primitive.
4208    pub fn validate_deps(&self) -> Result<(), DepError> {
4209        for &list in crate::dep::DepList::ALL {
4210            let mut seen = std::collections::HashSet::new();
4211            for dep in self.deps_of(list) {
4212                dep.validate()?;
4213                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4214                    DepError::duplicate_nome(dep.nome(), list.as_str())
4215                })?;
4216            }
4217        }
4218        crate::dep::validate_no_self_dep(self.deps(), self.deps_dev(), self.nome())?;
4219        Ok(())
4220    }
4221
4222    /// Run a per-slot typed validator on `self` and, on the per-arm
4223    /// parser-side error arm, thread the error into a paired
4224    /// [`crate::LayoutError`] wrap under `self.nome()`. Substrate
4225    /// primitive folding the 18 self-similar layout-pipeline wire-up
4226    /// sites at [`crate::layout::StandardLayout::verify`] that carry
4227    /// the identical
4228    /// `caixa.validate_<slot>().map_err(|err| crate::LayoutError::<slot>_violation(caixa, err))?;`
4229    /// cascade onto one dispatch. Each of the eighteen sites (`:nome`,
4230    /// `:nome`-chart-name-budget, `:versao`, `:deps`, `:etiquetas`,
4231    /// `:autores`, `:repositorio`, `:descricao`, `:licenca`, `:edicao`,
4232    /// `:bibliotecas`/`:exe`/`:servicos` code-path shape, `:limits`,
4233    /// `:behavior`, `:upgrade-from`, `:restart-window`, per-Supervisor
4234    /// shape, per-Aplicacao shape, per-Acao shape) carried the same
4235    /// four-line "run a per-slot typed validator on `caixa` and, on the
4236    /// per-arm parser-side error arm, thread it into the paired
4237    /// [`crate::LayoutError`] one-slot envelope through the substrate-
4238    /// canonical `layout_violation_ctors!` family (131ca0d)" cascade,
4239    /// differing only in the two names bound at each site — the
4240    /// validator (`Caixa::validate_deps` / `validate_nome` / ...) and
4241    /// the paired ctor (`LayoutError::deps_violation` / ...). Eighteen
4242    /// consumers, one identical shape, one substrate primitive on
4243    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
4244    /// a bug — on the second half of the per-slot cascade the peer
4245    /// substrate primitives on the [`crate::LayoutError`]-wrap side
4246    /// (the `layout_violation_ctors!` macro 131ca0d, the
4247    /// `layout_slot_kind_ctors!` macro 0419438, the `layout_nome_only_ctors!`
4248    /// macro 3fe3dd7, the [`crate::LayoutError::missing_entry`] ctor
4249    /// 1b09f9d, the [`crate::layout::StandardLayout::probe_declared_entry`]
4250    /// primitive fda1e35) each closed on their sibling envelopes; the
4251    /// first half of the cascade (the per-slot compound gates
4252    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
4253    /// baa4688, [`Self::validate_behavior`] 0d2877a,
4254    /// [`Self::validate_upgrade_from`] d6801df,
4255    /// [`Self::validate_aplicacao_shape`] 949a7a0,
4256    /// [`Self::validate_supervisor_shape`] 4c70105,
4257    /// [`Self::validate_acao_shape`] 5d6df54,
4258    /// [`Self::validate_kind_slot_coherence`] f0d286e,
4259    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
4260    /// [`Self::validate_ci_kind_coherence`] 9b55beb,
4261    /// [`Self::validate_required_kind_slot`] 9c385d8) each closed on
4262    /// their per-slot compound gates.
4263    ///
4264    /// Composes the [`crate::layout::LayoutError`] wrap and the per-slot
4265    /// typed validator through two typed callables: `gate` runs on
4266    /// `self` and yields a per-slot error `E`; on the `Err(E)` arm
4267    /// `wrap` re-wraps that error under `self` into a
4268    /// [`crate::layout::LayoutError`]. The `Ok(())` arm passes through
4269    /// verbatim as the fold's identity element — byte-equal to the
4270    /// pre-lift `Result::map_err` short-circuit at the `?;` marker
4271    /// every wire-up site formerly carried. Every future consumer that
4272    /// wants to run one of the per-slot gates and thread its error
4273    /// through the layout wrap (the deferred
4274    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4275    /// webhook per-slot re-check, a future `feira validate --<slot>`
4276    /// per-caixa admission verb, an overlay resolver re-running one
4277    /// gate after a per-slot patch) reaches the two-callable dispatch
4278    /// through one call rather than re-inlining the four-line cascade
4279    /// in lockstep with the pre-existing 18 wire-ups. The two callables
4280    /// reach the primitive as first-class type-checked references
4281    /// rather than the pre-lift `.map_err(|err| CTOR(caixa, err))`
4282    /// closure body — so a mismatch between the validator's `E` type
4283    /// and the ctor's `E` bound trips at the wire-up site (compile-
4284    /// time) rather than at the closure body (also compile-time, but
4285    /// with a diagnostic pointing at the closure expression rather
4286    /// than the two named callables).
4287    pub fn run_layout_gate<E, W>(
4288        &self,
4289        gate: impl FnOnce(&Caixa) -> Result<(), E>,
4290        wrap: W,
4291    ) -> Result<(), crate::LayoutError>
4292    where
4293        W: FnOnce(&Caixa, E) -> crate::LayoutError,
4294    {
4295        gate(self).map_err(|err| wrap(self, err))
4296    }
4297
4298    /// Run one arm of the cross-family kind ↔ owned-slot-family
4299    /// coherence cascade on `self`: on a caixa whose [`Self::kind`] does
4300    /// not own the typed-slot family named by `is_owner`, refuse when
4301    /// the paired `accumulator` reports any declared slot in that
4302    /// family; otherwise pass. Substrate primitive folding the three
4303    /// self-similar four-line
4304    /// `if !self.kind().is_<owner>() { let slots = self.declared_<family>_slots();
4305    /// if !slots.is_empty() { return Err(<wrap>(self, slots)); } }`
4306    /// arms at [`Self::validate_kind_slot_coherence`] onto one dispatch.
4307    /// Three consumers (M3 mesh — Aplicacao-owner, supervisor-tree —
4308    /// Supervisor-owner, M2 Servico-runtime — Servico-owner), one
4309    /// identical shape, one substrate primitive on [`Caixa`] closing
4310    /// the duplication the PRIME DIRECTIVE names as a bug on the
4311    /// outer kind-coherence arm shape — peer with the substrate
4312    /// primitives on the two adjacent halves of the same three-arm
4313    /// cascade the sibling [`Self::declared_mesh_slots`] /
4314    /// [`Self::declared_supervisor_slots`] /
4315    /// [`Self::declared_servico_slots`] accumulator family closes on
4316    /// the inner slot-set enumerator axis and the sibling
4317    /// [`crate::layout::layout_slot_kind_ctors!`] macro (0419438)
4318    /// closes on the inner wrap-envelope ctor axis. Each of the three
4319    /// [`Self::validate_kind_slot_coherence`] arms now reads through
4320    /// one call across every altitude of the per-arm cascade:
4321    /// one dispatch on this primitive for the outer guard shape, one
4322    /// dispatch on `Self::declared_<family>_slots` for the accumulator,
4323    /// one dispatch on `crate::LayoutError::<family>_on_non_<owner>`
4324    /// for the wrap ctor.
4325    ///
4326    /// Composes the outer owner-kind guard, the per-family accumulator,
4327    /// and the per-family wrap ctor through three typed callables:
4328    /// `is_owner` runs on `&self.kind()` (a `&CaixaKind` borrow so the
4329    /// `gen_platform::IsVariant`-derived `fn(&CaixaKind) -> bool`
4330    /// per-arm predicates — [`crate::CaixaKind::is_aplicacao`] /
4331    /// [`crate::CaixaKind::is_supervisor`] / [`crate::CaixaKind::is_servico`]
4332    /// — pass verbatim as function references), `accumulator` runs on
4333    /// `&self` and yields the
4334    /// per-family declared-slot list, and `wrap` runs on `(&self,
4335    /// Vec<&'static str>)` and yields the per-family
4336    /// [`crate::LayoutError`] wrap. The `is_owner` short-circuit fires
4337    /// before the accumulator dispatch (so the owner kind of each
4338    /// family passes without invoking `accumulator`, byte-equal to the
4339    /// pre-lift `if !self.kind().is_<owner>() { … }` outer guard's
4340    /// short-circuit — pinned by
4341    /// `run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator`),
4342    /// and the accumulator's `is_empty` short-circuit fires before the
4343    /// wrap dispatch (so a non-owner kind with no declared slot in that
4344    /// family passes without invoking `wrap`, byte-equal to the pre-lift
4345    /// `if !<slots>.is_empty() { … }` inner guard's short-circuit —
4346    /// pinned by
4347    /// `run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap`).
4348    /// The wrap ctor is `FnOnce(&Caixa, Vec<&'static str>) ->
4349    /// crate::LayoutError` — matching the [`crate::layout::layout_slot_kind_ctors!`]
4350    /// macro's per-variant `fn(&Caixa, Vec<&'static str>) -> LayoutError`
4351    /// substrate-canonical ctor shape verbatim, so
4352    /// [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
4353    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
4354    /// [`crate::LayoutError::servico_slots_on_non_servico`] pass as
4355    /// function references without a closure wrap. A mismatch between
4356    /// the ctor's signature and this bound trips at the wire-up site
4357    /// (compile-time) rather than at a closure body.
4358    ///
4359    /// The sibling [`crate::LayoutError::ForeignCodeSlot`] gate on the
4360    /// code-surface family sits outside this primitive because
4361    /// [`Self::declared_foreign_code_slots`] bakes the per-arm kind-
4362    /// check into the accumulator itself (each arm's
4363    /// `!self.kind().requires_<slot>()` guard fires inside the
4364    /// accumulator, not around it), so the code-surface arm carries no
4365    /// outer `is_owner`-shaped guard and its dispatch reads through
4366    /// [`Self::validate_foreign_code_kind_coherence`] verbatim without
4367    /// this primitive — the same posture the `_no_code_` /
4368    /// `_ci_kind_` coherence axes take on their respective per-arm
4369    /// shapes. The primitive here is specific to the "outer
4370    /// non-owner-kind guard + inner accumulator + inner emptiness
4371    /// guard + wrap" arm shape that fires three times in
4372    /// [`Self::validate_kind_slot_coherence`].
4373    ///
4374    /// Every future consumer that wants to gate one kind-owned slot
4375    /// family as a unit outside the composed cascade (the deferred
4376    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4377    /// webhook per-family re-check after a per-slot patch, a future
4378    /// `feira validate --<family>-coherence` per-caixa admission verb,
4379    /// a per-`Caixa` overlay resolver rejecting a kind-foreign patch
4380    /// on one family) reaches the four-line arm through one call
4381    /// rather than re-inlining the outer-guard + accumulator +
4382    /// emptiness-guard + wrap cascade in lockstep with the pre-existing
4383    /// three arms. Every future kind-owned typed-slot family (an
4384    /// `Actor`-owned per-virtual-actor grain slot the M5 Orleans-
4385    /// inspired kind reaches through, a per-Aplicacao overlay slot the
4386    /// M4 CR materializer consults) folds onto
4387    /// [`Self::validate_kind_slot_coherence`] as one additional
4388    /// dispatch on this primitive rather than a fourth open-coded
4389    /// four-line block.
4390    pub fn run_kind_owned_slot_family_gate<F, A, W>(
4391        &self,
4392        is_owner: F,
4393        accumulator: A,
4394        wrap: W,
4395    ) -> Result<(), crate::LayoutError>
4396    where
4397        F: FnOnce(&crate::CaixaKind) -> bool,
4398        A: FnOnce(&Caixa) -> Vec<&'static str>,
4399        W: FnOnce(&Caixa, Vec<&'static str>) -> crate::LayoutError,
4400    {
4401        if is_owner(&self.kind()) {
4402            return Ok(());
4403        }
4404        let slots = accumulator(self);
4405        if slots.is_empty() {
4406            return Ok(());
4407        }
4408        Err(wrap(self, slots))
4409    }
4410
4411    /// Reject `:nome` values the K8s apiserver would refuse at admission
4412    /// time. The top-level Caixa identity flows directly into every
4413    /// substrate-side artifact's `metadata.name` axis: the
4414    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4415    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4416    /// aggregator keys ComputeUnit derivation off
4417    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4418    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4419    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4420    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4421    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4422    /// ([`caixa-mesh::lib::cilium_network_policies`],
4423    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4424    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4425    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4426    /// schema enforces the DNS-1123 label rule on admission; a
4427    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4428    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4429    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4430    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4431    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4432    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4433    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4434    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4435    /// Invalid value` rejection on whichever derived artifact admitted
4436    /// first, far from the source `caixa.lisp` and without any field
4437    /// naming the offending `:nome`.
4438    ///
4439    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4440    /// substrate-side predicate the per-axis name gates already share:
4441    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4442    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4443    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4444    /// diagnostic is self-locating (the offending `:nome` is named
4445    /// verbatim) and the author can grep their `caixa.lisp` for
4446    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4447    /// every per-axis sibling gate already exposes
4448    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4449    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4450    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4451    ///
4452    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4453    /// derive macro stores the raw String) is gated by the narrower
4454    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4455    /// consulted, mirroring the empty-first cascade every per-axis
4456    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4457    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4458    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4459        // Routes through the shared
4460        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4461        // name axes each land on so drift between the eight axes'
4462        // accepted DNS-1123-label sets is structurally impossible.
4463        let nome = self.nome();
4464        crate::render::require_valid_dns_1123_label(
4465            nome,
4466            || ManifestError::NomeEmpty,
4467            |reason| ManifestError::NomeInvalid {
4468                nome: nome.to_string(),
4469                reason,
4470            },
4471        )
4472    }
4473
4474    /// Reject `:nome` values whose joint length with the canonical
4475    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4476    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4477    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4478    /// substrate carries materializes the caixa's `:nome` through the
4479    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4480    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4481    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4482    /// `ChartDir.name` + `Chart.yaml::name`
4483    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4484    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4485    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4486    /// `oci://<registry>/lareira-<nome>` chart ref
4487    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4488    /// admission rule strict-parses against DNS-1123-label, the Helm
4489    /// operator's tracking-secret name is derived from `release_name`
4490    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4491    /// K8s object `metadata.name` axes embed the chart name as a
4492    /// prefix — every one fails admission on a > 63-byte chart name.
4493    ///
4494    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4495    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4496    /// `:nome` of 56–63 bytes silently passed validate (the inner
4497    /// DNS-1123 check accepts the bare `:nome`) but produced a
4498    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4499    /// rejected at admission — far from the source `caixa.lisp`, with
4500    /// no field naming the overflow root cause. The
4501    /// [`lareira_chart_name`] helper's own doc comment
4502    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4503    /// "the M4 admission webhook will pin the joint-length invariant
4504    /// when it lands". This gate lands the invariant at the
4505    /// manifest-validate layer rather than waiting for the apiserver
4506    /// — the same fail-at-the-source posture every peer per-axis
4507    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4508    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4509    /// `:edicao`, etc.) takes.
4510    ///
4511    /// Thin wrapper around
4512    /// [`crate::render::is_lareira_chart_name_shape`] (the
4513    /// substrate-side predicate that composes [`lareira_chart_name`] +
4514    /// [`is_dns_1123_label`] via the lifted
4515    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4516    /// shared parser-shaped reason into the
4517    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4518    /// diagnostic is self-locating (the offending `:nome` is named
4519    /// verbatim alongside the rendered chart name and the budget) and
4520    /// the author can shorten in one edit. The gate runs across every
4521    /// `:kind` — `:nome` is the substrate-wide identity axis any
4522    /// future renderer the substrate adds can derive a
4523    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4524    /// the drift footgun where a future kind grows a chart-emitting
4525    /// render path while the validate cascade doesn't catch it.
4526    ///
4527    /// Runs *after* [`Self::validate_nome`] so the narrower
4528    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4529    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4530    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4531    /// specific shape error rather than the chart-name-budget error,
4532    /// preserving the legitimate "well-shaped `:nome` that happens to
4533    /// overflow the joint cap" arm for this gate.
4534    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4535        let nome = self.nome();
4536        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4537            ManifestError::NomeChartNameBudgetExceeded {
4538                nome: nome.to_string(),
4539                reason,
4540            }
4541        })
4542    }
4543
4544    /// Reject `:versao` values that don't parse as [`semver::Version`].
4545    /// The top-level Caixa version flows directly into every
4546    /// substrate-side artifact that carries a "this is which version of
4547    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4548    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4549    /// SemVer-2-strict at `helm template` / `helm install` time per
4550    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4551    /// `feira publish` Zig-style `v<versao>` git tag
4552    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4553    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4554    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4555    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4556    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4557    /// `skopeo push`, the lacre closure's pinned versions
4558    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4559    /// `:upgrade-from :from` references peers in this exact `versao`
4560    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4561    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4562    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4563    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4564    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4565    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4566    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4567    /// into the version field a peer `:deps :versao` accepts;
4568    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4569    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4570    /// derive macro stores the raw String) and the failure surfaced at
4571    /// the *first* downstream consumer that strict-parses it: at
4572    /// `helm install` time as a chart-version rejection, at
4573    /// `feira publish` time as a malformed git tag, at lacre-resolve
4574    /// time as a `semver::Error` not naming the offending caixa, at
4575    /// `feira upgrade --to <versao>` time as an unresolvable
4576    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4577    /// and without any field naming the offending `:versao`.
4578    ///
4579    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4580    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4581    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4582    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4583    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4584    /// variant, carrying the offending `:versao` verbatim + a
4585    /// parser-shaped reason naming the specific violation, so the
4586    /// diagnostic is self-locating (the author can grep their
4587    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4588    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4589    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4590    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4591    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4592    /// now structurally equivalent (every value past validate is
4593    /// round-trippable through [`semver::Version::parse`] without
4594    /// re-checking at the renderer, resolver, or operator hot-upgrade
4595    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4596    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4597    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4598    ///
4599    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4600    /// the derive macro stores the raw String) is gated by the
4601    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4602    /// consulted, mirroring the empty-first cascade every per-axis
4603    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4604    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4605    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4606    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4607        let versao = self.versao();
4608        if versao.is_empty() {
4609            return Err(ManifestError::VersaoEmpty);
4610        }
4611        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4612            versao: versao.to_string(),
4613            reason: e.to_string(),
4614        })?;
4615        Ok(())
4616    }
4617
4618    /// Compound per-`Caixa` entry gate on the M2 `:upgrade-from` slot:
4619    /// folds the three [`crate::upgrade`] top-level validators — the
4620    /// per-entry shape + cross-entry duplicate-`:from` gate
4621    /// ([`crate::upgrade::validate_upgrade_from`]), the cross-slot
4622    /// `:from < :versao` SemVer-2 precedence gate
4623    /// ([`crate::upgrade::validate_upgrade_from_against_versao`]), and the
4624    /// cross-slot `:state-change` ↔ `:on-state-change` composition gate
4625    /// ([`crate::upgrade::validate_upgrade_from_against_behavior`]) — onto
4626    /// one substrate primitive on [`Caixa`]. The three dispatches run in
4627    /// the same order the layout pipeline
4628    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4629    /// author-time gate) has always sequenced them, so the fold is
4630    /// byte-for-byte equivalent to the pre-fold three-block cascade at
4631    /// that call site (pinned by the per-arm
4632    /// `validate_upgrade_from_folds_per_entry_arm_matches_gate` /
4633    /// `_folds_versao_arm_matches_gate` / `_folds_behavior_arm_matches_gate`
4634    /// equivalence pins and by the cross-arm
4635    /// `validate_upgrade_from_per_entry_arm_fires_before_versao_arm` /
4636    /// `_versao_arm_fires_before_behavior_arm` ordering pins).
4637    ///
4638    /// Prior to this lift the three [`crate::upgrade`] top-level validators
4639    /// lived only open-coded at the layout wire-up site
4640    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4641    /// each threaded through the same `self.upgrade_from()` slice and each
4642    /// paired with the same [`crate::LayoutError::UpgradeViolation`]-wrap
4643    /// envelope: every future consumer that wanted to gate `:upgrade-from`
4644    /// as a whole — the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
4645    /// materializer's per-CR admission webhook re-checking `:upgrade-from`
4646    /// after a per-`(:from … :instructions …)` patch, a future `feira
4647    /// validate --upgrade` per-caixa admission verb, a per-`:upgrade-from`
4648    /// overlay resolver a per-cluster overlay lift would materialize —
4649    /// was structurally forced to either re-inline the three-dispatch
4650    /// cascade in lockstep with the layout wire-up (the duplication the
4651    /// PRIME DIRECTIVE names as a bug) or call the whole
4652    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4653    /// peer per-Caixa gate to re-check one slot. Post-fold each such
4654    /// consumer reaches the three-arm compound gate through one call on
4655    /// the substrate primitive.
4656    ///
4657    /// The three arms together name one contract with three axes:
4658    ///
4659    ///   - **per-entry + cross-entry graph-edge invariant** — every entry's
4660    ///     `:from` parses as SemVer-2 and every per-instruction / within-
4661    ///     entry ordering / singularity gate on each entry's
4662    ///     `:instructions` list passes, and no two entries share the same
4663    ///     parsed `:from` (the wasm-operator's OTP appup
4664    ///     `release_handler:install_release/1` analog picks at most one
4665    ///     matching block per running version — two entries with the same
4666    ///     parsed semver are an ambiguous edge in the typed upgrade graph).
4667    ///   - **cross-slot reachability invariant** — every entry's `:from`
4668    ///     is strictly less than the caixa's own `:versao` under SemVer-2
4669    ///     precedence. An entry whose `:from >= :versao` is structurally
4670    ///     unreachable by the operator's `:from`-match dispatch (the
4671    ///     operator loads the current `:versao` and matches the *running*
4672    ///     version against each entry's `:from`; an entry whose `:from >=
4673    ///     :versao` is never reached because the operator never runs a
4674    ///     version >= the current one that it could then upgrade *to* the
4675    ///     current one).
4676    ///   - **cross-slot composition invariant** — every entry carrying a
4677    ///     `(:state-change …)` instruction has a `:behavior
4678    ///     :on-state-change` callback declared on the same caixa. The
4679    ///     per-version migration script is the `gen_server:code_change/3`
4680    ///     analog and the runtime hook it is delivered through during hot
4681    ///     upgrade is the `:on-state-change` callback (the upgrade.rs
4682    ///     module doc pins the composition verbatim: "Composes with the
4683    ///     `:behavior :on-state-change` callback to deliver state migration
4684    ///     during hot upgrades").
4685    ///
4686    /// All three axes must hold together — every consumer's
4687    /// `:upgrade-from` accept-set past this compound gate is the same
4688    /// set the `feira build` author-time gate admits.
4689    ///
4690    /// The per-slot compound entry gate discipline lifted here onto the
4691    /// M2 `:upgrade-from` axis is the sibling of the peer per-kind
4692    /// compound entry gates ([`crate::render::require_supervisor_view`]
4693    /// / [`crate::render::require_aplicacao_view`] /
4694    /// [`crate::render::require_v0_servico_shape`]) that fold every
4695    /// per-kind cascade at the per-kind altitude, and of the peer
4696    /// per-slot compound gates ([`crate::AplicacaoSpec::validate_contratos`],
4697    /// [`crate::MeshPolicy::validate`],
4698    /// [`crate::SupervisorSpec::validate_children`]) that fold every
4699    /// structural axis on their slot onto one substrate primitive.
4700    /// Extended here to the last unlifted compound-cascade wire-up at
4701    /// the layout-pipeline altitude — the three-dispatch M2
4702    /// `:upgrade-from` cascade that lived only open-coded at the layout
4703    /// wire-up site.
4704    ///
4705    /// The per-instruction script-path on-disk existence-probe walk that
4706    /// [`crate::layout::StandardLayout::verify`] runs immediately after
4707    /// this gate (which resolves each entry's `:instructions
4708    /// (:state-change :script)` against the layout root) stays open-coded
4709    /// at the layout wire-up site — that arm needs the filesystem oracle
4710    /// on the [`crate::LayoutInvariants`] trait, not the pure per-Caixa
4711    /// typed-shape surface this compound gate folds. Same posture the
4712    /// peer [`Self::validate_code_paths`] takes on the sibling code-path
4713    /// axes: the typed-shape gate fires on the per-Caixa surface, the
4714    /// on-disk existence check fires on the [`crate::StandardLayout`]
4715    /// surface.
4716    ///
4717    /// # Errors
4718    ///
4719    /// Returns [`crate::UpgradeError::FromInvalid`] /
4720    /// [`crate::UpgradeError::ModuleEmpty`] /
4721    /// [`crate::UpgradeError::ModuleInvalid`] /
4722    /// [`crate::UpgradeError::EmptyScript`] /
4723    /// [`crate::UpgradeError::AbsoluteScript`] /
4724    /// [`crate::UpgradeError::ParentEscapeScript`] /
4725    /// [`crate::UpgradeError::NonLispExtensionScript`] /
4726    /// [`crate::UpgradeError::RestartNotExclusive`] /
4727    /// [`crate::UpgradeError::StateChangeWithoutPriorLoad`] /
4728    /// [`crate::UpgradeError::PurgeWithoutPriorLoad`] /
4729    /// [`crate::UpgradeError::StateChangeAfterCleanup`] /
4730    /// [`crate::UpgradeError::DuplicateLoadModule`] /
4731    /// [`crate::UpgradeError::DuplicateStateChange`] /
4732    /// [`crate::UpgradeError::DuplicateCleanup`] /
4733    /// [`crate::UpgradeError::DuplicateFrom`] on the per-entry +
4734    /// cross-entry axis; [`crate::UpgradeError::FromNotBeforeVersao`] on
4735    /// the cross-slot `:from ↔ :versao` axis;
4736    /// [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
4737    /// on the cross-slot `:state-change ↔ :on-state-change` axis.
4738    pub fn validate_upgrade_from(&self) -> Result<(), crate::UpgradeError> {
4739        crate::upgrade::validate_upgrade_from(self.upgrade_from())?;
4740        crate::upgrade::validate_upgrade_from_against_versao(self.upgrade_from(), self.versao())?;
4741        crate::upgrade::validate_upgrade_from_against_behavior(
4742            self.upgrade_from(),
4743            self.behavior(),
4744        )?;
4745        Ok(())
4746    }
4747
4748    /// Compound per-`Caixa` entry gate on the M2 `:limits` slot — folds
4749    /// the [`crate::LimitsSpec::validate`] four-axis cascade (`:memory`
4750    /// wasm32 zero-floor / below-page / above-cap / non-page-multiple;
4751    /// `:fuel` zero-floor / cap; `:wall-clock` zero-floor / cap; `:cpu`
4752    /// zero-floor / cap) onto one substrate primitive on [`Caixa`]. The
4753    /// `#[serde(default)]` absent-slot arm (`limits: None`, the
4754    /// canonical "no bound declared — engine-default applies" author
4755    /// shape [`crate::LimitsSpec::is_empty`]'s per-axis `None` cascade
4756    /// reads) is the fold's identity element and passes trivially; the
4757    /// present-slot arm (`limits: Some(l)`) dispatches to
4758    /// [`crate::LimitsSpec::validate`] verbatim, threading its per-axis
4759    /// [`crate::LimitsError`] Display through untouched.
4760    ///
4761    /// Prior to this lift the M2 `:limits` slot lived only wired
4762    /// open-coded at the layout wire-up site
4763    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4764    /// through the `if let Some(l) = caixa.limits() { l.validate() … }`
4765    /// three-line `Option::None → Ok(()) | Some(_) → …` unwrap-and-
4766    /// dispatch pattern paired with the same
4767    /// [`crate::LayoutError::LimitsViolation`]-wrap envelope: every
4768    /// future consumer that wanted to gate `:limits` as a whole — the
4769    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4770    /// per-CR admission webhook re-checking `:limits` after a per-
4771    /// `{:memory, :fuel, :wall-clock, :cpu}` patch (the exact case the
4772    /// [`Self::limits`] accessor docstring names as the second
4773    /// consumer of the slot), a future `feira validate --limits` per-
4774    /// caixa admission verb, a per-`:limits` overlay resolver a per-
4775    /// cluster `:limits-overrides` overlay lift would materialize — was
4776    /// structurally forced to either re-inline the two-line
4777    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4778    /// pattern in lockstep with the layout wire-up (the duplication the
4779    /// PRIME DIRECTIVE names as a bug) or call the whole
4780    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4781    /// peer per-Caixa gate ([`Self::validate_nome`],
4782    /// [`Self::validate_versao`], [`Self::validate_deps`],
4783    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4784    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4785    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4786    /// [`Self::validate_upgrade_from`], [`Self::validate_code_paths`],
4787    /// plus the per-kind `require_supervisor_view` /
4788    /// `require_aplicacao_view` gates, plus the on-disk existence
4789    /// walks) to re-check one slot. Post-lift each such consumer
4790    /// reaches the [`crate::LimitsSpec::validate`] four-axis cascade
4791    /// (and its identity-element on the absent slot) through one call
4792    /// on the substrate primitive.
4793    ///
4794    /// The per-slot compound entry-gate discipline lifted here onto the
4795    /// M2 `:limits` axis is the sibling of the peer per-slot compound
4796    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4797    /// [`crate::MeshPolicy::validate`],
4798    /// [`crate::SupervisorSpec::validate_children`],
4799    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`]) that
4800    /// fold every structural + cross-slot axis on their slot onto one
4801    /// substrate primitive. Extended here to the M2 `:limits` slot, the
4802    /// first of the two M2 typed slots (`:limits`, `:behavior`) whose
4803    /// per-Caixa compound-gate wire-up still lived open-coded at the
4804    /// layout altitude after the [`Self::validate_upgrade_from`] lift
4805    /// (d6801df) closed the sibling M2 slot's cascade.
4806    ///
4807    /// # Errors
4808    ///
4809    /// Returns every [`crate::LimitsError`] variant on the present-slot
4810    /// arm — verbatim from [`crate::LimitsSpec::validate`]. Passes
4811    /// trivially on the absent-slot arm (`limits: None`, the fold's
4812    /// identity element).
4813    pub fn validate_limits(&self) -> Result<(), crate::LimitsError> {
4814        match self.limits() {
4815            Some(l) => l.validate(),
4816            None => Ok(()),
4817        }
4818    }
4819
4820    /// Compound per-`Caixa` entry gate on the M2 `:behavior` slot's
4821    /// pure typed-shape surface — folds the
4822    /// [`crate::BehaviorSpec::validate`] six-slot value-shape cascade
4823    /// (each declared `:on-init` / `:on-call` / `:on-cast` / `:on-info`
4824    /// / `:on-state-change` / `:on-terminate` callback-path is
4825    /// non-empty / relative / no-`..`-parent-escape / terminating-
4826    /// `.lisp`-extension, routed through the shared
4827    /// [`crate::render::require_sandboxed_lisp_path`] arm-set) onto one
4828    /// substrate primitive on [`Caixa`]. The `#[serde(default)]`
4829    /// absent-slot arm (`behavior: None`, the canonical "no callback
4830    /// declared — the runtime falls back to the wasm-engine's default
4831    /// callback per arm" author shape [`crate::BehaviorSpec::is_empty`]'s
4832    /// per-slot `None` cascade reads) is the fold's identity element
4833    /// and passes trivially; the present-slot arm (`behavior: Some(b)`)
4834    /// dispatches to [`crate::BehaviorSpec::validate`] verbatim,
4835    /// threading its per-slot [`crate::BehaviorError`] Display through
4836    /// untouched.
4837    ///
4838    /// Scope note — the on-disk callback-path existence walk paired
4839    /// with the value-shape gate at
4840    /// [`crate::layout::StandardLayout::verify`] stays open-coded at
4841    /// the layout altitude, because it needs the
4842    /// [`crate::layout::LayoutInvariants`] filesystem oracle
4843    /// ([`crate::layout::LayoutInvariants::exists`]) that the pure
4844    /// per-Caixa typed-shape surface this compound gate folds onto has
4845    /// no reference to. Same posture the peer M2 `:upgrade-from`
4846    /// per-Caixa compound gate ([`Self::validate_upgrade_from`]
4847    /// d6801df) already carries: the pure typed-shape surface folds
4848    /// onto the substrate primitive; the per-instruction script-path
4849    /// existence probe on the paired axis (there `:state-change
4850    /// :script`; here `:on-*`) stays at the layout altitude.
4851    ///
4852    /// Prior to this lift the pure value-shape surface of the M2
4853    /// `:behavior` slot lived only wired open-coded at the layout
4854    /// wire-up site ([`crate::layout::StandardLayout::verify`],
4855    /// caixa-core/src/layout.rs), through the
4856    /// `if let Some(b) = caixa.behavior() { b.validate() … }`
4857    /// unwrap-and-dispatch pattern paired with the same
4858    /// [`crate::LayoutError::BehaviorViolation`]-wrap envelope: every
4859    /// future consumer that wanted to gate the `:behavior` slot's
4860    /// value-shape as a whole — the deferred
4861    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4862    /// admission webhook re-checking `:behavior` after a per-`{:on-init,
4863    /// :on-call, :on-cast, :on-info, :on-state-change, :on-terminate}`
4864    /// patch (the exact case the peer `:on-*` accessor docstrings on
4865    /// [`crate::BehaviorSpec`] already name as deferred consumers of
4866    /// the slot), a future `feira validate --behavior` per-caixa
4867    /// admission verb, a per-`:behavior` overlay resolver a future
4868    /// per-cluster callback-overlay lift would materialize — was
4869    /// structurally forced to either re-inline the two-line
4870    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4871    /// pattern in lockstep with the layout wire-up (the duplication the
4872    /// PRIME DIRECTIVE names as a bug) or call the whole
4873    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4874    /// peer per-Caixa gate ([`Self::validate_nome`],
4875    /// [`Self::validate_versao`], [`Self::validate_deps`],
4876    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4877    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4878    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4879    /// [`Self::validate_limits`], [`Self::validate_upgrade_from`],
4880    /// [`Self::validate_code_paths`], plus the per-kind
4881    /// `require_supervisor_view` / `require_aplicacao_view` gates, plus
4882    /// the on-disk existence walks) to re-check one slot. Post-lift
4883    /// each such consumer reaches the [`crate::BehaviorSpec::validate`]
4884    /// six-slot cascade (and its identity-element on the absent slot)
4885    /// through one call on the substrate primitive.
4886    ///
4887    /// The per-slot compound entry-gate discipline lifted here onto the
4888    /// M2 `:behavior` axis is the sibling of the peer per-slot compound
4889    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4890    /// [`crate::MeshPolicy::validate`],
4891    /// [`crate::SupervisorSpec::validate_children`],
4892    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`],
4893    /// [`Self::validate_limits`]) that fold every structural + cross-
4894    /// slot axis on their slot onto one substrate primitive. Extended
4895    /// here to the M2 `:behavior` slot, the last of the four M2 typed
4896    /// slots (`:limits`, `:behavior`, `:upgrade-from`, plus the
4897    /// supervisor-only `:children` peer) whose per-Caixa compound-gate
4898    /// wire-up still lived open-coded at the layout altitude after the
4899    /// [`Self::validate_limits`] lift (baa4688) closed the sibling M2
4900    /// `:limits` slot's cascade. With this lift the "one named per-slot
4901    /// / per-Caixa compound gate per typed slot folding every structural
4902    /// axis on that slot (plus the `Option::None` identity element for
4903    /// the `Option`-shaped slots) onto one substrate primitive"
4904    /// discipline spans every M2 typed slot uniformly, so a reader who
4905    /// has learned any peer M2 gate reads `:behavior` without a per-
4906    /// slot exception carve-out.
4907    ///
4908    /// # Errors
4909    ///
4910    /// Returns every [`crate::BehaviorError`] variant on the present-
4911    /// slot arm — verbatim from [`crate::BehaviorSpec::validate`].
4912    /// Passes trivially on the absent-slot arm (`behavior: None`, the
4913    /// fold's identity element).
4914    pub fn validate_behavior(&self) -> Result<(), crate::BehaviorError> {
4915        match self.behavior() {
4916            Some(b) => b.validate(),
4917            None => Ok(()),
4918        }
4919    }
4920
4921    /// Reject `:restart-window` values the shared
4922    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4923    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4924    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4925    /// `Option<Duration>` routed through the shared codec via `with =
4926    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4927    /// view-construction path ([`Self::supervisor_view`]) folds the
4928    /// raw string through the same shared codec and soft-swallows the
4929    /// parse error as `None` to keep the view best-effort. Without
4930    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4931    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4932    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4933    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4934    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4935    /// edge case) silently produced a `SupervisorSpec` with
4936    /// `restart_window: None`, indistinguishable from the canonical
4937    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4938    /// `MaxIntensity / Period` invariant turns into a never-reset
4939    /// supervisor far from the source `caixa.lisp`, with no field
4940    /// naming the offending `:restart-window`. Lifting the gate to a
4941    /// Caixa-level validator mirrors the trajectory of the peer
4942    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4943    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4944    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4945    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4946    ///
4947    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4948    /// (the shared codec backing `:supervisor :restart-window` as
4949    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4950    /// `:politicas :circuit-breaker :window` — all three covered by
4951    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4952    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4953    /// variant, carrying the offending raw string + a parser-shaped
4954    /// reason naming the canonical authoring form, so the diagnostic
4955    /// is self-locating (the author can grep their `caixa.lisp` for
4956    /// `:restart-window "<value>"` and fix it in one edit) and
4957    /// uniform with every other manifest-level validate diagnostic.
4958    /// With this gate the four `:restart-window`-shaped surfaces (the
4959    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4960    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4961    /// now structurally equivalent — every value past the codec is in
4962    /// one accepted set, by construction.
4963    ///
4964    /// `None` (the canonical "omit the slot to express no reset"
4965    /// shape) is accepted trivially — the gate is a no-op when the
4966    /// author didn't author a window. The empty string is rejected by
4967    /// the shared codec (its digit-only gate refuses an empty
4968    /// magnitude), surfacing the same `RestartWindowMalformed`
4969    /// diagnostic as every other rejected non-canonical shape.
4970    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4971        let Some(s) = self.restart_window() else {
4972            return Ok(());
4973        };
4974        crate::supervisor::duration_codec::parse(s)
4975            .map(|_| ())
4976            .map_err(|reason| ManifestError::RestartWindowMalformed {
4977                restart_window: s.to_string(),
4978                reason,
4979            })
4980    }
4981
4982    /// Compound per-`Caixa` entry gate on the Aplicacao-kind mesh-slot
4983    /// family — folds the paired [`crate::AplicacaoSpec::validate`]
4984    /// typed-shape cascade (per-slot gates on `:membros`, `:contratos`,
4985    /// `:entrada`, `:placement`, `:politicas`, in that declared order)
4986    /// plus the cross-slot self-edge gate
4987    /// ([`crate::aplicacao::validate_no_self_membership`], the
4988    /// `:membros :caixa` ≠ `:nome` invariant the typed view cannot
4989    /// enforce on its own because it carries the membros but not the
4990    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4991    /// non-Aplicacao kinds the fold is the identity element — the paired
4992    /// [`Self::aplicacao_view`] accessor returns `None` off the
4993    /// Aplicacao arm (peer with the [`Self::validate_limits`] /
4994    /// [`Self::validate_behavior`] M2 `Option`-arm identity element),
4995    /// so the gate passes trivially without touching the mesh slots.
4996    ///
4997    /// Prior to this lift the paired cascade lived only wired open-coded
4998    /// at the layout wire-up site
4999    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
5000    /// as the three-line `let view = caixa.aplicacao_view().expect(...);
5001    /// view.validate() … validate_no_self_membership(...) …` pattern
5002    /// paired with two `.map_err(|err| LayoutError::AplicacaoViolation
5003    /// { caixa, issue })` wraps — every future consumer that wanted to
5004    /// gate the Aplicacao-shape cascade as a whole (the deferred
5005    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
5006    /// admission webhook re-checking `:membros` / `:contratos` after a
5007    /// per-slot patch, a future `feira validate --aplicacao` per-caixa
5008    /// admission verb, a per-Aplicacao overlay resolver) was structurally
5009    /// forced to either re-inline the two-dispatch cascade in lockstep
5010    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5011    /// names as a bug) or call the whole
5012    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5013    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5014    /// such consumer reaches the two-arm compound gate through one call
5015    /// on the substrate primitive.
5016    ///
5017    /// Peer to the [`crate::render::require_aplicacao_view`] compound
5018    /// entry gate every per-Aplicacao *renderer* routes through
5019    /// (3aefefb folded `validate_no_self_membership` onto the renderer
5020    /// path) — this gate mirrors the same fold on the *layout* path, so
5021    /// the two consumers of the Aplicacao-shape cascade (the author-time
5022    /// gate and every per-Aplicacao renderer) share one substrate
5023    /// primitive rather than two open-coded cascades kept in lockstep.
5024    /// Same lift discipline the peer per-slot compound gates
5025    /// ([`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5026    /// b5dd55e, [`Self::validate_limits`] baa4688,
5027    /// [`Self::validate_behavior`] 0d2877a) each carry.
5028    ///
5029    /// # Errors
5030    ///
5031    /// Returns every [`crate::AplicacaoError`] variant on the present-
5032    /// kind arm — the typed-shape cascade's per-slot arms first
5033    /// (matching [`crate::AplicacaoSpec::validate`]'s declared order),
5034    /// then the cross-slot self-edge arm
5035    /// ([`crate::AplicacaoError::MembroIsSelfAplicacao`]). Passes
5036    /// trivially on non-Aplicacao kinds (the fold's identity element).
5037    pub fn validate_aplicacao_shape(&self) -> Result<(), crate::AplicacaoError> {
5038        let Some(view) = self.aplicacao_view() else {
5039            return Ok(());
5040        };
5041        view.validate()?;
5042        crate::aplicacao::validate_no_self_membership(self.membros(), self.nome())?;
5043        Ok(())
5044    }
5045
5046    /// Compound per-`Caixa` entry gate on the Supervisor-kind
5047    /// supervision-tree slot family — folds the paired
5048    /// [`crate::SupervisorSpec::validate`] typed-shape cascade
5049    /// (`:estrategia` ↔ `:children` invariants, `:max-restarts` /
5050    /// `:restart-window` bounds, per-child DNS-1123 `:caixa` names,
5051    /// semver-valid `:versao` constraints, the set-not-multiset
5052    /// duplicate-child gate) plus the cross-slot self-edge gate
5053    /// ([`crate::supervisor::validate_no_self_supervision`], the
5054    /// `:children :caixa` ≠ `:nome` invariant the typed view cannot
5055    /// enforce on its own because it carries the children but not the
5056    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
5057    /// non-Supervisor kinds the fold is the identity element — the paired
5058    /// [`Self::supervisor_view`] accessor returns `None` off the
5059    /// Supervisor arm (peer with the [`Self::validate_limits`] /
5060    /// [`Self::validate_behavior`] M2 `Option`-arm identity element and
5061    /// the sibling per-Aplicacao [`Self::validate_aplicacao_shape`]),
5062    /// so the gate passes trivially without touching the supervision-tree
5063    /// slots.
5064    ///
5065    /// Prior to this lift the paired cascade lived only wired open-coded
5066    /// at the layout wire-up site
5067    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
5068    /// as the three-line `let view = caixa.supervisor_view().expect(...);
5069    /// view.validate() … validate_no_self_supervision(...) …` pattern
5070    /// paired with two `.map_err(|err| LayoutError::SupervisorViolation
5071    /// { caixa, issue })` wraps — every future consumer that wanted to
5072    /// gate the Supervisor-shape cascade as a whole (the wasm-operator's
5073    /// hierarchical reconciliation scheduler re-checking `:children` /
5074    /// `:estrategia` after a per-slot patch, the M4
5075    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
5076    /// webhook, a future `feira validate --supervisor` per-caixa
5077    /// admission verb, a per-Supervisor overlay resolver) was structurally
5078    /// forced to either re-inline the two-dispatch cascade in lockstep
5079    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5080    /// names as a bug) or call the whole
5081    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5082    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5083    /// such consumer reaches the two-arm compound gate through one call
5084    /// on the substrate primitive.
5085    ///
5086    /// Peer to the [`crate::render::require_supervisor_view`] compound
5087    /// entry gate every per-Supervisor *renderer* would route through
5088    /// (which already folds the same `spec.validate()` +
5089    /// `validate_no_self_supervision` two-arm cascade behind its
5090    /// `require_kind` + `validate_restart_window` prelude) — this gate
5091    /// mirrors the same fold on the *layout* path, so the two consumers
5092    /// of the Supervisor-shape cascade (the author-time gate and every
5093    /// per-Supervisor renderer) share one substrate primitive rather
5094    /// than two open-coded cascades kept in lockstep. Same lift
5095    /// discipline the peer per-slot compound gates
5096    /// ([`Self::validate_aplicacao_shape`] 949a7a0,
5097    /// [`Self::validate_upgrade_from`] d6801df,
5098    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5099    /// baa4688, [`Self::validate_behavior`] 0d2877a) each carry.
5100    ///
5101    /// # Errors
5102    ///
5103    /// Returns every [`crate::SupervisorError`] variant on the present-
5104    /// kind arm — the typed-shape cascade's per-slot arms first
5105    /// (matching [`crate::SupervisorSpec::validate`]'s declared order),
5106    /// then the cross-slot self-edge arm
5107    /// ([`crate::SupervisorError::ChildSupervisesSelf`]). Passes
5108    /// trivially on non-Supervisor kinds (the fold's identity element).
5109    pub fn validate_supervisor_shape(&self) -> Result<(), crate::SupervisorError> {
5110        let Some(view) = self.supervisor_view() else {
5111            return Ok(());
5112        };
5113        view.validate()?;
5114        crate::supervisor::validate_no_self_supervision(self.children(), self.nome())?;
5115        Ok(())
5116    }
5117
5118    /// Compound per-`Caixa` entry gate on the Acao-kind `:ci` slot
5119    /// family — folds the [`crate::decompose_ci`] typed decompose gate
5120    /// (`canteiro_types::decompose` refusing every illegal
5121    /// [`canteiro_types::CiRun`] shape: duplicate node name, dependency
5122    /// on an undeclared node, dependency cycle) onto one substrate
5123    /// primitive on [`Caixa`]. On non-`Acao` kinds the fold is the
5124    /// identity element — the paired [`Self::kind`] `is_acao()` guard
5125    /// short-circuits before the decompose gate ever fires (peer with
5126    /// the [`Self::validate_aplicacao_shape`] /
5127    /// [`Self::validate_supervisor_shape`] typed-view identity element
5128    /// and the [`Self::validate_limits`] / [`Self::validate_behavior`]
5129    /// M2 `Option`-arm identity element), so the gate passes trivially
5130    /// without touching the `:ci` slot. An `:kind Acao` caixa with
5131    /// `ci = None` is also an identity-element pass: the presence gate
5132    /// is the sibling axis owned by [`crate::LayoutError::MissingCi`] /
5133    /// [`crate::require_ci`] / [`crate::MissingCiSlot`], not by the
5134    /// decompose gate — a caixa that carries no `:ci` slot has no run
5135    /// to decompose. Same split the peer per-Servico
5136    /// [`crate::LayoutError::ServicoWithoutServicos`] presence gate and
5137    /// per-Binario [`crate::LayoutError::BinarioWithoutExe`] presence
5138    /// gate keep from their sibling per-slot shape gates, so the two
5139    /// axes stay separately diagnosable at the layout altitude.
5140    ///
5141    /// Prior to this lift the decompose gate lived only wired
5142    /// open-coded at the [`caixa_actions::validate`] renderer-side
5143    /// entry gate (routed through the substrate-canonical
5144    /// [`crate::require_acao_view`] compound helper) — the *layout*
5145    /// pipeline ([`crate::layout::StandardLayout::verify`], caixa-core/
5146    /// src/layout.rs) only checked `:ci` *presence* via
5147    /// [`crate::LayoutError::MissingCi`], so a `:kind Acao` caixa
5148    /// carrying a structurally illegal `:ci` (a duplicate node name, a
5149    /// dependency on an undeclared node, a dependency cycle) passed
5150    /// `feira build` cleanly and surfaced the diagnostic only when
5151    /// [`caixa_actions::validate`] later refused it — far from the
5152    /// source `caixa.lisp` on the author-time gate side. Every future
5153    /// consumer that wanted to gate the Acao-shape cascade as a whole
5154    /// (a per-`Acao` CR materializer's admission webhook re-checking
5155    /// `:ci` after a per-node patch, a future `feira validate --acao`
5156    /// per-caixa admission verb, a per-`Acao` overlay resolver
5157    /// rejecting an added / renamed node against a cluster-local
5158    /// snapshot) was structurally forced to either re-inline the
5159    /// decompose dispatch in lockstep with the renderer-side wire-up
5160    /// (the duplication the PRIME DIRECTIVE names as a bug) or call
5161    /// the whole [`caixa_actions::validate`] renderer and pay the
5162    /// per-node accumulation to re-check one slot. Post-fold each such
5163    /// consumer reaches the decompose gate through one call on the
5164    /// substrate primitive.
5165    ///
5166    /// Peer to the [`crate::require_acao_view`] compound entry gate
5167    /// every per-`Acao` *renderer* routes through (which already folds
5168    /// the same `require_ci + decompose_ci` two-arm cascade behind its
5169    /// `require_kind` prelude) — this gate mirrors the same fold on
5170    /// the *layout* path, so the two consumers of the Acao-shape
5171    /// cascade (the author-time gate and every per-`Acao` renderer)
5172    /// share one substrate primitive rather than two open-coded
5173    /// cascades kept in lockstep. Same lift discipline the peer
5174    /// per-kind compound gates ([`Self::validate_aplicacao_shape`]
5175    /// 949a7a0, [`Self::validate_supervisor_shape`] 4c70105,
5176    /// [`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5177    /// b5dd55e, [`Self::validate_limits`] baa4688,
5178    /// [`Self::validate_behavior`] 0d2877a) each carry. Closes the
5179    /// last per-kind asymmetry: with this lift the four typed
5180    /// named-caixa kinds (`Servico` / `Aplicacao` / `Supervisor` /
5181    /// `Acao`) each carry a compound per-`Caixa` shape gate on the
5182    /// substrate, and the layout pipeline routes through the same one
5183    /// substrate primitive per kind rather than four open-coded
5184    /// cascades.
5185    ///
5186    /// # Errors
5187    ///
5188    /// Returns the [`crate::CiDecomposeFailure`] typed view on the
5189    /// present-slot arm — the caixa's `:nome` alongside the borrowed
5190    /// [`canteiro_types::DecomposeError`] source (`DuplicateNode` /
5191    /// `UnknownDep` / `Cycle`) verbatim, so a consumer that fans on
5192    /// the specific arm reaches for `err.source` directly rather than
5193    /// re-parsing the Display bytes. Passes trivially on non-`Acao`
5194    /// kinds and on `:kind Acao` caixas with absent `:ci` (the fold's
5195    /// two identity-element arms).
5196    pub fn validate_acao_shape(&self) -> Result<(), crate::CiDecomposeFailure> {
5197        if !self.kind().is_acao() {
5198            return Ok(());
5199        }
5200        let Some(ci) = self.ci() else {
5201            return Ok(());
5202        };
5203        crate::render::decompose_ci(self, ci).map(|_| ())
5204    }
5205
5206    /// Compound per-`Caixa` kind ↔ typed-slot coherence gate on the
5207    /// three "declared but ignored" typed-slot families — M3 mesh
5208    /// (`:membros` / `:contratos` / `:politicas` / `:placement` /
5209    /// `:entrada`, owned by `:kind Aplicacao`, MESH-COMPOSITION §III.1),
5210    /// supervisor-tree (`:estrategia` / `:max-restarts` /
5211    /// `:restart-window` / `:children`, owned by `:kind Supervisor`,
5212    /// INSPIRATIONS §II.2), and M2 Servico-runtime (`:limits` /
5213    /// `:behavior` / `:upgrade-from`, owned by `:kind Servico`,
5214    /// INSPIRATIONS §III.1 / §II.3 / §II.4). Folds the three sibling
5215    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5216    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5217    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
5218    /// gates — each pre-lift a self-similar five-line
5219    /// `if !caixa.kind().is_<owner>() { let slots = caixa.declared_
5220    /// <family>_slots(); if !slots.is_empty() { return
5221    /// Err(LayoutError::<family>_on_non_<owner>(caixa, slots)); } }`
5222    /// block at [`crate::layout::StandardLayout::verify`] — onto one
5223    /// substrate primitive on [`Caixa`]. Every arm passes as an
5224    /// identity element on the owner kind (the paired
5225    /// [`Self::kind`] `is_<owner>()` guard short-circuits before the
5226    /// per-family `declared_*_slots` gate fires) and on non-owner
5227    /// kinds carrying no declared slot in that family (the
5228    /// [`Vec::is_empty`] check short-circuits before the wrap fires),
5229    /// so a bare no-code caixa on any kind passes the fold trivially
5230    /// on all three arms.
5231    ///
5232    /// Prior to this lift the three-arm cascade lived only wired
5233    /// open-coded at the layout wire-up site
5234    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/
5235    /// layout.rs) as three self-similar five-line blocks paired with
5236    /// three [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5237    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5238    /// [`crate::LayoutError::servico_slots_on_non_servico`] ctor
5239    /// dispatches (each of which the peer
5240    /// [`crate::layout::layout_slot_kind_ctors!`] macro already folds
5241    /// onto one substrate primitive per typed variant, 0419438) —
5242    /// every future consumer that wanted to gate the whole
5243    /// kind-coherence cascade as a unit (the deferred
5244    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5245    /// webhook re-checking every typed-slot family after a per-slot
5246    /// patch, a future `feira validate --kind-coherence` per-caixa
5247    /// admission verb, a per-`Caixa` overlay resolver rejecting a
5248    /// kind-foreign patch against a cluster-local snapshot) was
5249    /// structurally forced to either re-inline the three-block
5250    /// cascade in lockstep with the layout wire-up (the duplication
5251    /// the PRIME DIRECTIVE names as a bug) or call the whole
5252    /// [`crate::layout::StandardLayout::verify`] pipeline and pay
5253    /// every peer per-`Caixa` gate to re-check three slot families.
5254    /// Post-fold each such consumer reaches the three-arm cascade
5255    /// through one call on the substrate primitive.
5256    ///
5257    /// Diagnostic order matches the pre-fold layout wire-up
5258    /// canonical sequence — mesh → supervisor → servico — pinned by
5259    /// the load-bearing
5260    /// `validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm`
5261    /// / `_supervisor_arm_fires_before_servico_arm` ordering pins
5262    /// below. The three arms enumerate every typed-slot family the
5263    /// substrate carries whose "declared but ignored" footgun is
5264    /// gated at the layout altitude by a `{ caixa, kind, slots }`
5265    /// wrap variant — the peer
5266    /// [`crate::LayoutError::ForeignCodeSlot`] gate on the
5267    /// code-surface family sits outside this fold because
5268    /// [`Self::declared_foreign_code_slots`] bakes the kind-check
5269    /// into the helper (so the layout wire-up carries no outer
5270    /// `if !caixa.kind().is_<owner>()` guard), and the peer
5271    /// [`crate::LayoutError::CiOnNonAcao`] gate on the `:ci` axis
5272    /// carries a distinct `{ caixa, kind }` wrap shape (no `slots`
5273    /// field — `:ci` is a single `Option` not a `Vec`-of-named-slots)
5274    /// and rides on its own peer substrate primitive
5275    /// [`Self::validate_ci_kind_coherence`] (the direct sibling to
5276    /// this fold on the `:ci` axis) — the two folds share the same
5277    /// altitude and diagnostic order at the layout wire-up site but
5278    /// keep their distinct envelope shapes, so no consumer of
5279    /// `CiOnNonAcao` sees a variant rename.
5280    ///
5281    /// Peer to the per-kind compound entry gates every substrate
5282    /// primitive on the M2/M3 typed-slot family already carries
5283    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5284    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5285    /// [`Self::validate_upgrade_from`] d6801df,
5286    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5287    /// [`Self::validate_supervisor_shape`] 4c70105,
5288    /// [`Self::validate_acao_shape`] 5d6df54): the author-time gate
5289    /// axis on the *per-slot* algebra now shares one substrate
5290    /// primitive per compound gate, and this lift closes the
5291    /// symmetric axis on the *cross-family* kind ↔ slot coherence
5292    /// algebra so the layout pipeline routes the three self-similar
5293    /// gates through one substrate primitive rather than three
5294    /// open-coded blocks. Every future kind that adds its own
5295    /// exclusive typed-slot family (an `Actor`-owned per-virtual-
5296    /// actor grain slot the M5 Orleans-inspired kind reaches
5297    /// through, a per-Aplicacao overlay slot the M4 CR materializer
5298    /// consults) folds onto this compound gate as one arm addition
5299    /// rather than a fourth open-coded block at the wire-up site.
5300    ///
5301    /// # Errors
5302    ///
5303    /// Returns the first [`crate::LayoutError`] variant surfacing
5304    /// under the canonical mesh → supervisor → servico order:
5305    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] on a non-
5306    /// Aplicacao caixa with a declared M3 mesh slot,
5307    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] on a
5308    /// non-Supervisor caixa with a declared supervisor-tree slot,
5309    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] on a
5310    /// non-Servico caixa with a declared M2 slot. Passes trivially
5311    /// on the owner kind of each family and on non-owner kinds
5312    /// carrying no declared slot in that family (the fold's identity
5313    /// element on both axes).
5314    pub fn validate_kind_slot_coherence(&self) -> Result<(), crate::LayoutError> {
5315        // Each of the three arms routes through the shared
5316        // [`Self::run_kind_owned_slot_family_gate`] substrate primitive
5317        // — the outer non-owner-kind guard + inner accumulator + inner
5318        // emptiness-guard + wrap arm shape now lands on one dispatch
5319        // per family rather than a four-line open-coded block in
5320        // lockstep across all three arms. Canonical mesh → supervisor
5321        // → servico order preserved (the primitive short-circuits
5322        // arm-by-arm; the outer `?;` cascade at this altitude threads
5323        // the first surfaced arm's error verbatim). Each of the three
5324        // ctors ([`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5325        // [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5326        // [`crate::LayoutError::servico_slots_on_non_servico`]) was
5327        // already lifted onto the substrate by the peer
5328        // [`crate::layout::layout_slot_kind_ctors!`] macro, so each arm
5329        // routes through the same substrate-canonical
5330        // `Self::<variant> { caixa, kind, slots }` wrap per arm as the
5331        // pre-lift open-coded blocks — byte-equal, pinned by the
5332        // paired `validate_kind_slot_coherence_folds_<family>_arm_matches_gate`
5333        // equivalence pins and the peer
5334        // `validate_kind_slot_coherence_{mesh,supervisor}_arm_fires_before_<next>_arm`
5335        // ordering pins.
5336        self.run_kind_owned_slot_family_gate(
5337            crate::CaixaKind::is_aplicacao,
5338            Caixa::declared_mesh_slots,
5339            crate::LayoutError::mesh_slots_on_non_aplicacao,
5340        )?;
5341        self.run_kind_owned_slot_family_gate(
5342            crate::CaixaKind::is_supervisor,
5343            Caixa::declared_supervisor_slots,
5344            crate::LayoutError::supervisor_slots_on_non_supervisor,
5345        )?;
5346        self.run_kind_owned_slot_family_gate(
5347            crate::CaixaKind::is_servico,
5348            Caixa::declared_servico_slots,
5349            crate::LayoutError::servico_slots_on_non_servico,
5350        )?;
5351        Ok(())
5352    }
5353
5354    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on
5355    /// the three no-code kinds — `Supervisor` (supervises other
5356    /// caixas, INSPIRATIONS §II.2), `Aplicacao` (composes Servicos,
5357    /// MESH-COMPOSITION §III.1), and `Acao` (owns a typed CI run,
5358    /// CANTEIRO §7.1-C). Each carries no code of its own, so
5359    /// declaring any of `:bibliotecas` / `:exe` / `:servicos`
5360    /// silently passes the layout's path-existence loops (the paths
5361    /// still resolve on disk) and then vanishes downstream — the
5362    /// per-kind renderers gate emission on
5363    /// [`crate::render::require_kind`] and only emit the code
5364    /// surface for its owning kind, so a declared code slot on a
5365    /// no-code kind is the manifest field's documented "ignored
5366    /// otherwise" footgun.
5367    ///
5368    /// Pre-lift each of the three arms lived as a self-similar
5369    /// `if !caixa.kind().is_<no-code-kind>() { … } else if has_code
5370    /// { return Err(LayoutError::<kind>_owns_code(caixa)); }` block
5371    /// at [`crate::layout::StandardLayout::verify`] — three
5372    /// consumers, three identical shapes. Every future consumer
5373    /// that wanted to gate the whole code-surface coherence cascade
5374    /// as a unit (the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
5375    /// materializer's admission webhook re-checking after a
5376    /// per-slot patch, a future `feira validate --no-code-kind`
5377    /// per-caixa admission verb, a per-`Caixa` overlay resolver
5378    /// rejecting a kind-foreign patch) was structurally forced to
5379    /// either re-inline the three-block cascade in lockstep with
5380    /// the layout wire-up (the duplication the PRIME DIRECTIVE
5381    /// names as a bug) or call the whole
5382    /// [`crate::layout::StandardLayout::verify`] pipeline. Post-fold
5383    /// each such consumer reaches the three-arm cascade through
5384    /// one call.
5385    ///
5386    /// Mirror of the sibling [`Self::validate_kind_slot_coherence`]
5387    /// fold (f0d286e) on the author-time typed-slot coherence axis:
5388    /// that gate closes the "non-owner kind declares owner-only
5389    /// typed slots" three-arm cascade on the M2 / supervisor-tree /
5390    /// M3 slot families; this gate closes the reciprocal
5391    /// "no-code kind declares code" three-arm cascade on the
5392    /// `:bibliotecas` / `:exe` / `:servicos` code surface. Together
5393    /// the two folds route every kind ↔ author-shape coherence
5394    /// diagnostic at the layout altitude through one substrate
5395    /// primitive per axis.
5396    ///
5397    /// The gate carries two identity elements:
5398    /// - **`has_code == false`** — any kind (including the three
5399    ///   no-code kinds) that declares no code passes the paired
5400    ///   `!has_code` short-circuit before every per-arm dispatch.
5401    /// - **Code-owning kinds** (`Biblioteca` owning
5402    ///   `:bibliotecas`, `Binario` owning `:exe`, `Servico` owning
5403    ///   `:servicos`) — the three no-code arm-firing predicates
5404    ///   short-circuit on every code-owning kind, so the gate
5405    ///   passes trivially regardless of what code they declare.
5406    ///   Foreign-code-slot violations on a code-owning kind (e.g.
5407    ///   `:kind Servico` declaring `:exe`) surface through the
5408    ///   sibling [`crate::LayoutError::ForeignCodeSlot`] gate on
5409    ///   [`Self::declared_foreign_code_slots`], not through this
5410    ///   gate.
5411    ///
5412    /// Unlike the sibling cross-family
5413    /// [`Self::validate_kind_slot_coherence`], the three arms of
5414    /// this fold are mutually exclusive by construction — `:kind`
5415    /// is a single-valued [`CaixaKind`] discriminator so at most
5416    /// one arm can fire per caixa — and no cross-arm ordering pin
5417    /// is meaningful (the pre-fold three-block cascade at the
5418    /// wire-up site was already unreachable past the first
5419    /// matching arm).
5420    ///
5421    /// Peer to the per-kind compound entry gates every substrate
5422    /// primitive on the M2/M3 typed-slot family already carries
5423    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5424    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5425    /// [`Self::validate_upgrade_from`] d6801df,
5426    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5427    /// [`Self::validate_supervisor_shape`] 4c70105,
5428    /// [`Self::validate_acao_shape`] 5d6df54,
5429    /// [`Self::validate_kind_slot_coherence`] f0d286e): the
5430    /// author-time gate axis on the *per-slot* and *cross-family
5431    /// typed-slot* algebras each share one substrate primitive per
5432    /// compound gate, and this lift closes the third axis on the
5433    /// *code-surface* algebra so the layout pipeline routes all
5434    /// three coherence axes through one substrate primitive rather
5435    /// than nine open-coded blocks. Every future no-code kind
5436    /// (an `Actor` virtual-actor arm the M5 Orleans-inspired kind
5437    /// reaches through if it lands as a no-code composer, a future
5438    /// `Namespace` grouping kind) folds onto this compound gate
5439    /// as one arm addition rather than a fourth open-coded block
5440    /// at the wire-up site.
5441    ///
5442    /// # Errors
5443    ///
5444    /// Returns the [`crate::LayoutError`] variant naming the
5445    /// offending no-code kind:
5446    /// [`crate::LayoutError::SupervisorOwnsCode`] on a `:kind
5447    /// Supervisor` caixa with any declared code,
5448    /// [`crate::LayoutError::AplicacaoOwnsCode`] on a `:kind
5449    /// Aplicacao` caixa with any declared code,
5450    /// [`crate::LayoutError::AcaoOwnsCode`] on a `:kind Acao` caixa
5451    /// with any declared code. Passes trivially on every kind with
5452    /// no declared code and on every code-owning kind regardless
5453    /// of declared code (the fold's two identity-element arms).
5454    pub fn validate_no_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5455        let has_code =
5456            !self.bibliotecas().is_empty() || !self.exe().is_empty() || !self.servicos().is_empty();
5457        if !has_code {
5458            return Ok(());
5459        }
5460        if self.kind().is_supervisor() {
5461            return Err(crate::LayoutError::supervisor_owns_code(self));
5462        }
5463        if self.kind().is_aplicacao() {
5464            return Err(crate::LayoutError::aplicacao_owns_code(self));
5465        }
5466        if self.kind().is_acao() {
5467            return Err(crate::LayoutError::acao_owns_code(self));
5468        }
5469        Ok(())
5470    }
5471
5472    /// Compound per-`Caixa` kind ↔ `:ci` coherence gate — the `Acao`
5473    /// axis-only companion to the sibling three-arm
5474    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) on the
5475    /// M3 mesh / supervisor-tree / M2 Servico-runtime typed-slot
5476    /// families. `:ci` carries a typed CI run
5477    /// ([`canteiro_types::CiRun`], CANTEIRO §7.1-C) that only the
5478    /// `caixa-actions` renderer decomposes + validates and only for a
5479    /// `:kind Acao`. On any *other* kind a declared `:ci` is the
5480    /// manifest field's documented "ignored otherwise" — it silently
5481    /// passes verify and then vanishes (never decomposed, never
5482    /// rendered), far from the source `caixa.lisp`.
5483    ///
5484    /// Pre-lift the arm lived as a self-similar
5485    /// `if caixa.ci().is_some() && !caixa.kind().is_acao() { return
5486    /// Err(LayoutError::CiOnNonAcao { caixa: caixa.nome().to_string(),
5487    /// kind: caixa.kind() }); }` block at
5488    /// [`crate::layout::StandardLayout::verify`] — one consumer today
5489    /// but every future consumer that wanted to gate the `:ci`
5490    /// coherence axis as a unit (the deferred
5491    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5492    /// webhook re-checking after a per-slot patch, a future
5493    /// `feira validate --ci-coherence` per-caixa admission verb, a
5494    /// per-`Caixa` overlay resolver rejecting a kind-foreign `:ci`
5495    /// patch) was structurally forced to either re-inline the
5496    /// two-condition guard in lockstep with the layout wire-up (the
5497    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5498    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5499    /// Post-fold each such consumer reaches the arm through one call.
5500    ///
5501    /// Peer of the sibling three-arm
5502    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5503    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5504    /// axes under a uniform `{ caixa, kind, slots }` envelope
5505    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5506    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5507    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]). The `:ci`
5508    /// axis stays on its own primitive because
5509    /// [`crate::LayoutError::CiOnNonAcao`] carries a distinct
5510    /// `{ caixa, kind }` wrap shape (no `slots` field — `:ci` is a
5511    /// single `Option` not a `Vec`-of-named-slots) whose reshape
5512    /// onto the sibling `{ caixa, kind, slots }` envelope would
5513    /// force a variant rename touching every consumer of
5514    /// `CiOnNonAcao`; the two folds share the same
5515    /// author-time-vs-renderer split and diagnostic altitude, and
5516    /// route through peer substrate primitives on the same
5517    /// [`Caixa`] surface.
5518    ///
5519    /// Peer to the per-kind compound entry gates every substrate
5520    /// primitive on the M2/M3 typed-slot family already carries
5521    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5522    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5523    /// [`Self::validate_upgrade_from`] d6801df,
5524    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5525    /// [`Self::validate_supervisor_shape`] 4c70105,
5526    /// [`Self::validate_acao_shape`] 5d6df54,
5527    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5528    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2): every
5529    /// author-time coherence axis on the typed [`Caixa`] surface now
5530    /// routes through one substrate primitive per axis rather than
5531    /// an open-coded block at the layout wire-up site.
5532    ///
5533    /// The gate carries two identity elements:
5534    /// - **`ci().is_none()`** — a caixa that declares no `:ci`
5535    ///   passes the first short-circuit before every per-arm
5536    ///   dispatch, on every kind. The canonical shape of the four
5537    ///   non-`Acao` kinds (`Biblioteca` / `Binario` / `Servico` /
5538    ///   `Supervisor` / `Aplicacao`) is `ci = None` — the arm
5539    ///   never fires on a well-shaped fixture.
5540    /// - **`:kind Acao`** — the owner-kind arm short-circuits on
5541    ///   every `Acao` caixa regardless of its `:ci` shape; a
5542    ///   malformed `:ci` on an `Acao` surfaces through the peer
5543    ///   [`Self::validate_acao_shape`] compound decompose gate
5544    ///   (5d6df54), not through this coherence gate.
5545    ///
5546    /// # Errors
5547    ///
5548    /// Returns [`crate::LayoutError::CiOnNonAcao`] naming the
5549    /// offending caixa's nome + kind on any non-`Acao` caixa with
5550    /// `:ci` declared. Passes trivially on every kind that declares
5551    /// no `:ci` and on every `:kind Acao` caixa regardless of
5552    /// declared `:ci` (the fold's two identity-element arms).
5553    pub fn validate_ci_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5554        if self.ci().is_some() && !self.kind().is_acao() {
5555            return Err(crate::LayoutError::CiOnNonAcao {
5556                caixa: self.nome().to_string(),
5557                kind: self.kind(),
5558            });
5559        }
5560        Ok(())
5561    }
5562
5563    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on the
5564    /// two exclusive code-surface slots — `:exe` (owned only by
5565    /// [`crate::CaixaKind::Binario`], the nix-built executable surface)
5566    /// and `:servicos` (owned only by [`crate::CaixaKind::Servico`],
5567    /// the wasm-component + `ComputeUnit` daemon surface). The
5568    /// `caixa-helm` / `caixa-flux` / `caixa-flake` renderers gate
5569    /// emission on [`crate::render::require_kind`]`(_, <owning-kind>)`
5570    /// and only emit the slot for its owning kind — so on any *other*
5571    /// code-running kind a declared `:exe` / `:servicos` is the
5572    /// manifest field's documented "ignored otherwise": the path is
5573    /// validated by the per-kind path-existence loops in
5574    /// [`crate::layout::StandardLayout::verify`], but the value is
5575    /// never rendered into a build target or programs.yaml entry —
5576    /// it silently passes `feira build` and then vanishes, far from
5577    /// the source `caixa.lisp`, with no field naming which slot is
5578    /// foreign.
5579    ///
5580    /// Pre-lift the arm lived as a self-similar four-line `let
5581    /// foreign_code_slots = caixa.declared_foreign_code_slots(); if
5582    /// !foreign_code_slots.is_empty() { return
5583    /// Err(LayoutError::foreign_code_slot(caixa, foreign_code_slots));
5584    /// }` block at [`crate::layout::StandardLayout::verify`] — one
5585    /// consumer today but every future consumer that wanted to gate
5586    /// the code-surface coherence axis as a unit (the deferred
5587    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5588    /// webhook re-checking after a per-slot patch, a future
5589    /// `feira validate --foreign-code` per-caixa admission verb, a
5590    /// per-`Caixa` overlay resolver rejecting a kind-foreign code-
5591    /// slot patch) was structurally forced to either re-inline the
5592    /// two-condition guard in lockstep with the layout wire-up (the
5593    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5594    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5595    /// Post-fold each such consumer reaches the arm through one call.
5596    ///
5597    /// Peer of the sibling three-arm
5598    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5599    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5600    /// axes under the uniform `{ caixa, kind, slots }` envelope
5601    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5602    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5603    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]); this gate
5604    /// carries the code-surface axis under the same
5605    /// `{ caixa, kind, slots }` envelope
5606    /// ([`crate::LayoutError::ForeignCodeSlot`]). The two folds share
5607    /// the envelope shape but stay separate primitives because the
5608    /// per-arm predicate differs: the cross-family fold rides on the
5609    /// outer `!self.kind().is_<owner>()` guard *paired* with a
5610    /// per-family `declared_<family>_slots` accumulator, while this
5611    /// fold's per-arm kind-check is baked into
5612    /// [`Self::declared_foreign_code_slots`] itself (each arm's
5613    /// `!self.kind().requires_<slot>()` guard fires inside the
5614    /// accumulator, not around it) — so a `:kind Binario` declaring
5615    /// `:servicos` and a `:kind Servico` declaring `:exe` are both
5616    /// caught by one accumulator sweep rather than by two independent
5617    /// arm dispatches. Peer with [`Self::validate_ci_kind_coherence`]
5618    /// (9b55beb) which carries the `:ci` axis on its own primitive
5619    /// for the same "distinct per-arm predicate shape, shared
5620    /// diagnostic altitude" reason.
5621    ///
5622    /// Peer to the per-kind and per-slot compound entry gates every
5623    /// substrate primitive on the M2/M3 typed-slot family already
5624    /// carries ([`Self::validate_deps`] b5dd55e,
5625    /// [`Self::validate_limits`] baa4688,
5626    /// [`Self::validate_behavior`] 0d2877a,
5627    /// [`Self::validate_upgrade_from`] d6801df,
5628    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5629    /// [`Self::validate_supervisor_shape`] 4c70105,
5630    /// [`Self::validate_acao_shape`] 5d6df54,
5631    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5632    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5633    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5634    /// author-time coherence axis on the typed [`Caixa`] surface now
5635    /// routes through one substrate primitive per axis rather than an
5636    /// open-coded block at the layout wire-up site. This closes the
5637    /// last open-coded kind ↔ slot coherence gate at the layout
5638    /// altitude — every kind-coherence diagnostic is now a substrate
5639    /// primitive.
5640    ///
5641    /// The gate carries three identity elements:
5642    /// - **Code-owning kinds on their native slot** — a
5643    ///   [`crate::CaixaKind::Binario`] declaring `:exe`, a
5644    ///   [`crate::CaixaKind::Servico`] declaring `:servicos` — each
5645    ///   arm's `!requires_<slot>()` predicate short-circuits inside
5646    ///   [`Self::declared_foreign_code_slots`], so the accumulator
5647    ///   returns an empty `Vec` and the outer `is_empty` short-
5648    ///   circuits before the wrap fires.
5649    /// - **Bare caixas** — a caixa with no declared code on any kind
5650    ///   passes the same accumulator's `is_empty` short-circuit on
5651    ///   every arm.
5652    /// - **No-code kinds** ([`crate::CaixaKind::Supervisor`] /
5653    ///   [`crate::CaixaKind::Aplicacao`] / [`crate::CaixaKind::Acao`])
5654    ///   declaring code — dominated upstream by the sibling
5655    ///   [`Self::validate_no_code_kind_coherence`] (3bbf6a2) which
5656    ///   surfaces [`crate::LayoutError::SupervisorOwnsCode`] /
5657    ///   [`crate::LayoutError::AplicacaoOwnsCode`] /
5658    ///   [`crate::LayoutError::AcaoOwnsCode`] first at the layout
5659    ///   wire-up site, so this gate never fires on a no-code kind
5660    ///   through the layout pipeline. A standalone caller reaching
5661    ///   this primitive without the sibling `_no_code_` gate first
5662    ///   would see a no-code kind's declared `:exe` / `:servicos`
5663    ///   surface `ForeignCodeSlot` here (the two folds partition the
5664    ///   diagnostic responsibility along the "declared no-code slot"
5665    ///   axis: no-code kinds get `OwnsCode`, code-running kinds get
5666    ///   `ForeignCodeSlot`), and the layout wire-up's canonical
5667    ///   `_no_code_` → `_foreign_code_` ordering keeps the
5668    ///   [`crate::LayoutError::SupervisorOwnsCode`] / … arm the one
5669    ///   that surfaces in the composed pipeline.
5670    ///
5671    /// Diagnostic order within the arm matches the pre-fold layout
5672    /// wire-up canonical sequence — `:exe` → `:servicos` — pinned by
5673    /// [`Self::declared_foreign_code_slots`]'s per-arm push order.
5674    ///
5675    /// # Errors
5676    ///
5677    /// Returns [`crate::LayoutError::ForeignCodeSlot`] naming the
5678    /// offending caixa's nome + kind + declared foreign-code slot
5679    /// list on any code-running kind ([`crate::CaixaKind::Biblioteca`]
5680    /// / [`crate::CaixaKind::Binario`] / [`crate::CaixaKind::Servico`])
5681    /// declaring another code-running kind's exclusive code surface.
5682    /// Passes trivially on every native-slot declaration (Binario
5683    /// with `:exe`, Servico with `:servicos`), on every bare caixa,
5684    /// and on every no-code kind (dominated upstream by the sibling
5685    /// [`Self::validate_no_code_kind_coherence`] `OwnsCode` gates —
5686    /// see the identity-element notes above).
5687    pub fn validate_foreign_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5688        let foreign_code_slots = self.declared_foreign_code_slots();
5689        if !foreign_code_slots.is_empty() {
5690            return Err(crate::LayoutError::foreign_code_slot(
5691                self,
5692                foreign_code_slots,
5693            ));
5694        }
5695        Ok(())
5696    }
5697
5698    /// Compound per-`Caixa` required-slot gate on the three
5699    /// [`crate::CaixaKind`] arms whose sole payload is a canonical
5700    /// typed slot: `Binario`'s `:exe`, `Servico`'s `:servicos`,
5701    /// `Acao`'s `:ci`. Each arm refuses a caixa on its owner kind
5702    /// that declares no value in the corresponding required slot,
5703    /// so `feira build` (the canonical author-time gate) surfaces the
5704    /// self-locating "this kind needs this slot" diagnostic at the
5705    /// source `caixa.lisp` rather than deferring the failure to a
5706    /// downstream consumer (a nix build with no `:exe` to build, a
5707    /// programs.yaml fan-out with no `:servicos` to enumerate, a
5708    /// `caixa-actions` decompose with no `:ci` to walk).
5709    ///
5710    /// Pre-lift each of the three arms lived as a self-similar
5711    /// `if caixa.kind().requires_<slot>() && caixa.<slot>().is_<empty>() {
5712    /// return Err(LayoutError::<kind>_without_<slot>(caixa)); }`
5713    /// block at [`crate::layout::StandardLayout::verify`] — three
5714    /// consumers, three identical shapes, one substrate primitive on
5715    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
5716    /// a bug. Each of the three inner ctors
5717    /// ([`crate::LayoutError::binario_without_exe`] /
5718    /// [`crate::LayoutError::servico_without_servicos`] /
5719    /// [`crate::LayoutError::missing_ci`]) was already lifted onto
5720    /// the substrate by the peer [`crate::layout::layout_nome_only_ctors!`]
5721    /// macro, so the primitive routes through the same
5722    /// `Self::<variant>(caixa.nome().to_string())` tuple-literal
5723    /// wrap per arm as the pre-lift open-coded blocks.
5724    ///
5725    /// The paired `Biblioteca`-arm required-slot check
5726    /// ([`crate::LayoutError::MissingLib`]) stays open-coded at the
5727    /// layout wire-up site by design: it needs the filesystem oracle
5728    /// on [`crate::layout::LayoutInvariants`] to check the default
5729    /// `lib/<nome>.lisp` fallback path, which the pure per-`Caixa`
5730    /// typed-shape surface this fold rides on has no reference to.
5731    /// Same posture the peer [`Self::validate_no_code_kind_coherence`]
5732    /// fold takes on the on-disk existence loops.
5733    ///
5734    /// Diagnostic order at the primitive matches the pre-fold layout
5735    /// wire-up canonical sequence — `:exe` → `:servicos` → `:ci` —
5736    /// the same three-arm sweep the peer [`crate::CaixaKind`]
5737    /// discriminator carries at its `requires_*` accessors. Unlike
5738    /// the sibling cross-family [`Self::validate_kind_slot_coherence`]
5739    /// fold, the three arms of this fold are mutually exclusive by
5740    /// construction — `:kind` is a single-valued [`crate::CaixaKind`]
5741    /// discriminator so at most one arm can fire per caixa — and no
5742    /// cross-arm ordering pin is meaningful (the pre-fold three-block
5743    /// cascade at the wire-up site was already unreachable past the
5744    /// first matching arm).
5745    ///
5746    /// Peer to the per-kind and per-slot compound entry gates every
5747    /// substrate primitive on the M2/M3 typed-slot family already
5748    /// carries ([`Self::validate_deps`] b5dd55e,
5749    /// [`Self::validate_limits`] baa4688,
5750    /// [`Self::validate_behavior`] 0d2877a,
5751    /// [`Self::validate_upgrade_from`] d6801df,
5752    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5753    /// [`Self::validate_supervisor_shape`] 4c70105,
5754    /// [`Self::validate_acao_shape`] 5d6df54,
5755    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5756    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5757    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5758    /// author-time coherence axis on the typed [`Caixa`] surface
5759    /// now routes through one substrate primitive per axis rather
5760    /// than an open-coded block at the layout wire-up site.
5761    ///
5762    /// The gate carries two identity elements:
5763    /// - **Non-owner kinds** — each per-arm predicate is
5764    ///   `self.kind().requires_<slot>()`, which returns `true` only
5765    ///   for the owning kind ([`crate::CaixaKind::Binario`] on `:exe`,
5766    ///   [`crate::CaixaKind::Servico`] on `:servicos`,
5767    ///   [`crate::CaixaKind::Acao`] on `:ci`). Every non-owner kind
5768    ///   passes each per-arm dispatch trivially.
5769    /// - **Owner kinds with the required slot present** — a
5770    ///   [`crate::CaixaKind::Binario`] with a non-empty `:exe`, a
5771    ///   [`crate::CaixaKind::Servico`] with a non-empty `:servicos`,
5772    ///   an [`crate::CaixaKind::Acao`] with `ci = Some(_)` — passes
5773    ///   its arm's `is_empty` / `is_none` short-circuit.
5774    ///
5775    /// # Errors
5776    ///
5777    /// Returns the [`crate::LayoutError`] variant naming the
5778    /// offending owner kind:
5779    /// [`crate::LayoutError::BinarioWithoutExe`] on a
5780    /// [`crate::CaixaKind::Binario`] caixa with no declared `:exe`,
5781    /// [`crate::LayoutError::ServicoWithoutServicos`] on a
5782    /// [`crate::CaixaKind::Servico`] caixa with no declared
5783    /// `:servicos`, [`crate::LayoutError::MissingCi`] on a
5784    /// [`crate::CaixaKind::Acao`] caixa with no declared `:ci`.
5785    /// Passes trivially on every non-owner kind and on every owner
5786    /// kind with its required slot present.
5787    pub fn validate_required_kind_slot(&self) -> Result<(), crate::LayoutError> {
5788        if self.kind().requires_exe() && self.exe().is_empty() {
5789            return Err(crate::LayoutError::binario_without_exe(self));
5790        }
5791        if self.kind().requires_servicos() && self.servicos().is_empty() {
5792            return Err(crate::LayoutError::servico_without_servicos(self));
5793        }
5794        if self.kind().requires_ci() && self.ci().is_none() {
5795            return Err(crate::LayoutError::missing_ci(self));
5796        }
5797        Ok(())
5798    }
5799
5800    /// Reject per-entry values on the three Caixa-level code-surface
5801    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
5802    /// layout checker's `root.join(p)` sandbox would silently subvert.
5803    /// Same three structural footguns the peer
5804    /// [`BehaviorSpec::validate`] (b0c8389) and
5805    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
5806    /// (26da2c7) already close on the M2 `:behavior :on-*` and
5807    /// `:upgrade-from :state-change :script` axes, here lifted onto
5808    /// the three top-level code-path axes through the shared
5809    /// [`is_sandboxed_relative_path`] predicate:
5810    ///
5811    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
5812    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
5813    ///     [`Path::join`] as the base itself — `root.join("")` ==
5814    ///     `root`, so the existence check (`self.exists(&root)`)
5815    ///     trivially passes (the project root exists), and the layout
5816    ///     silently treats the project root as a biblioteca / exe /
5817    ///     servico entry. The `:bibliotecas` loop then hands the root
5818    ///     to `tatara_lisp::read` at `feira build` time as if the root
5819    ///     directory itself were a Lisp source file — a parse error
5820    ///     far from the source `caixa.lisp` with no field naming the
5821    ///     offending entry.
5822    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
5823    ///     [`Path::join`] *replaces* the base when the right-hand side
5824    ///     is absolute, so `root.join("/etc/passwd")` resolves to
5825    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
5826    ///     The existence check then silently consults whatever the
5827    ///     escaped path resolves to — for `:bibliotecas`, the layout
5828    ///     has no `starts_with`-fence (only `:exe` is fenced under
5829    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
5830    ///     `:bibliotecas` entry that happens to resolve on disk
5831    ///     silently passes. For `:exe` / `:servicos` the fence catches
5832    ///     the absolute case downstream as `ExeOutsideDir` /
5833    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
5834    ///     doesn't exist), but with a downstream-shaped diagnostic
5835    ///     that names the resolved escape path rather than the
5836    ///     authoring footgun at the source.
5837    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
5838    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
5839    ///     [`std::path::Component::ParentDir`] anywhere round-trips
5840    ///     through [`Path::join`] as a traversal above the caixa root.
5841    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
5842    ///     *component-aware* (not canonical-path-aware), so
5843    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
5844    ///     is **true** even though the canonical resolution
5845    ///     `{parent of root}/escape.lisp` lives outside the caixa root
5846    ///     — the fence silently lets the parent-escape through, and
5847    ///     the existence check passes if that escape-target happens
5848    ///     to exist. Caught regardless of where the `..` sits
5849    ///     (leading, mid-path, trailing) so the gate matches the peer
5850    ///     predicate's full coverage.
5851    ///
5852    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
5853    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
5854    /// same per-slot diagnostic shape every peer per-axis path-gate
5855    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
5856    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
5857    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
5858    /// order [`Caixa::declared_foreign_code_slots`] uses for its
5859    /// canonical foreign-code-slot diagnostic, so a manifest with
5860    /// multiple malformed slots surfaces the lexicographically-earliest
5861    /// slot's diagnostic deterministically.
5862    ///
5863    /// Lifted to the typed surface as a Caixa-level validator (peer
5864    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
5865    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
5866    /// and wired into [`crate::StandardLayout::verify`] before the
5867    /// existence-check loops so the diagnostic names the offending
5868    /// slot at the source caixa.lisp rather than reporting a
5869    /// downstream `MissingEntry` / `ExeOutsideDir` /
5870    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
5871    /// The fourth typed code-path surface — every author-supplied
5872    /// path on the manifest — is now structurally accept-shaped
5873    /// past validate, peer with `:behavior :on-*` and
5874    /// `:upgrade-from :state-change :script`.
5875    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
5876        /// Per-slot file-type contract for the three Caixa-level
5877        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
5878        /// Each variant names the predicate the per-entry file-type
5879        /// gate consults; [`Self::None`] opts the slot out of any
5880        /// file-type contract. Lifted as a typed local enum so the
5881        /// per-slot dispatch is exhaustive at the `match` — adding a
5882        /// future axis to the typed-substrate `:` slot set (the
5883        /// future `:assets` resource axis the M5 roadmap names, the
5884        /// future `:nix-flake` derivation axis the caixa-flake
5885        /// emitter consults) lands as one variant + one `match` arm,
5886        /// not a coordinated rewrite of every per-slot bool flag.
5887        ///
5888        /// Peer of the typed-substrate per-slot variant disciplines
5889        /// already established on this surface
5890        /// ([`crate::supervisor::RestartStrategy`] +
5891        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
5892        /// supervision-tree axis,
5893        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
5894        /// placement axis, [`crate::aplicacao::WitTarget`] on the
5895        /// `:contratos` payload-target axis): the typed `enum` is
5896        /// the substrate's single source of truth for the per-axis
5897        /// dispatch, and every consumer (the per-arm body here, the
5898        /// future feira-lint per-slot diagnostic renderer, the M4
5899        /// per-axis admission webhook) reaches for the same typed
5900        /// surface rather than re-deriving the partition from inline
5901        /// flag combinations.
5902        enum CodePathFileType {
5903            /// `:exe` — nix-build derivation output, no terminating-
5904            /// extension contract (the canonical `"exe/<name>"`
5905            /// fixtures the layout's `ExeOutsideDir` error message
5906            /// documents carry no extension by convention).
5907            None,
5908            /// `:bibliotecas` — tatara-lisp source files the
5909            /// `feira build` loop reads through `tatara_lisp::read`
5910            /// at parse time. Routes to [`is_lisp_extension`].
5911            LispSource,
5912            /// `:servicos` — ComputeUnit-CR YAML files the
5913            /// caixa-helm / caixa-flux renderers consume through
5914            /// `serde_yaml::from_str`. Routes to
5915            /// [`is_computeunit_yaml_extension`].
5916            ComputeUnitYaml,
5917        }
5918
5919        // The per-slot [`CodePathFileType`] selects which axes carry the
5920        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
5921        // source axis (the `feira build` loop at
5922        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
5923        // `tatara_lisp::read` at parse time) — the lifted
5924        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
5925        // `:exe` is the nix-built executable surface (per the canonical
5926        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
5927        // error message documents and every in-tree
5928        // `caixa_with_code_paths` positive control uses) — its file-type
5929        // contract is "nix-build derivation output", not a typed source
5930        // file, so [`CodePathFileType::None`] opts the slot out of any
5931        // file-type gate. `:servicos` is the `.computeunit.yaml`
5932        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
5933        // renderers consume each entry through `serde_yaml::from_str` as
5934        // a typed `ComputeUnit` CR) — the lifted
5935        // [`is_computeunit_yaml_extension`] predicate gates the compound
5936        // `.computeunit.yaml` suffix. All three axes are surfaced through
5937        // the same iteration so the sandbox-shape + duplicate gates
5938        // apply uniformly; the typed file-type dispatch fires per-slot
5939        // exactly where the downstream consumer's accepted set demands
5940        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
5941        // compounding lift on the peer 64772a9 `:bibliotecas`
5942        // `.lisp`-gate trajectory — the second of the three code-path
5943        // axes to land on a typed compound-suffix gate, with the same
5944        // self-locating per-slot diagnostic shape every peer per-axis
5945        // file-type lift uses (`*NonLispExtension { slot, path }` /
5946        // `*NonComputeUnitYamlExtension { slot, path }`).
5947        for (slot, list, file_type) in [
5948            (
5949                ":bibliotecas",
5950                &self.bibliotecas,
5951                CodePathFileType::LispSource,
5952            ),
5953            (":exe", &self.exe, CodePathFileType::None),
5954            (
5955                ":servicos",
5956                &self.servicos,
5957                CodePathFileType::ComputeUnitYaml,
5958            ),
5959        ] {
5960            // Per-slot set-not-multiset gate on the typed code-path axis.
5961            // Every peer Vec-shaped author-supplied list past validate is
5962            // a set, not a multiset: `:membros :caixa`
5963            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
5964            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
5965            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
5966            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
5967            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
5968            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
5969            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
5970            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
5971            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
5972            // the three code-path lists are the last Vec-shaped author-
5973            // supplied slots on the typed Caixa surface still admitting a
5974            // duplicate entry silently. Scope is per-list (`:bibliotecas`
5975            // duplicates are flagged within `:bibliotecas`, not across
5976            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
5977            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
5978            // legitimate dev-vs-runtime shape on the dep axis, fenced
5979            // separately by [`crate::dep::validate_no_self_dep`]). On the
5980            // code-path axis a cross-slot collision is structurally
5981            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
5982            // fence — `:exe` and `:servicos` entries are confined to their
5983            // own directory trees, so the only way a string could appear
5984            // on two code-path lists is the (rare, structurally invalid)
5985            // case where `:bibliotecas` carries an `"exe/<x>"` or
5986            // `"servicos/<x>.yaml"`-shaped path.
5987            //
5988            // Without the gate three authoring footguns silently passed:
5989            //
5990            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
5991            //     canonical copy-paste-the-wrong-file footgun. `feira
5992            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
5993            //     list and re-parses the same file twice, wasting work
5994            //     and silently masking the author's intent to declare a
5995            //     *second* biblioteca.
5996            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
5997            //     Binario surface. The future `caixa-flake` `nix flake`
5998            //     emitter that materializes each `:exe` entry as a flake
5999            //     `packages.<exe-name>` derivation would collide on the
6000            //     duplicate package name and surface a flake-eval error
6001            //     far from the source `caixa.lisp`.
6002            //   - `:servicos ("servicos/x.computeunit.yaml"
6003            //     "servicos/x.computeunit.yaml")` — the same footgun on
6004            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
6005            //     renderers already refuse `:servicos.len() != 1` with
6006            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
6007            //     that diagnostic surfaces "too many servicos" without
6008            //     naming "duplicate entry" — the typed self-locating
6009            //     "which entry is the duplicate" framing only lands at
6010            //     this gate.
6011            //
6012            // Same `seen.insert(entry.as_str())` shape every peer per-list
6013            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
6014            // 86c769b, `:deps` 359fba5) and the same "structural shape
6015            // checks fire before the duplicate check on the same entry"
6016            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
6017            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
6018            // empty entry first, not the duplicate on the later pair).
6019            let mut seen = std::collections::HashSet::new();
6020            for entry in list {
6021                let path = Path::new(entry);
6022                match is_sandboxed_relative_path(path) {
6023                    Ok(()) => {}
6024                    Err(PathShapeViolation::Empty) => {
6025                        return Err(ManifestError::CodePathEmpty { slot });
6026                    }
6027                    Err(PathShapeViolation::Absolute) => {
6028                        return Err(ManifestError::CodePathAbsolute {
6029                            slot,
6030                            path: path.to_path_buf(),
6031                        });
6032                    }
6033                    Err(PathShapeViolation::ParentEscape) => {
6034                        return Err(ManifestError::CodePathParentEscape {
6035                            slot,
6036                            path: path.to_path_buf(),
6037                        });
6038                    }
6039                }
6040                // The per-slot file-type gate dispatched through the
6041                // typed [`CodePathFileType`] selector above. Each variant
6042                // routes to the lifted predicate the downstream consumer
6043                // demands:
6044                //
6045                //   - [`LispSource`] → [`is_lisp_extension`] for
6046                //     `:bibliotecas` (the `feira build` loop's
6047                //     `tatara_lisp::read` consumer);
6048                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
6049                //     for `:servicos` (the caixa-helm / caixa-flux
6050                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
6051                //     accepted set);
6052                //   - [`None`] for `:exe` — the nix-build derivation-
6053                //     output axis has no terminating-extension contract.
6054                //
6055                // Fires after the sandbox-shape arms so a path that is
6056                // *both* sandbox-escaping and wrong-extension surfaces
6057                // the more fundamental sandbox-shape diagnostic first
6058                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
6059                // `ParentEscape` → `NonLispExtension` arm-ordering on
6060                // `:behavior :on-*` c97815a, and `EmptyScript` →
6061                // `AbsoluteScript` → `ParentEscapeScript` →
6062                // `NonLispExtensionScript` on
6063                // `:upgrade-from :state-change :script` 33cc830), and
6064                // before the duplicate gate so the narrower per-entry
6065                // file-type shape dominates the cross-entry uniqueness
6066                // diagnostic (a
6067                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
6068                // `:servicos` surfaces
6069                // `CodePathNonComputeUnitYamlExtension` on the first
6070                // entry rather than `CodePathDuplicate` on the pair —
6071                // peer with the 64772a9 `:bibliotecas`
6072                // `("lib/x.txt" "lib/x.txt")` ordering).
6073                match file_type {
6074                    CodePathFileType::None => {}
6075                    CodePathFileType::LispSource => {
6076                        if !is_lisp_extension(path) {
6077                            return Err(ManifestError::CodePathNonLispExtension {
6078                                slot,
6079                                path: path.to_path_buf(),
6080                            });
6081                        }
6082                    }
6083                    CodePathFileType::ComputeUnitYaml => {
6084                        if !is_computeunit_yaml_extension(path) {
6085                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
6086                                slot,
6087                                path: path.to_path_buf(),
6088                            });
6089                        }
6090                    }
6091                }
6092                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
6093                    ManifestError::CodePathDuplicate {
6094                        slot,
6095                        path: path.to_path_buf(),
6096                    }
6097                })?;
6098            }
6099        }
6100        Ok(())
6101    }
6102
6103    /// Reject `:etiquetas` lists with an empty entry or with two entries
6104    /// agreeing on the same string. `:etiquetas` is the universal
6105    /// registry-search-tag axis on [`Caixa`] (every kind carries the
6106    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6107    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
6108    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
6109    /// a [`std::collections::BTreeSet`] alongside the four substrate-
6110    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
6111    /// Two authoring footguns silently passed validate without this gate:
6112    ///
6113    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
6114    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
6115    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
6116    ///     `chart.metadata.keywords` admits the value without a strict
6117    ///     parser-side gate, but the empty keyword has no operational
6118    ///     meaning — it indexes nothing in the future caixa-registry
6119    ///     search axis and clutters the rendered chart with a no-op tag.
6120    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
6121    ///     copy-paste-the-wrong-tag footgun) silently passed validate
6122    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
6123    ///     at chart render — a "second wins / one silently disappears"
6124    ///     shape divergent from every peer typed-graph set gate
6125    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
6126    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
6127    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6128    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6129    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6130    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
6131    ///     on `:upgrade-from`, the per-instruction-class singularity
6132    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
6133    ///     [`crate::UpgradeError::DuplicateStateChange`] /
6134    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
6135    ///     discipline is uniform: every Vec-shaped author-supplied list
6136    ///     past validate is set-not-multiset, by construction.
6137    ///
6138    /// Past the empty arm the gate enforces the chart-keyword shape
6139    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
6140    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
6141    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
6142    /// continuation. Closes the canonical paste-from-doc footguns the
6143    /// bare empty + duplicate arms left open: paste-from-aligned-doc
6144    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
6145    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
6146    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
6147    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
6148    /// — the author meant three separate list entries), path-separator
6149    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
6150    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
6151    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
6152    /// control bytes that would silently land as malformed search tags
6153    /// in the rendered Chart.yaml `keywords:` array and break the
6154    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
6155    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
6156    /// established on the sibling universal-axis `Vec<String>` surface
6157    /// — the second universal-axis Vec<String> surface to land the
6158    /// empty-first-then-shape-then-duplicate per-entry cascade.
6159    ///
6160    /// Same empty-first cascade discipline every peer per-axis gate
6161    /// uses: the per-entry empty arm fires before the per-entry shape
6162    /// arm fires before the cross-entry duplicate arm, so an
6163    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
6164    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
6165    /// has no value" defect) before either the shape or the duplicate
6166    /// diagnostic. Walks the list in declaration order so the
6167    /// first-collision diagnostic surfaces the lexicographically-
6168    /// earliest offending position, peer with every other duplicate
6169    /// gate on this surface.
6170    ///
6171    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
6172    /// caixa-build gate alongside the peer universal gates
6173    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6174    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
6175    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6176    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6177    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6178    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6179    /// slot sets. The future caixa-registry search axis can reach for
6180    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
6181    /// chart-keyword-shaped string without re-deriving the precondition.
6182    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
6183        let mut seen = std::collections::HashSet::new();
6184        for etiqueta in self.etiquetas() {
6185            if etiqueta.is_empty() {
6186                return Err(ManifestError::EtiquetaEmpty);
6187            }
6188            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
6189                ManifestError::EtiquetaInvalid {
6190                    etiqueta: etiqueta.clone(),
6191                    reason,
6192                }
6193            })?;
6194            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
6195                ManifestError::EtiquetaDuplicate {
6196                    etiqueta: etiqueta.clone(),
6197                }
6198            })?;
6199        }
6200        Ok(())
6201    }
6202
6203    /// Reject `:autores` lists with an empty entry or with two entries
6204    /// agreeing on the same string. `:autores` is the universal
6205    /// maintainer-axis on [`Caixa`] (every kind carries the
6206    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6207    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
6208    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
6209    /// to a `Maintainer { name, email: None }` without dedup). Two
6210    /// authoring footguns silently passed validate without this gate:
6211    ///
6212    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
6213    ///     blank-doc footgun) rendered as
6214    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
6215    ///     empty maintainer name has no operational meaning — it
6216    ///     identifies no one in the substrate's authorship index and
6217    ///     clutters the rendered chart with a no-op maintainer.
6218    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
6219    ///     the copy-paste-the-wrong-author footgun) silently passed
6220    ///     validate and rendered as two identical maintainer entries.
6221    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
6222    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
6223    ///     rendered `keywords:` array at chart-render time), the
6224    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
6225    ///     entries stack verbatim in the chart, divergent from every
6226    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
6227    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
6228    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6229    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6230    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6231    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
6232    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
6233    ///     `:etiquetas`).
6234    ///
6235    /// Past the empty arm the gate enforces the chart-maintainer-name
6236    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
6237    /// the structural single-line printable-UTF-8 floor every realistic
6238    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
6239    /// or trailing whitespace, no ASCII control characters anywhere,
6240    /// Unicode bytes accepted. Closes the canonical paste-from-doc
6241    /// footguns the bare empty + duplicate arms left open:
6242    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
6243    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
6244    /// pasted a multi-line block of author records into one `:autores`
6245    /// entry instead of splitting into one entry per author),
6246    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
6247    /// and the paste-from-binary-blob control bytes that would silently
6248    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
6249    /// `maintainers:` array. Mirrors the shape-predicate cascade
6250    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6251    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
6252    /// establish past their own empty arms on the sibling universal-axis
6253    /// `Option<String>` surfaces — the first universal-axis Vec<String>
6254    /// surface to land the empty-first-then-shape-then-duplicate per-entry
6255    /// cascade.
6256    ///
6257    /// Same empty-first cascade discipline every peer per-axis gate
6258    /// uses: the per-entry empty arm fires before the per-entry shape
6259    /// arm before the cross-entry duplicate arm. Walks the list in
6260    /// declaration order so the first-collision diagnostic surfaces the
6261    /// lexicographically-earliest offending position, peer with every
6262    /// other duplicate gate on this surface.
6263    ///
6264    /// Universal-axis (every kind carries `:autores`), so wired at the
6265    /// caixa-build gate alongside the peer universal gates
6266    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6267    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6268    /// [`Self::validate_code_paths`] — before the kind-coherence gates
6269    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6270    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6271    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6272    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6273    /// slot sets.
6274    pub fn validate_autores(&self) -> Result<(), ManifestError> {
6275        let mut seen = std::collections::HashSet::new();
6276        for autor in self.autores() {
6277            if autor.is_empty() {
6278                return Err(ManifestError::AutorEmpty);
6279            }
6280            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
6281                ManifestError::AutorInvalid {
6282                    autor: autor.clone(),
6283                    reason,
6284                }
6285            })?;
6286            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
6287                ManifestError::AutorDuplicate {
6288                    autor: autor.clone(),
6289                }
6290            })?;
6291        }
6292        Ok(())
6293    }
6294
6295    /// Reject `:repositorio` values whose shape the shared
6296    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
6297    /// `repositorio: Option<String>` slot on [`Caixa`] is the
6298    /// universal git-shaped homepage axis every kind carries — the
6299    /// substrate routes the same string through two load-bearing
6300    /// consumers:
6301    ///
6302    ///   - [`caixa-helm`] folds it verbatim into the rendered
6303    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
6304    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
6305    ///     the chart `README.md` `repo = …` interpolation
6306    ///     (`caixa-helm/src/lib.rs:359`).
6307    ///   - [`caixa-flux`] folds it verbatim into the standalone
6308    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
6309    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
6310    ///     `GitRepository.spec.url` the cluster's source-controller
6311    ///     polls — the load-bearing deploy-time axis.
6312    ///
6313    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6314    /// substitute a placeholder when the slot is absent (`None` → the
6315    /// fallback fires); a `Some("")` *skips the fallback* and silently
6316    /// passes the empty string through to `Chart.yaml home: ""` /
6317    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
6318    /// controller both reject the empty URL far from the source
6319    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
6320    /// Similarly a malformed `:repositorio` (whitespace, control char,
6321    /// missing `:` separator, leading `-`) silently lands in the
6322    /// rendered artifacts and breaks at `git clone` / `helm template`
6323    /// / `flux reconcile` time.
6324    ///
6325    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
6326    /// same shared predicate the peer [`crate::DepSource::validate`]
6327    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
6328    /// gate the two `git URL`-shaped surfaces on the typed Caixa
6329    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
6330    /// structurally equivalent: every value past validate is
6331    /// guaranteed-acceptable by the predicate's union of constraints
6332    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
6333    /// control chars, ASCII only, no leading `:`, contains a `:`
6334    /// separator). The predicate accepts every documented authoring
6335    /// shape — `github:org/repo` shorthand, `https://host/path`,
6336    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
6337    /// scp-style SSH, `file:///path` — and refuses the canonical
6338    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
6339    /// injection footguns at validate time. Maps the predicate's
6340    /// `String` reason verbatim into the
6341    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
6342    /// offending value + parser-shaped reason so the diagnostic is
6343    /// self-locating (the author can grep their `caixa.lisp` for
6344    /// `:repositorio "<value>"` and fix it in one edit).
6345    ///
6346    /// `None` (the canonical "omit the slot to express no published
6347    /// homepage" shape) is accepted trivially — the gate is a no-op
6348    /// when the author didn't declare a value. `Some("")` is gated by
6349    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
6350    /// shape predicate is consulted, mirroring the empty-first cascade
6351    /// every peer per-axis identity gate uses
6352    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
6353    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
6354    /// [`crate::DepError::FonteRepoEmpty`] →
6355    /// [`crate::DepError::FonteRepoInvalid`]).
6356    ///
6357    /// Universal-axis (every kind carries `:repositorio`), so wired at
6358    /// the caixa-build gate alongside the peer universal gates
6359    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6360    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6361    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
6362    /// before the kind-coherence gates
6363    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6364    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6365    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6366    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6367    /// specific slot sets.
6368    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
6369        let Some(s) = self.repositorio() else {
6370            return Ok(());
6371        };
6372        if s.is_empty() {
6373            return Err(ManifestError::RepositorioEmpty);
6374        }
6375        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
6376            repositorio: s.to_string(),
6377            reason,
6378        })
6379    }
6380
6381    /// Reject `:descricao` values that are the empty string. The flat
6382    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
6383    /// free-form-prose homepage axis every kind carries — the
6384    /// substrate routes the same string through two load-bearing
6385    /// consumers in the [`caixa-helm`] renderer:
6386    ///
6387    ///   - `build_chart_yaml` folds it verbatim into the rendered
6388    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
6389    ///     field (`caixa-helm/src/lib.rs:232-235`).
6390    ///   - `build_readme` folds it verbatim into the rendered chart
6391    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
6392    ///
6393    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6394    /// substitute a `caixa.nome`-derived placeholder when the slot is
6395    /// absent (`None` → the fallback fires); a `Some("")` *skips the
6396    /// fallback* and silently passes the empty string through to
6397    /// `Chart.yaml description: ""` / a blank chart `README.md`
6398    /// header. Helm's chart spec requires a non-empty `description:`
6399    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
6400    /// `WARNING [chart.metadata.description]: description is required`),
6401    /// so the empty `Some("")` silently lands in the rendered
6402    /// artifacts and breaks at `helm lint` / `helm install` time far
6403    /// from the source `caixa.lisp`, with no field naming the
6404    /// offending `:descricao`.
6405    ///
6406    /// `None` (the canonical "omit the slot to defer to the renderer's
6407    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
6408    /// the gate is a no-op when the author didn't declare a value.
6409    /// `Some("")` is gated by the narrower
6410    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
6411    /// shape every peer per-axis empty gate uses
6412    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6413    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6414    /// [`ManifestError::RepositorioEmpty`]).
6415    ///
6416    /// Universal-axis (every kind carries `:descricao`), so wired at
6417    /// the caixa-build gate alongside the peer universal gates
6418    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6419    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6420    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6421    /// [`Self::validate_code_paths`] — before the kind-coherence
6422    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6423    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6424    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6425    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6426    /// specific slot sets.
6427    ///
6428    /// Past the empty arm the gate enforces the chart-description
6429    /// shape predicate via [`crate::render::is_chart_description_shape`]:
6430    /// the structural single-line UTF-8 floor every realistic chart
6431    /// description in the wild matches — 1..=512 bytes, no leading
6432    /// or trailing whitespace, no ASCII control characters anywhere
6433    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
6434    /// carriage return, and every other control byte), Unicode
6435    /// continuation bytes accepted (the canonical fixtures carry
6436    /// `→` and `—`). Closes the canonical paste-from-doc footguns
6437    /// the bare empty-arm gate left open: paste-from-aligned-doc
6438    /// leading / trailing whitespace (`" Checkout flow."`,
6439    /// `"Checkout flow. "`), paste-from-multiline-doc newline
6440    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
6441    /// (`"Checkout\rflow."`), tab-from-aligned-doc
6442    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
6443    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
6444    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
6445    /// [`Self::validate_edicao`] establish past their own empty arms
6446    /// on the sibling universal-axis `Option<String>` Caixa-level
6447    /// value-shape surfaces.
6448    ///
6449    /// The empty-first cascade discipline mirrors every peer per-axis
6450    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
6451    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
6452    /// diagnostic surfaces on `Some("")` rather than the broader
6453    /// shape-predicate diagnostic — peer with how
6454    /// [`ManifestError::LicencaEmpty`] runs before
6455    /// [`ManifestError::LicencaInvalid`],
6456    /// [`ManifestError::EdicaoEmpty`] runs before
6457    /// [`ManifestError::EdicaoInvalid`],
6458    /// [`ManifestError::RepositorioEmpty`] runs before
6459    /// [`ManifestError::RepositorioInvalid`].
6460    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
6461        let Some(s) = self.descricao() else {
6462            return Ok(());
6463        };
6464        if s.is_empty() {
6465            return Err(ManifestError::DescricaoEmpty);
6466        }
6467        crate::render::is_chart_description_shape(s).map_err(|reason| {
6468            ManifestError::DescricaoInvalid {
6469                descricao: s.to_string(),
6470                reason,
6471            }
6472        })?;
6473        Ok(())
6474    }
6475
6476    /// Reject `:licenca` values that are the empty string. The flat
6477    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
6478    /// SPDX-shaped license-expression axis every kind carries — the
6479    /// substrate routes the same string through the [`caixa-helm`]
6480    /// renderer's `build_readme` which folds it verbatim into the
6481    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
6482    /// section (`caixa-helm/src/lib.rs:361`) via
6483    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
6484    /// fallback only fires on `None`; a `Some("")` *skips the
6485    /// fallback* and silently passes the empty string through to a
6486    /// chart `README.md` whose `License` section renders as the bare
6487    /// trailing period (`.\n`) — peer footgun with the
6488    /// `Some("")`-skips-`unwrap_or_else` shape the
6489    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
6490    /// gates close on the sibling free-form-prose and git-URL axes.
6491    ///
6492    /// `None` (the canonical "omit the slot to defer to the
6493    /// renderer's `MIT` fallback" shape every existing fixture
6494    /// carries) is accepted trivially — the gate is a no-op when the
6495    /// author didn't declare a value. `Some("")` is gated by the
6496    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
6497    /// empty-arm shape every peer per-axis empty gate uses
6498    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6499    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6500    /// [`ManifestError::RepositorioEmpty`],
6501    /// [`ManifestError::DescricaoEmpty`]).
6502    ///
6503    /// Universal-axis (every kind carries `:licenca`), so wired at
6504    /// the caixa-build gate alongside the peer universal gates
6505    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6506    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6507    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6508    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
6509    /// — before the kind-coherence gates
6510    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6511    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6512    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6513    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6514    /// specific slot sets.
6515    ///
6516    /// Past the empty arm the gate enforces the SPDX-expression shape
6517    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
6518    /// structural alphabet floor every realistic SPDX expression in
6519    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
6520    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
6521    /// single ASCII space (token separator). Closes the canonical
6522    /// paste-from-doc footguns the bare empty-arm gate left open:
6523    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
6524    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
6525    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
6526    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
6527    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
6528    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
6529    /// Apache-2.0"`), and semicolon-list-separator confusion
6530    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
6531    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
6532    /// establish past their own empty arms.
6533    ///
6534    /// The empty-first cascade discipline mirrors every peer per-axis
6535    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
6536    /// [`ManifestError::LicencaInvalid`], so the narrower empty
6537    /// diagnostic surfaces on `Some("")` rather than the broader
6538    /// shape-predicate diagnostic — peer with how
6539    /// [`ManifestError::EdicaoEmpty`] runs before
6540    /// [`ManifestError::EdicaoInvalid`],
6541    /// [`ManifestError::RepositorioEmpty`] runs before
6542    /// [`ManifestError::RepositorioInvalid`].
6543    ///
6544    /// A future tightening on this axis can extend the alphabet
6545    /// floor into a full SPDX expression parser + license-id
6546    /// allowlist (rejecting alphabet-valid values that don't name a
6547    /// real SPDX license identifier — e.g., `"NotAReal"` is
6548    /// alphabet-valid but no `NotAReal` license-id exists). That
6549    /// parser only becomes meaningful past a real SPDX-spec
6550    /// dependency; this gate establishes the structural floor by
6551    /// refusing every non-SPDX-alphabet value at validate time.
6552    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
6553        let Some(s) = self.licenca() else {
6554            return Ok(());
6555        };
6556        if s.is_empty() {
6557            return Err(ManifestError::LicencaEmpty);
6558        }
6559        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
6560            ManifestError::LicencaInvalid {
6561                licenca: s.to_string(),
6562                reason,
6563            }
6564        })?;
6565        Ok(())
6566    }
6567
6568    /// Reject `:edicao` values that are the empty string. The flat
6569    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
6570    /// language-edition axis every kind carries — it determines the
6571    /// tatara-lisp macro surface + compatibility flags the substrate
6572    /// applies when building a caixa, and lands verbatim in the
6573    /// `Caixa::template` author-time scaffold (the canonical
6574    /// `:edicao "2026"` line every `feira init` emits via
6575    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
6576    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
6577    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
6578    /// `caixa-core/src/render.rs:2510`) via
6579    /// `edicao: Some("2026".into())`.
6580    ///
6581    /// `None` (the canonical "omit the slot to defer to the
6582    /// substrate's default edition" shape every existing
6583    /// [`caixa-resolver`] integration test fixture carries via
6584    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6585    /// is accepted trivially — the gate is a no-op when the author
6586    /// didn't declare a value. `Some("")` is gated by the narrower
6587    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
6588    /// shape every peer per-axis empty gate uses
6589    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6590    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6591    /// [`ManifestError::RepositorioEmpty`],
6592    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
6593    ///
6594    /// Universal-axis (every kind carries `:edicao`), so wired at
6595    /// the caixa-build gate alongside the peer universal gates
6596    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6597    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6598    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6599    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6600    /// [`Self::validate_code_paths`] — before the kind-coherence
6601    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6602    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6603    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6604    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6605    /// specific slot sets.
6606    ///
6607    /// Past the empty arm the gate enforces the canonical year-shape
6608    /// predicate: every documented tatara-lisp edition is a 4-digit
6609    /// ASCII decimal year (`"2026"` is the only edition currently
6610    /// minted; future-introduced siblings will follow the same
6611    /// shape, peer with Cargo's `[package] edition` grammar which
6612    /// every value Cargo has ever accepted matches — `"2015"`,
6613    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
6614    /// 4 ASCII decimal bytes is rejected with the narrower
6615    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
6616    /// shape-predicate cascade [`Self::validate_repositorio`]
6617    /// establishes past its own empty arm
6618    /// ([`ManifestError::RepositorioEmpty`] →
6619    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
6620    /// paste-from-doc footguns the bare empty-arm gate left open:
6621    ///
6622    ///   - leading / trailing whitespace from a paste-from-doc
6623    ///     (`"2026 "`, `" 2026"`)
6624    ///   - control characters / CRLF from a paste-from-multiline-doc
6625    ///     (`"2026\n"`)
6626    ///   - non-ASCII look-alikes from a fullwidth keyboard
6627    ///     (`"2026"`) which would silently land as a non-ASCII
6628    ///     string in the rendered caixa.lisp
6629    ///   - free-form non-year values (`"x"`, `"latest"`,
6630    ///     `"nightly"`) that have no operational meaning on the
6631    ///     substrate's build-time edition selector
6632    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
6633    ///     `"r2026"`) — common version-tag idioms that don't apply
6634    ///     to the year-shaped edition axis
6635    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
6636    ///     edition is a year, not a fractional version
6637    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
6638    ///     `"00026"`) that don't name a year
6639    ///
6640    /// `None` (the canonical "omit the slot to defer to the
6641    /// substrate's default edition" shape every existing
6642    /// [`caixa-resolver`] integration test fixture carries via
6643    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6644    /// is accepted trivially — the gate is a no-op when the author
6645    /// didn't declare a value. The empty-first cascade discipline
6646    /// mirrors every peer per-axis identity gate:
6647    /// [`ManifestError::EdicaoEmpty`] runs before
6648    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
6649    /// diagnostic surfaces on `Some("")` rather than the broader
6650    /// shape-predicate diagnostic — peer with how
6651    /// [`ManifestError::NomeEmpty`] runs before
6652    /// [`ManifestError::NomeInvalid`],
6653    /// [`ManifestError::VersaoEmpty`] runs before
6654    /// [`ManifestError::VersaoInvalid`],
6655    /// [`ManifestError::RepositorioEmpty`] runs before
6656    /// [`ManifestError::RepositorioInvalid`].
6657    ///
6658    /// A future tightening on this axis can extend the shape
6659    /// predicate into a known-edition allowlist (rejecting
6660    /// year-shaped values that don't name a tatara-lisp edition
6661    /// the substrate actually understands — e.g., `"1999"` is
6662    /// year-shaped but no `1999` edition exists). That allowlist
6663    /// only becomes meaningful past the introduction of a sibling
6664    /// edition to `"2026"`; this gate establishes the structural
6665    /// floor by refusing every non-year-shaped value at validate
6666    /// time.
6667    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
6668        let Some(s) = self.edicao() else {
6669            return Ok(());
6670        };
6671        if s.is_empty() {
6672            return Err(ManifestError::EdicaoEmpty);
6673        }
6674        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
6675            return Err(ManifestError::EdicaoInvalid {
6676                edicao: s.to_string(),
6677                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
6678            });
6679        }
6680        Ok(())
6681    }
6682
6683    /// Compose the supervisor-related flat slots into a single
6684    /// [`SupervisorSpec`] for validation. Returns `None` when the
6685    /// caixa isn't a `:kind Supervisor`.
6686    ///
6687    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
6688    /// simple (one form, no nested `:supervisor (…)` block); this view
6689    /// is the "typed shape" the operator + supervisor reconciler
6690    /// consume.
6691    #[must_use]
6692    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
6693        if !self.kind().is_supervisor() {
6694            return None;
6695        }
6696        // Fold through the shared `supervisor::duration_codec::parse`
6697        // — the same parser the serde-routed `with = "duration_codec"`
6698        // on `SupervisorSpec::restart_window`, the `:politicas
6699        // :timeout` codec, and the `:politicas :circuit-breaker
6700        // :window` codec all consume. The prior inline f64-shaped
6701        // duplicate (`parse_window_inline`) admitted every magnitude
6702        // the integer-magnitude gate (1c55a2a) rejects on the three
6703        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
6704        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
6705        // `None` (i.e. "no reset"), divergent from the shared codec's
6706        // integer-magnitude discipline by construction. The fold
6707        // closes the divergence: every value the typed
6708        // `SupervisorSpec` carries past `supervisor_view` is in the
6709        // shared codec's accepted set. The `.ok()` here preserves the
6710        // existing soft-swallow shape on this view-construction path;
6711        // the new [`Caixa::validate_restart_window`] (sibling of
6712        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
6713        // the offending raw string at build time so authoring tools
6714        // (`feira lint`, the future layout-side wire-up) surface a
6715        // self-locating diagnostic instead of a silently dropped
6716        // window.
6717        let restart_window = self
6718            .restart_window()
6719            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
6720        Some(SupervisorSpec {
6721            // Route the author-omitted `:estrategia` arm through the
6722            // substrate-canonical
6723            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
6724            // `pub const` rather than the transitively-derived
6725            // [`RestartStrategy::default`] route the prior
6726            // `.unwrap_or_default()` fold reached for — one source of
6727            // truth for the Erlang/OTP `one_for_one` half of Learn You
6728            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
6729            // supervisor canonical default that also backs the
6730            // [`crate::supervisor::Default for RestartStrategy`] impl
6731            // and the [`crate::supervisor::Default for SupervisorSpec`]
6732            // impl's struct-literal `estrategia` field, all now routed
6733            // through the same lifted constant. Prior to the lift the
6734            // composition site carried `.unwrap_or_default()` with no
6735            // compile-time link back to the shared OTP-canonical
6736            // default that the peer paired
6737            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
6738            // arm on the sibling `:max-restarts` axis routes through —
6739            // so a future rebrand of the OTP-canonical strategy default
6740            // (a widening to `rest_for_one` once the substrate
6741            // discovers startup-order-coupled child cohorts as the more
6742            // common shape, a per-cluster overlay the operator pins
6743            // through the MESH-COMPOSITION §III.2 supervision-canary
6744            // `:estrategia-overrides` roadmap slot) would have had to
6745            // migrate the paired `MaxIntensity` + `Period` halves
6746            // through the lifted constants and the `one_for_one` half
6747            // through a `RestartStrategy::default()` route in lockstep
6748            // or the three halves of the same OTP-canonical default
6749            // would silently drift out of pairing. Byte-parity against
6750            // the lifted constant closes the split. Pinned by
6751            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
6752            // in the tests module.
6753            estrategia: self
6754                .estrategia()
6755                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
6756            // Route the author-omitted `:max-restarts` arm through the
6757            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
6758            // typed `pub const` rather than the raw `5` literal — one
6759            // source of truth for the Erlang/OTP-canonical
6760            // `{intensity, 5, 60}` `MaxIntensity` default that also
6761            // backs the serde-side wire-format author-omitted arm on
6762            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
6763            // `#[serde(default = "default_max_restarts")]` and the
6764            // [`Default for SupervisorSpec`] impl's struct-literal
6765            // default field. Prior to the lift the composition site
6766            // carried a raw `5` with no compile-time link back to the
6767            // serde-side default, so a future rebrand of the OTP-
6768            // canonical default (a tightening to Elixir's `3`, a
6769            // widening to a per-cluster overlay the operator pins
6770            // through the MESH-COMPOSITION §III.2 supervision-canary
6771            // `:supervisor :max-restarts-overrides` roadmap slot)
6772            // would have had to be threaded through both open-coded
6773            // copies in lockstep or the wire-format author-omitted arm
6774            // and this view-construction author-omitted arm would
6775            // silently disagree on which restart-budget an omitted
6776            // `:max-restarts` resolves to. Pinned by
6777            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
6778            // in the tests module.
6779            max_restarts: self
6780                .max_restarts()
6781                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
6782            restart_window,
6783            children: self.children().to_vec(),
6784        })
6785    }
6786
6787    /// A minimal starter manifest emitted by `feira init`.
6788    #[must_use]
6789    pub fn template(nome: &str) -> String {
6790        format!(
6791            "(defcaixa\n  \
6792               :nome        {nome:?}\n  \
6793               :versao      \"0.1.0\"\n  \
6794               :kind        Biblioteca\n  \
6795               :edicao      \"2026\"\n  \
6796               :descricao   \"FIXME — describe this caixa\"\n  \
6797               :autores     ()\n  \
6798               :etiquetas   ()\n  \
6799               :deps        ()\n  \
6800               :deps-dev    ()\n  \
6801               :bibliotecas (\"lib/{nome}.lisp\"))\n"
6802        )
6803    }
6804
6805    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
6806    /// back after mutation (e.g. `feira add`).
6807    ///
6808    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
6809    /// The derive-macro `compile_from_sexp` path is the inverse, so any
6810    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
6811    #[must_use]
6812    pub fn to_lisp(&self) -> String {
6813        let json = serde_json::to_value(self).expect("Caixa serialize");
6814        let sexp = tatara_lisp::domain::json_to_sexp(&json);
6815        let tatara_lisp::Sexp::List(items) = sexp else {
6816            return format!("(defcaixa {sexp})\n");
6817        };
6818        let mut out = String::from("(defcaixa");
6819        let mut i = 0;
6820        while i + 1 < items.len() {
6821            out.push_str("\n  ");
6822            out.push_str(&items[i].to_string());
6823            out.push(' ');
6824            out.push_str(&items[i + 1].to_string());
6825            i += 2;
6826        }
6827        out.push_str(")\n");
6828        out
6829    }
6830}
6831
6832/// Errors raised by top-level [`Caixa`] validators that don't fit
6833/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
6834/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
6835/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
6836/// through every substrate-side artifact's `metadata.name` /
6837/// version derivation.
6838///
6839/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
6840/// doc-comment anticipates) can hold one of each per-axis error
6841/// family without reshaping individual diagnostics; this enum is
6842/// the first such per-Caixa-identity family.
6843#[derive(Debug, Error, PartialEq, Eq)]
6844pub enum ManifestError {
6845    #[error(
6846        ":nome is empty (every caixa must name itself; the value flows \
6847         into every K8s artifact's `metadata.name` derivation and into \
6848         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
6849    )]
6850    NomeEmpty,
6851    #[error(
6852        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
6853         apiserver enforces this rule on every `metadata.name` the \
6854         caixa's substrate-side renderers derive from `:nome` — the \
6855         `lareira-<nome>` Helm chart name, the programs.yaml entry \
6856         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
6857         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
6858         name; use a lowercase alphanumeric + hyphen identifier like \
6859         `\"checkout\"` or `\"cart-v2\"`)"
6860    )]
6861    NomeInvalid { nome: String, reason: String },
6862    #[error(
6863        ":nome {nome:?} overflows the joint-length budget on the canonical \
6864         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
6865         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
6866         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
6867         `chart:` slot, `caixa-tatara`'s `release_name` + \
6868         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
6869         joint name through the canonical `lareira_chart_name` helper, and \
6870         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
6871         DNS-1123 label cap on every chart-name-derived `metadata.name` \
6872         reject any joint name exceeding 63 bytes; the narrower \
6873         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
6874         arm gates the chart-name budget downstream renderers inherit)"
6875    )]
6876    NomeChartNameBudgetExceeded { nome: String, reason: String },
6877    #[error(
6878        ":versao is empty (every caixa must pin its own version; the value flows \
6879         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
6880         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
6881         `:latest` tags, the lacre closure's `concrete_versao`, and the \
6882         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
6883    )]
6884    VersaoEmpty,
6885    #[error(
6886        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
6887         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
6888         with optional `-prerelease` and `+build` — across every artifact derived \
6889         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
6890         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
6891         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
6892         and the `:upgrade-from :from` peers that match against this exact shape; \
6893         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
6894         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
6895         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
6896    )]
6897    VersaoInvalid { versao: String, reason: String },
6898    #[error(
6899        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
6900         substrate consumes this string through the shared \
6901         `supervisor::duration_codec` — the same parser routed via `with = \
6902         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
6903         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
6904         the canonical authoring form is `<integer><unit>` where the unit is one \
6905         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
6906         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
6907         Without this gate a malformed `:restart-window` silently produced a \
6908         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
6909         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
6910         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
6911         layer with the offending value named verbatim. Omit the slot entirely to \
6912         express \"no reset\"; carry a positive integer duration to express the \
6913         sliding window)"
6914    )]
6915    RestartWindowMalformed {
6916        restart_window: String,
6917        reason: String,
6918    },
6919    #[error(
6920        "{slot} entry is an empty path string — every {slot} entry must name \
6921         a file relative to the caixa root; omit the entry to omit the file \
6922         (the layout checker's `root.join(\"\")` resolves to the caixa root \
6923         itself, so an empty entry silently aliases the project root as a \
6924         declared {slot} file, then fails downstream at parse / existence \
6925         time with a diagnostic that names the root rather than the offending \
6926         entry)"
6927    )]
6928    CodePathEmpty { slot: &'static str },
6929    #[error(
6930        "{slot} entry {} is an absolute path — entries must be relative to \
6931         the caixa root, since `Path::join` replaces the base with an absolute \
6932         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
6933         outside the caixa root sandbox; rewrite the entry as a relative path \
6934         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
6935         `\"servicos/<name>.computeunit.yaml\"`)",
6936        path.display()
6937    )]
6938    CodePathAbsolute { slot: &'static str, path: PathBuf },
6939    #[error(
6940        "{slot} entry {} contains a `..` component — entries must not traverse \
6941         above the caixa root (the layout's `starts_with(<dir>)` fence on \
6942         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
6943         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
6944         has no such fence, so a leading `..` escapes unconditionally if the \
6945         resolved target happens to exist)",
6946        path.display()
6947    )]
6948    CodePathParentEscape { slot: &'static str, path: PathBuf },
6949    #[error(
6950        "{slot} entry {} does not terminate in the `.lisp` extension — every \
6951         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
6952         loop reads through `tatara_lisp::read` at parse time, so any other \
6953         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
6954         structurally a parser error far from the source caixa.lisp, with \
6955         no field naming the offending `:bibliotecas` entry. Pin a relative \
6956         path under the caixa root whose terminating extension is \
6957         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
6958         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
6959         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
6960         (33cc830) axes already carry through the same lifted \
6961         `is_lisp_extension` predicate",
6962        path.display()
6963    )]
6964    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
6965    #[error(
6966        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
6967         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
6968         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
6969         through `serde_yaml::from_str` at chart / FluxCD bundle render \
6970         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
6971         off-by-one-segment `.computeunit-yaml`, the editor-backup \
6972         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
6973         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
6974         source caixa.lisp, with no field naming the offending `:servicos` \
6975         entry. Pin a relative path under the caixa root whose terminating \
6976         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
6977         `\"servicos/<name>.computeunit.yaml\"`, \
6978         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
6979         contract the sibling `:bibliotecas` axis (64772a9) already carries \
6980         on the tatara-lisp-source axis through the peer lifted \
6981         `is_lisp_extension` predicate, here on the compound-suffix axis \
6982         `Path::extension` can't express on its own through the lifted \
6983         `is_computeunit_yaml_extension` predicate",
6984        path.display()
6985    )]
6986    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
6987    #[error(
6988        "{slot} entry {} appears more than once (the code-path list is \
6989         a set, not a multiset; every peer Vec-shaped author-supplied \
6990         list past validate is set-not-multiset — `:membros :caixa`, \
6991         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6992         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
6993         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
6994         code-path lists are the last Vec-shaped author-supplied slots on \
6995         the typed Caixa surface still admitting a duplicate entry. \
6996         `:bibliotecas` duplicates re-parse the same file at \
6997         `feira build` time and silently mask the author's intent to \
6998         declare a *second* biblioteca; `:exe` duplicates collide on the \
6999         flake `packages.<name>` derivation key at the future \
7000         `caixa-flake` materializer; `:servicos` duplicates surface as the \
7001         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
7002         rejection far from the source `caixa.lisp`. Drop the duplicate \
7003         or rename it to the actual second file intended)",
7004        path.display()
7005    )]
7006    CodePathDuplicate { slot: &'static str, path: PathBuf },
7007    #[error(
7008        ":etiquetas entry is empty (every tag must carry a non-empty \
7009         registry-search identifier; the empty entry has no operational \
7010         meaning — it indexes nothing in the future caixa-registry search \
7011         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
7012         with a no-op tag; omit the entry to express \"no tag on this \
7013         position\")"
7014    )]
7015    EtiquetaEmpty,
7016    #[error(
7017        ":etiquetas entry {etiqueta:?} appears more than once (the \
7018         registry-search tag set is a set, not a multiset; duplicate \
7019         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
7020         at chart render — a \"second wins / one silently disappears\" \
7021         shape divergent from every peer typed-graph set gate \
7022         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
7023         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
7024         duplicate or rename it to the actual tag intended)"
7025    )]
7026    EtiquetaDuplicate { etiqueta: String },
7027    #[error(
7028        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
7029         {reason} (the substrate consumes this string through the shared \
7030         `crate::render::is_chart_keyword_shape` predicate — the same \
7031         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
7032         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
7033         continuation. The canonical authoring shapes are short kebab-case \
7034         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
7035         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
7036         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
7037         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
7038         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
7039         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
7040         `\"mesh,http,grpc\"` — the author meant to author three separate \
7041         list entries; path-separator confusion `\"caixa/servico\"`; \
7042         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
7043         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
7044         `\"café\"` — every legitimate search tag is strict ASCII; \
7045         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
7046         passed `from_lisp` + `validate_etiquetas` + \
7047         `StandardLayout::verify` and landed in the rendered \
7048         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
7049         malformed search tag — Artifact Hub's keyword index + the future \
7050         caixa-registry's keyword index would either silently drop the \
7051         tag or fail to index it far from the source caixa.lisp; the gate \
7052         moves the diagnostic to the manifest layer with the offending \
7053         value named verbatim)"
7054    )]
7055    EtiquetaInvalid { etiqueta: String, reason: String },
7056    #[error(
7057        ":autores entry is empty (every maintainer must carry a non-empty \
7058         identifier; the empty entry has no operational meaning — it \
7059         identifies no one in the substrate's authorship index and renders \
7060         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
7061         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
7062         omit the entry to express \"no maintainer on this position\")"
7063    )]
7064    AutorEmpty,
7065    #[error(
7066        ":autores entry {autor:?} appears more than once (the maintainer \
7067         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
7068         `maintainers:` rendering does *no* dedup — duplicate entries \
7069         stack verbatim in `Chart.yaml` as two identical \
7070         `Maintainer {{ name, email: None }}` records, divergent from every \
7071         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
7072         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
7073         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
7074         rename it to the actual author intended)"
7075    )]
7076    AutorDuplicate { autor: String },
7077    #[error(
7078        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
7079         {reason} (the substrate consumes this string through the shared \
7080         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
7081         single-line-UTF-8 floor every realistic chart maintainer name carries: \
7082         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
7083         characters anywhere, Unicode bytes accepted. The canonical authoring \
7084         shapes are short single-line identifiers like `\"pleme-io\"`, \
7085         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
7086         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
7087         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
7088         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
7089         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
7090         records into one entry instead of splitting into one entry per author; \
7091         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
7092         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
7093         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7094         `validate_autores` + `StandardLayout::verify` and landed in the \
7095         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
7096         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
7097         round-trip — every chart-aware UI (`helm list`, `helm search`, \
7098         Artifact Hub maintainer index) would render the maintainer name in a \
7099         single-line column far from the source caixa.lisp; the gate moves the \
7100         diagnostic to the manifest layer with the offending value named \
7101         verbatim)"
7102    )]
7103    AutorInvalid { autor: String, reason: String },
7104    #[error(
7105        ":repositorio is the empty string (every published caixa names its \
7106         git source via a non-empty `:repositorio` locator — the value \
7107         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
7108         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
7109         `GitRepository.spec.url` via `caixa-flux`'s \
7110         `ClusterBundleOpts::for_caixa`; both consumers' \
7111         `Option::unwrap_or_else` fallbacks only fire when the slot is \
7112         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
7113         `url: \"\"` in the rendered artifacts and breaks at `helm \
7114         template` / FluxCD source-controller reconcile time far from the \
7115         source caixa.lisp; omit the slot entirely to defer to the \
7116         renderer's `https://github.com/pleme-io/<nome>` / \
7117         `caixa.nome`-derived fallback, or carry a canonical authoring \
7118         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
7119         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
7120         `\"file:///path\"`)"
7121    )]
7122    RepositorioEmpty,
7123    #[error(
7124        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
7125         (the substrate consumes this string through the shared \
7126         `crate::render::is_git_repo_url` predicate — the same parser the \
7127         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
7128         value through via `DepSource::validate`; the canonical authoring \
7129         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
7130         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
7131         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
7132         scp-style SSH form. Without this gate a malformed `:repositorio` \
7133         (whitespace from a paste-from-doc; control characters / CRLF \
7134         from a paste-from-multiline-doc; a leading `-` from a \
7135         CLI-argument-injection footgun; a missing `:` separator from a \
7136         bare `org/repo` shape git treats as a relative filesystem path) \
7137         silently landed in the rendered `Chart.yaml home:` and the \
7138         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
7139         FluxCD reconcile time far from the source caixa.lisp; the gate \
7140         moves the diagnostic to the manifest layer with the offending \
7141         value named verbatim)"
7142    )]
7143    RepositorioInvalid { repositorio: String, reason: String },
7144    #[error(
7145        ":descricao is the empty string (every published caixa names \
7146         its purpose via a non-empty `:descricao` summary — the value \
7147         flows verbatim into the rendered `lareira-<nome>` Helm \
7148         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
7149         `build_chart_yaml` and into the chart `README.md` header via \
7150         `build_readme`; both consumers' `Option::unwrap_or_else` \
7151         `caixa.nome`-derived fallbacks only fire when the slot is \
7152         `None`, so an empty `Some(\"\")` silently lands as \
7153         `description: \"\"` / a blank `README.md` header in the \
7154         rendered artifacts and breaks at `helm lint` time \
7155         (`WARNING [chart.metadata.description]: description is \
7156         required` on `apiVersion: v2` charts) far from the source \
7157         caixa.lisp; omit the slot entirely to defer to the \
7158         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
7159         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
7160         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
7161         Servico.\"`)"
7162    )]
7163    DescricaoEmpty,
7164    #[error(
7165        ":descricao {descricao:?} is not a valid chart-description shape: \
7166         {reason} (the substrate consumes this string through the shared \
7167         `crate::render::is_chart_description_shape` predicate — the same \
7168         single-line-UTF-8 floor every realistic chart description carries: \
7169         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
7170         characters anywhere, Unicode prose bytes accepted. The canonical \
7171         authoring shapes are short single-line summaries like `\"Canonical \
7172         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
7173         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
7174         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
7175         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
7176         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
7177         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
7178         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
7179         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7180         `validate_descricao` + `StandardLayout::verify` and landed in the \
7181         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
7182         field + `README.md` header paragraph as a YAML-illegal multi-line \
7183         scalar or a silently-trimmed whitespace round-trip — every \
7184         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
7185         render the description in a single-line column far from the source \
7186         caixa.lisp; the gate moves the diagnostic to the manifest layer \
7187         with the offending value named verbatim)"
7188    )]
7189    DescricaoInvalid { descricao: String, reason: String },
7190    #[error(
7191        ":licenca is the empty string (every published caixa names \
7192         its license via a non-empty `:licenca` SPDX expression — the \
7193         value flows verbatim into the rendered `lareira-<nome>` Helm \
7194         chart's `README.md` `## License` section via `caixa-helm`'s \
7195         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
7196         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
7197         only fires when the slot is `None`, so an empty `Some(\"\")` \
7198         silently lands as a bare trailing period in the rendered \
7199         chart `README.md` `License` section far from the source \
7200         caixa.lisp; omit the slot entirely to defer to the \
7201         renderer's `MIT` fallback, or carry a canonical SPDX \
7202         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
7203         `\"Apache-2.0 OR MIT\"`)"
7204    )]
7205    LicencaEmpty,
7206    #[error(
7207        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
7208         (the substrate consumes this string through the shared \
7209         `crate::render::is_spdx_expression_shape` predicate — the same \
7210         alphabet-floor parser every peer per-axis value-shape gate routes \
7211         its value through; the canonical authoring shapes are single \
7212         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
7213         compound expressions like `\"Apache-2.0 OR MIT\"`, \
7214         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
7215         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
7216         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
7217         like `\"LicenseRef-MyLicense\"` / \
7218         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
7219         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
7220         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
7221         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
7222         a smart-quote paste; underscore-instead-of-hyphen typo \
7223         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
7224         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
7225         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
7226         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
7227         `README.md` `## License` section + a future SPDX-aware \
7228         `Chart.yaml license:` emitter would refuse the value at \
7229         `helm lint` time far from the source caixa.lisp; the gate moves \
7230         the diagnostic to the manifest layer with the offending value \
7231         named verbatim)"
7232    )]
7233    LicencaInvalid { licenca: String, reason: String },
7234    #[error(
7235        ":edicao is the empty string (every published caixa names \
7236         its language edition via a non-empty `:edicao` value — the \
7237         edition determines the tatara-lisp macro surface + \
7238         compatibility flags the substrate applies when building \
7239         the caixa; the canonical `Caixa::template` scaffold every \
7240         `feira init` emits carries `:edicao \"2026\"` verbatim and \
7241         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
7242         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
7243         construction, so an empty `Some(\"\")` silently lands as a \
7244         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
7245         a future renderer-side consumer that folds it through \
7246         `Option::unwrap_or_else` will skip the fallback and pass the \
7247         empty edition through to the substrate's build-time edition \
7248         selector far from the source caixa.lisp; omit the slot \
7249         entirely to defer to the substrate's default edition, or \
7250         carry a canonical edition like `\"2026\"`)"
7251    )]
7252    EdicaoEmpty,
7253    #[error(
7254        ":edicao {edicao:?} is not a valid edition: {reason} (every \
7255         documented tatara-lisp edition is a 4-digit ASCII decimal \
7256         year — `\"2026\"` is the only edition currently minted; \
7257         future-introduced siblings will follow the same shape, peer \
7258         with Cargo's `[package] edition` grammar which every value \
7259         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
7260         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
7261         paste-from-doc footguns silently passed: a trailing space \
7262         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
7263         from a paste-from-multiline-doc, a fullwidth-keyboard \
7264         look-alike (`\"2026\"`), a free-form non-year value \
7265         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
7266         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
7267         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
7268         wrong-length numeric value (`\"26\"`, `\"202\"`, \
7269         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
7270         rendered caixa.lisp and broke at the substrate's \
7271         build-time edition selector far from the source caixa.lisp; \
7272         omit the slot entirely to defer to the substrate's default \
7273         edition, or carry a canonical 4-digit ASCII decimal year \
7274         like `\"2026\"`)"
7275    )]
7276    EdicaoInvalid { edicao: String, reason: String },
7277}
7278
7279#[cfg(test)]
7280mod tests {
7281    use super::*;
7282
7283    #[test]
7284    fn template_round_trips() {
7285        let src = Caixa::template("demo");
7286        let c = Caixa::from_lisp(&src).expect("template must parse");
7287        assert_eq!(c.nome, "demo");
7288        assert_eq!(c.versao, "0.1.0");
7289        assert_eq!(c.kind, CaixaKind::Biblioteca);
7290        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
7291        assert!(c.deps.is_empty());
7292        assert!(c.deps_dev.is_empty());
7293    }
7294
7295    #[test]
7296    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
7297        // Fail-before-pass-after pin on [`Caixa::nome`] +
7298        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
7299        // accessor projects the top-level manifest's per-`:nome` /
7300        // per-`:versao` [`String`] storage through the `pub const fn`
7301        // [`String::as_str`] (const-stable since Rust 1.87, well within
7302        // the workspace MSRV) — any future accidental downgrade to
7303        // non-`const` fails the corresponding `<name>_via_const_fn`
7304        // wrapper at caixa-core build time with E0015 (`cannot call
7305        // non-const method`), strictly stronger than a runtime
7306        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
7307        // scalar-accessor family pins on the sibling `const`-eval-
7308        // surface passes ([`crate::CaixaVersion::as_str`] at the
7309        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
7310        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
7311        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
7312        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
7313        // axis, [`crate::supervisor::ChildSpec::nome`] /
7314        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
7315        // M2 supervisor-tree axis,
7316        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
7317        // upgrade axis, [`crate::dep::Dep::nome`] /
7318        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
7319        // axis, and the per-`:contratos`
7320        // [`crate::aplicacao::WitContract::source`] /
7321        // [`crate::aplicacao::WitContract::destination`] /
7322        // [`crate::aplicacao::WitContract::world_ref`] trio the
7323        // sibling pin at 279823b already anchors).
7324        const fn nome_via_const_fn(c: &Caixa) -> &str {
7325            c.nome()
7326        }
7327        const fn versao_via_const_fn(c: &Caixa) -> &str {
7328            c.versao()
7329        }
7330        let src = Caixa::template("demo");
7331        let c = Caixa::from_lisp(&src).expect("template must parse");
7332        assert_eq!(nome_via_const_fn(&c), c.nome());
7333        assert_eq!(versao_via_const_fn(&c), c.versao());
7334        assert_eq!(c.nome(), "demo");
7335        assert_eq!(c.versao(), "0.1.0");
7336    }
7337
7338    #[test]
7339    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
7340        // Fail-before-pass-after pin on the five per-`Caixa`
7341        // `Option<String> → Option<&str>` scalar accessors
7342        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
7343        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
7344        // manifest's optional universal-axis surface, plus
7345        // [`Caixa::restart_window`] on the M2 supervisor-tree
7346        // per-`SupervisorSpec` peer raw-window-string projection axis).
7347        // Each accessor destructures the typed slot's `Option<String>`
7348        // storage through the `match &self.<field> { Some(s) =>
7349        // Some(s.as_str()), None => None }` shape — routing through
7350        // [`String::as_str`] (const-stable since Rust 1.87, well within
7351        // the workspace MSRV) rather than the non-const
7352        // [`Option::as_deref`] the pre-lift bodies carried — and any
7353        // future accidental downgrade to non-`const` fails the
7354        // corresponding `<name>_via_const_fn` wrapper at caixa-core
7355        // build time with E0015 (`cannot call non-const method`),
7356        // strictly stronger than a runtime `assert!` and strictly
7357        // stronger than a module-scope `const _: () = assert!(…)` pin
7358        // (which cannot be formed on a `&Caixa` fixture because the
7359        // type's `String` / `Option<String>` carriers rule out
7360        // `const`-context value construction; the `const fn` wrapper
7361        // is the load-bearing shape that side-steps the destructor-in-
7362        // const restriction on the value axis while still pinning the
7363        // `const`-fn posture on the callee — mirror of the sibling
7364        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7365        // pin's discipline verbatim on the peer non-`Option`
7366        // `String → &str` axis at the same struct).
7367        //
7368        // Peer of the sibling per-M2/M3-slot `Option<String> →
7369        // Option<&str>` accessor family pin
7370        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
7371        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
7372        // [`WitContract::subject`] / [`WitContract::slot`] on the
7373        // per-`:contratos` payload-carrier trio,
7374        // [`Placement::shard_key`] / [`Placement::affinity`] on the
7375        // per-`:placement` optional-scalar pair).
7376        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
7377            c.licenca()
7378        }
7379        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
7380            c.repositorio()
7381        }
7382        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
7383            c.descricao()
7384        }
7385        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
7386            c.edicao()
7387        }
7388        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
7389            c.restart_window()
7390        }
7391        // Sweep both the `Some`-carrying arm (author-declared slot,
7392        // the byte-string projection payload) and the `None`-carrying
7393        // arm (author-omitted slot, the default-path projection) on
7394        // every accessor so the `const fn` wrapper family pins each
7395        // axis's canonical two-arm partition through the same const
7396        // dispatch as the runtime path.
7397        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7398        c1.licenca = Some("MIT".to_string());
7399        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
7400        c1.descricao = Some("demo caixa".to_string());
7401        c1.edicao = Some("2024".to_string());
7402        c1.restart_window = Some("60s".to_string());
7403        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
7404        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
7405        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
7406        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
7407        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
7408        assert_eq!(c1.licenca(), Some("MIT"));
7409        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
7410        assert_eq!(c1.descricao(), Some("demo caixa"));
7411        assert_eq!(c1.edicao(), Some("2024"));
7412        assert_eq!(c1.restart_window(), Some("60s"));
7413        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7414        c2.licenca = None;
7415        c2.repositorio = None;
7416        c2.descricao = None;
7417        c2.edicao = None;
7418        c2.restart_window = None;
7419        assert_eq!(licenca_via_const_fn(&c2), None);
7420        assert_eq!(repositorio_via_const_fn(&c2), None);
7421        assert_eq!(descricao_via_const_fn(&c2), None);
7422        assert_eq!(edicao_via_const_fn(&c2), None);
7423        assert_eq!(restart_window_via_const_fn(&c2), None);
7424    }
7425
7426    #[test]
7427    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
7428        // Fail-before-pass-after pin on the two outer-[`Caixa`]
7429        // `Copy`-return accessors — [`Caixa::kind`] on the required
7430        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
7431        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
7432        // axis. Both accessors project a `Copy`-carrier field
7433        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
7434        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
7435        // `Option<RestartStrategy>: Copy`) by value through a bare
7436        // `self.<field>` field-access — no dispatch, no destructor, no
7437        // heap. Any future accidental downgrade to non-`const` fails
7438        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
7439        // build time with E0015 (`cannot call non-const method`),
7440        // strictly stronger than a runtime `assert!` and strictly
7441        // stronger than a module-scope `const _: () = assert!(…)` pin
7442        // (which cannot be formed on a `&Caixa` fixture because the
7443        // type's `String` / `Vec` / `Option<Composite>` carriers rule
7444        // out `const`-context value construction; the `const fn`
7445        // wrapper is the load-bearing shape that side-steps the
7446        // destructor-in-const restriction on the value axis while still
7447        // pinning the `const`-fn posture on the callee — mirror of the
7448        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7449        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
7450        // pins' discipline verbatim on the peer outer-`Caixa`
7451        // `String → &str` + `Option<String> → Option<&str>` axes at the
7452        // same struct).
7453        //
7454        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
7455        // family on the inner-altitude nested-spec typed-slot
7456        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
7457        // + [`crate::supervisor::ChildSpec::restart`] on the M2
7458        // supervisor-tree axis (pinned at 152c868), and
7459        // [`crate::aplicacao::Placement::estrategia`] +
7460        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
7461        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
7462        // unlifted altitude for the `Copy`-return-accessor family.
7463        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
7464            c.kind()
7465        }
7466        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
7467            c.estrategia()
7468        }
7469        // Sweep every arm of both discriminant partitions the accessors
7470        // fan on — every [`CaixaKind`] variant the six-arm required
7471        // discriminant carries (Biblioteca / Binario / Servico /
7472        // Supervisor / Aplicacao / Acao) and both arms of the
7473        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
7474        // (`Some(<strategy>)` on an author-declared supervisor and
7475        // `None` on the author-omitted default arm every non-Supervisor
7476        // caixa carries by `#[serde(default)]`) — so the `const fn`
7477        // wrapper family pins the closed-set partition through the
7478        // same const dispatch as the runtime path.
7479        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7480        c1.kind = CaixaKind::Servico;
7481        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
7482        assert_eq!(kind_via_const_fn(&c1), c1.kind());
7483        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
7484        assert_eq!(c1.kind(), CaixaKind::Servico);
7485        assert_eq!(
7486            c1.estrategia(),
7487            Some(crate::supervisor::RestartStrategy::OneForAll)
7488        );
7489        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7490        c2.kind = CaixaKind::Aplicacao;
7491        c2.estrategia = None;
7492        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
7493        assert_eq!(estrategia_via_const_fn(&c2), None);
7494        // Anchor the remaining discriminant arms so any future
7495        // reordering of [`CaixaKind`]'s six-variant enum surfaces
7496        // through the wrapper dispatch, not just through the direct
7497        // method call.
7498        for kind in [
7499            CaixaKind::Biblioteca,
7500            CaixaKind::Binario,
7501            CaixaKind::Servico,
7502            CaixaKind::Supervisor,
7503            CaixaKind::Aplicacao,
7504            CaixaKind::Acao,
7505        ] {
7506            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7507            c.kind = kind;
7508            assert_eq!(kind_via_const_fn(&c), kind);
7509        }
7510    }
7511
7512    #[test]
7513    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
7514        // Fail-before-pass-after pin on the five outer-[`Caixa`]
7515        // `Vec<String> → &[String]` slice-return accessors on the
7516        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
7517        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
7518        // Each body is a bare `self.<field>.as_slice()` dispatch through
7519        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7520        // the workspace MSRV). Any future accidental downgrade to
7521        // non-`const` fails the corresponding `<name>_via_const_fn`
7522        // wrapper at caixa-core build time with E0015 (`cannot call
7523        // non-const method`) — mirror of the sibling
7524        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
7525        // discipline on the peer outer-`Caixa` `Copy`-return accessor
7526        // axis, and peer of the sibling composite-carrier slice-return
7527        // pin below on the peer outer-`Caixa` composite-slice axis.
7528        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
7529            c.autores()
7530        }
7531        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
7532            c.etiquetas()
7533        }
7534        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
7535            c.bibliotecas()
7536        }
7537        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
7538            c.exe()
7539        }
7540        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
7541            c.servicos()
7542        }
7543        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
7544        // `servicos` — the template's `Vec::new()` default) and the
7545        // populated arm (mutated below) on every accessor so the
7546        // `const fn` wrapper family pins each axis's two-arm partition
7547        // through the same const dispatch as the runtime path.
7548        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
7549        // so that arm's "empty" fixture is the populated arm the
7550        // mutation sweep covers.
7551        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7552        assert!(autores_via_const_fn(&c_empty).is_empty());
7553        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
7554        assert!(exe_via_const_fn(&c_empty).is_empty());
7555        assert!(servicos_via_const_fn(&c_empty).is_empty());
7556        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7557        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
7558        c_full.etiquetas = vec!["compounding".to_string()];
7559        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
7560        c_full.exe = vec!["exe/cli.lisp".to_string()];
7561        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
7562        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
7563        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
7564        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
7565        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
7566        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
7567        assert_eq!(
7568            bibliotecas_via_const_fn(&c_full),
7569            &["lib/one.lisp", "lib/two.lisp"]
7570        );
7571        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
7572        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
7573        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
7574        assert_eq!(
7575            servicos_via_const_fn(&c_full),
7576            &["servicos/one.computeunit.yaml"]
7577        );
7578    }
7579
7580    #[test]
7581    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
7582        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
7583        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
7584        // / [`Caixa::deps_dev`] on the dep-graph axis,
7585        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
7586        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
7587        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
7588        // bare `self.<field>.as_slice()` dispatch through
7589        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7590        // the workspace MSRV) — peer of the sibling `String`-payload
7591        // slice-return pin above on the peer outer-`Caixa` universal-
7592        // axis surface, and peer of the sibling inner-composite-
7593        // altitude reference-return pin family
7594        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
7595        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
7596        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
7597        // (all pinned at 0b23e0f).
7598        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
7599            c.deps()
7600        }
7601        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
7602            c.deps_dev()
7603        }
7604        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
7605            c.upgrade_from()
7606        }
7607        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
7608            c.children()
7609        }
7610        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
7611            c.membros()
7612        }
7613        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
7614            c.contratos()
7615        }
7616        // Empty-arm sweep on all six composite-carrier axes — every
7617        // `Caixa::template` starts with `Vec::new()` on each.
7618        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7619        assert!(deps_via_const_fn(&c_empty).is_empty());
7620        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
7621        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
7622        assert!(children_via_const_fn(&c_empty).is_empty());
7623        assert!(membros_via_const_fn(&c_empty).is_empty());
7624        assert!(contratos_via_const_fn(&c_empty).is_empty());
7625        // Populate `:membros` / `:contratos` directly via struct literals
7626        // — the parser-side validation path fans on `:kind`-gated cross-
7627        // slot invariants irrelevant to the accessor dispatch under test.
7628        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7629        c_full.membros = vec![
7630            crate::aplicacao::Membro {
7631                caixa: "demo-a".to_string(),
7632                versao: "^0.1.0".to_string(),
7633            },
7634            crate::aplicacao::Membro {
7635                caixa: "demo-b".to_string(),
7636                versao: "^0.2.0".to_string(),
7637            },
7638        ];
7639        c_full.contratos = vec![crate::aplicacao::WitContract {
7640            de: "demo-a".to_string(),
7641            para: "demo-b".to_string(),
7642            wit: "wasi:http/proxy".to_string(),
7643            endpoint: Some("/edge".to_string()),
7644            subject: None,
7645            slot: None,
7646        }];
7647        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
7648        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
7649        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
7650        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
7651        // Alias-borrow check on the four remaining composite-carrier
7652        // slice-return arms — the wrapper's return borrow must alias the
7653        // caller's borrow so any future accessor re-routing that skips
7654        // the storage field surfaces through the assertion.
7655        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
7656        assert!(std::ptr::eq(
7657            deps_dev_via_const_fn(&c_full),
7658            c_full.deps_dev()
7659        ));
7660        assert!(std::ptr::eq(
7661            upgrade_from_via_const_fn(&c_full),
7662            c_full.upgrade_from()
7663        ));
7664        assert!(std::ptr::eq(
7665            children_via_const_fn(&c_full),
7666            c_full.children()
7667        ));
7668    }
7669
7670    #[test]
7671    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
7672        // Fail-before-pass-after pin on the six outer-[`Caixa`]
7673        // `Option<Composite> → Option<&Composite>` reference-return
7674        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
7675        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
7676        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
7677        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
7678        // Each body is a bare `self.<field>.as_ref()` dispatch through
7679        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
7680        // the workspace MSRV of 1.89). Any future accidental downgrade
7681        // to non-`const` fails the corresponding `<name>_via_const_fn`
7682        // wrapper at caixa-core build time with E0015 (`cannot call
7683        // non-const method`), strictly stronger than a runtime `assert!`
7684        // and strictly stronger than a module-scope `const _: () =
7685        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
7686        // because the type's `String` / `Vec` / `Option<Composite>`
7687        // carriers rule out `const`-context value construction; the
7688        // `const fn` wrapper is the load-bearing shape that side-steps
7689        // the destructor-in-const restriction on the value axis while
7690        // still pinning the `const`-fn posture on the callee — mirror
7691        // of the sibling
7692        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
7693        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
7694        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
7695        // pins' discipline verbatim on the peer outer-`Caixa` axes at
7696        // the same struct).
7697        //
7698        // Closes the outer-`Caixa` `Option<&Composite>` composite-
7699        // reference-return sub-family — the last unlifted altitude on
7700        // the outer-`Caixa` accessor-family const-eval surface after
7701        // the sibling `Copy`-return / universal-axis-`&str` /
7702        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
7703        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
7704        // 231a968 (the last of these pins the `Vec<T> → &[T]`
7705        // composite-slice arm the six accessors here close as their
7706        // `Option<Composite> → Option<&Composite>` peer). Peer of the
7707        // sibling inner-altitude nested-spec composite-reference-return
7708        // pin family — [`crate::AplicacaoSpec::politicas`] /
7709        // [`crate::AplicacaoSpec::placement`] /
7710        // [`crate::AplicacaoSpec::entrada`] on the inner
7711        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
7712        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
7713        // the same shape so both altitudes of the reference-return
7714        // discipline (per-`Caixa` outer-slot presence + per-
7715        // `AplicacaoSpec` inner-slot presence) route through one typed
7716        // const dispatch on the substrate primitive.
7717        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
7718            c.limits()
7719        }
7720        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
7721            c.behavior()
7722        }
7723        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
7724            c.politicas()
7725        }
7726        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
7727            c.placement()
7728        }
7729        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
7730            c.entrada()
7731        }
7732        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
7733            c.ci()
7734        }
7735        // Both-arm sweep on every accessor: the `None` author-omitted
7736        // arm (template default — no M2/M3/CI slot declared) and the
7737        // `Some(<composite>)` authored arm (mutated below via struct-
7738        // literal seeds, side-stepping the parser-side `:kind`-gated
7739        // cross-slot invariants irrelevant to the accessor dispatch
7740        // under test). Both arms route through the `const fn` wrapper
7741        // family so the two-arm `Option` partition is pinned through
7742        // the same const dispatch as the runtime path.
7743        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7744        assert!(limits_via_const_fn(&c_empty).is_none());
7745        assert!(behavior_via_const_fn(&c_empty).is_none());
7746        assert!(politicas_via_const_fn(&c_empty).is_none());
7747        assert!(placement_via_const_fn(&c_empty).is_none());
7748        assert!(entrada_via_const_fn(&c_empty).is_none());
7749        assert!(ci_via_const_fn(&c_empty).is_none());
7750        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7751        c_full.limits = Some(LimitsSpec::default());
7752        c_full.behavior = Some(crate::BehaviorSpec::default());
7753        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
7754        c_full.placement = Some(crate::aplicacao::Placement::default());
7755        c_full.entrada = Some(crate::aplicacao::Entrada {
7756            host: "demo.quero.cloud".to_string(),
7757            para: "demo".to_string(),
7758            paths: Vec::new(),
7759            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
7760        });
7761        c_full.ci = Some(canteiro_types::CiRun {
7762            workspace: "pleme-io".into(),
7763            repo: "caixa".into(),
7764            nodes: vec![],
7765        });
7766        assert!(limits_via_const_fn(&c_full).is_some());
7767        assert!(behavior_via_const_fn(&c_full).is_some());
7768        assert!(politicas_via_const_fn(&c_full).is_some());
7769        assert!(placement_via_const_fn(&c_full).is_some());
7770        assert!(entrada_via_const_fn(&c_full).is_some());
7771        assert!(ci_via_const_fn(&c_full).is_some());
7772        // Alias-borrow check on every arm: the wrapper's inner-`Option`
7773        // reference must alias the caller's borrow so any future accessor
7774        // re-routing that skips the storage field surfaces through the
7775        // assertion.
7776        assert!(std::ptr::eq(
7777            limits_via_const_fn(&c_full).unwrap(),
7778            c_full.limits().unwrap()
7779        ));
7780        assert!(std::ptr::eq(
7781            behavior_via_const_fn(&c_full).unwrap(),
7782            c_full.behavior().unwrap()
7783        ));
7784        assert!(std::ptr::eq(
7785            politicas_via_const_fn(&c_full).unwrap(),
7786            c_full.politicas().unwrap()
7787        ));
7788        assert!(std::ptr::eq(
7789            placement_via_const_fn(&c_full).unwrap(),
7790            c_full.placement().unwrap()
7791        ));
7792        assert!(std::ptr::eq(
7793            entrada_via_const_fn(&c_full).unwrap(),
7794            c_full.entrada().unwrap()
7795        ));
7796        assert!(std::ptr::eq(
7797            ci_via_const_fn(&c_full).unwrap(),
7798            c_full.ci().unwrap()
7799        ));
7800    }
7801
7802    #[test]
7803    fn register_populates_registry() {
7804        Caixa::register().expect("first register call in this test process must succeed");
7805        let kws = tatara_lisp::domain::registered_keywords();
7806        assert!(kws.contains(&"defcaixa"));
7807    }
7808
7809    #[test]
7810    fn to_lisp_round_trips() {
7811        let src = Caixa::template("demo");
7812        let c1 = Caixa::from_lisp(&src).unwrap();
7813        let emitted = c1.to_lisp();
7814        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
7815        assert_eq!(c1, c2);
7816    }
7817
7818    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
7819    //
7820    // The compounding pin: the variant stores only the typed
7821    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
7822    // (canonical keyword, description, consumer) routes through the enum's
7823    // own accessors at Display time. Prior to that closure the variant
7824    // carried each accessor's return value as a stored `&'static str`
7825    // snapshot alongside `dialeto`; a caller could construct the variant
7826    // with a snapshot that drifted from what `dialeto`'s accessors would
7827    // return, and every downstream user-facing projection would silently
7828    // disagree with the classification. Storing only the axis makes the
7829    // drift structurally impossible.
7830
7831    #[test]
7832    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
7833        // Single-field construction is the whole compounding shape — a
7834        // future re-introduction of a snapshot field (a `palavra_canonica:
7835        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
7836        // would re-open the drift surface and this construction would fail
7837        // to compile with "missing field" until every snapshot was seeded
7838        // at the call site again. The compile-time guarantee is the
7839        // invariant; the assertion below only witnesses that the
7840        // construction is well-formed after the closure.
7841        let err = LeituraError::DialetoEstrangeiro {
7842            dialeto: crate::dialeto::CaixaDialeto::Molde,
7843        };
7844        assert!(matches!(
7845            err,
7846            LeituraError::DialetoEstrangeiro {
7847                dialeto: crate::dialeto::CaixaDialeto::Molde,
7848            }
7849        ));
7850    }
7851
7852    #[test]
7853    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
7854        // For every foreign-dialect classification the variant surfaces —
7855        // [`crate::dialeto::CaixaDialeto::Molde`] and
7856        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
7857        // variants [`Caixa::from_lisp`] raises this error for — the
7858        // rendered [`std::fmt::Display`] byte-string must interpolate each
7859        // typed accessor's return verbatim. A future re-introduction of a
7860        // stored `&'static str` snapshot alongside `dialeto` that Display
7861        // read instead of the accessor would fail this pin as soon as the
7862        // two disagreed; a future accessor rebrand (a per-dialect
7863        // consumer rename, a canonical-keyword shift once the substrate
7864        // migration named in [`crate::dialeto`] completes) reaches every
7865        // consumer through one typed dispatch and this pin verifies the
7866        // display path is one of them.
7867        for d in [
7868            crate::dialeto::CaixaDialeto::Molde,
7869            crate::dialeto::CaixaDialeto::MoldePosicional,
7870        ] {
7871            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
7872            assert!(
7873                rendered.contains(d.palavra_canonica()),
7874                "Display must interpolate `dialeto.palavra_canonica()` \
7875                 verbatim — a stored snapshot would silently drift from \
7876                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
7877            );
7878            assert!(
7879                rendered.contains(d.descricao()),
7880                "Display must interpolate `dialeto.descricao()` verbatim. \
7881                 dialect: {d}, rendered: {rendered:?}"
7882            );
7883            assert!(
7884                rendered.contains(d.consumidor()),
7885                "Display must interpolate `dialeto.consumidor()` verbatim. \
7886                 dialect: {d}, rendered: {rendered:?}"
7887            );
7888        }
7889    }
7890
7891    #[test]
7892    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
7893        // The end-to-end pin the compounding closure defends: a
7894        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
7895        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
7896        // rendered Display byte-string names the Molde accessors'
7897        // returns verbatim. Any future path that constructed the variant
7898        // with a mismatched snapshot (a stored `palavra_canonica:
7899        // "defcaixa"` on a `Molde` classification) would land Display
7900        // pointing at `defcaixa` while the typed axis said `Molde` — the
7901        // exact drift the closure removes.
7902        let src = r#"
7903          (defcaixa
7904            :name "x"
7905            :kind :Biblioteca
7906            :ecosystem :rust-single-crate
7907            :package {:name "x" :version "0.1.0"})
7908        "#;
7909        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
7910        match err {
7911            LeituraError::DialetoEstrangeiro { dialeto } => {
7912                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
7913                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
7914                assert!(rendered.contains(dialeto.palavra_canonica()));
7915                assert!(rendered.contains(dialeto.consumidor()));
7916                assert!(rendered.contains(dialeto.descricao()));
7917            }
7918            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
7919        }
7920    }
7921
7922    #[test]
7923    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
7924        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
7925        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
7926        // positional-arity `defmolde` form written under a `(defcaixa …)`
7927        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
7928        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
7929        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
7930        // so no test exercised the positional-arity path through
7931        // `Caixa::from_lisp` specifically; the sibling
7932        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
7933        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
7934        // two arms route through the lifted
7935        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
7936        // typed predicate — the same predicate the pre-lift `foreign =>`
7937        // wildcard resolved to today — and this pin makes the
7938        // positional-arity arm's byte-shape at the gate explicit rather
7939        // than implied by wildcard-absorption. A future regression that
7940        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
7941        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
7942        // from the two-arity closure) would fail this pin at caixa-core
7943        // test time rather than surfacing far from the change as a
7944        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
7945        // …)` silently parsing past the derive.
7946        let src = r#"
7947          (defcaixa todoku-go
7948            :kind :Biblioteca
7949            :ecosystem :go
7950            :package {:name "todoku-go" :version "0.3.0"})
7951        "#;
7952        let err =
7953            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
7954        match err {
7955            LeituraError::DialetoEstrangeiro { dialeto } => {
7956                assert_eq!(
7957                    dialeto,
7958                    crate::dialeto::CaixaDialeto::MoldePosicional,
7959                    "DialetoEstrangeiro must carry the MoldePosicional \
7960                     variant verbatim — the positional-arity `defmolde` \
7961                     form under a `(defcaixa …)` head is the \
7962                     `MoldePosicional` arm's canonical byte-shape"
7963                );
7964                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
7965                assert!(
7966                    rendered.contains(dialeto.palavra_canonica()),
7967                    "Display must interpolate `dialeto.palavra_canonica()` \
7968                     verbatim on the MoldePosicional arm; rendered: \
7969                     {rendered:?}"
7970                );
7971                assert!(
7972                    rendered.contains(dialeto.consumidor()),
7973                    "Display must interpolate `dialeto.consumidor()` \
7974                     verbatim on the MoldePosicional arm; rendered: \
7975                     {rendered:?}"
7976                );
7977                assert!(
7978                    rendered.contains(dialeto.descricao()),
7979                    "Display must interpolate `dialeto.descricao()` \
7980                     verbatim on the MoldePosicional arm; rendered: \
7981                     {rendered:?}"
7982                );
7983            }
7984            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
7985        }
7986    }
7987
7988    #[test]
7989    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
7990        // Load-bearing byte-parity pin: for every arm in
7991        // [`crate::dialeto::CaixaDialeto::ALL`], the
7992        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
7993        // partition must agree with the lifted
7994        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
7995        // typed predicate — i.e. from_lisp raises
7996        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
7997        // `d.is_molde_family()` returns `true`, and does NOT raise
7998        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
7999        // predicate returns `false` (the arm's source falls through to
8000        // the derive — parses cleanly on
8001        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
8002        // [`LeituraError::Leitura`] on
8003        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
8004        //
8005        // Pre-lift the gate hand-rolled a three-arm match
8006        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
8007        // whose `foreign =>` wildcard expressed no compile-time link
8008        // back to the substrate primitive's arm-family; a future fifth
8009        // dialect the [`crate::dialeto`] module doc's "third dialect"
8010        // hazard actualises would fall silently onto the wildcard
8011        // regardless of whether it belonged to the `defmolde` family or
8012        // to a distinct `defcaixa`-family. Post-lift the partition
8013        // resolves through
8014        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
8015        // typed dispatch, and this pin refuses any future regression
8016        // that silently split the from_lisp partition from the typed
8017        // predicate — the two paths now migrate as one on any future
8018        // arm addition.
8019        //
8020        // Sibling in shape to the peer
8021        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
8022        // (e9d2315) that pins the same byte-parity between
8023        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
8024        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
8025        // `== "defmolde"` classifier — extends the discipline from the
8026        // two paths within the [`crate::dialeto`] primitive onto the
8027        // third external consumer of the `defmolde`-family partition
8028        // (the [`Caixa::from_lisp`] gate that raises
8029        // [`LeituraError::DialetoEstrangeiro`]).
8030        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
8031            (
8032                crate::dialeto::CaixaDialeto::Pacote,
8033                r#"
8034                  (defcaixa
8035                    :nome   "checkout"
8036                    :versao "0.1.0"
8037                    :kind   Biblioteca
8038                    :edicao "2026"
8039                    :descricao "canonical Pacote source"
8040                    :autores ()
8041                    :etiquetas ()
8042                    :deps ()
8043                    :deps-dev ()
8044                    :bibliotecas ("lib/checkout.lisp"))
8045                "#,
8046            ),
8047            (
8048                crate::dialeto::CaixaDialeto::Molde,
8049                r#"
8050                  (defcaixa
8051                    :name "base64"
8052                    :kind :Biblioteca
8053                    :ecosystem :rust-single-crate
8054                    :package {:name "base64" :version "0.22.1"}
8055                    :workflows [:auto-release])
8056                "#,
8057            ),
8058            (
8059                crate::dialeto::CaixaDialeto::MoldePosicional,
8060                r#"
8061                  (defcaixa todoku-go
8062                    :kind :Biblioteca
8063                    :ecosystem :go
8064                    :package {:name "todoku-go" :version "0.3.0"})
8065                "#,
8066            ),
8067            (
8068                crate::dialeto::CaixaDialeto::Desconhecido,
8069                r#"(defcaixa :licenca "MIT")"#,
8070            ),
8071        ];
8072
8073        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
8074        // must appear in the fixture table so the pin's arm-set stays
8075        // synchronised with the enum's arm-set. Fails at test time if a
8076        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
8077        // (with a corresponding `is_molde_family` return) forgot to
8078        // extend this fixture table with a canonical source for the new
8079        // arm — the pin cannot cover an arm it has no source for.
8080        for &expected in crate::dialeto::CaixaDialeto::ALL {
8081            assert!(
8082                fixtures.iter().any(|(d, _)| *d == expected),
8083                "fixture table must carry a canonical source for every \
8084                 CaixaDialeto arm; missing: {expected:?}"
8085            );
8086        }
8087
8088        for &(expected_dialect, src) in fixtures {
8089            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
8090                panic!(
8091                    "fixture source for {expected_dialect:?} must classify \
8092                     cleanly, got err: {err:?}"
8093                )
8094            });
8095            assert_eq!(
8096                classified, expected_dialect,
8097                "fixture source for {expected_dialect:?} must classify as \
8098                 {expected_dialect:?} (drift here defeats the byte-parity \
8099                 pin below — a source labelled for one arm but classifying \
8100                 as another would silently satisfy or violate the pin for \
8101                 the wrong reason)"
8102            );
8103
8104            let outcome = Caixa::from_lisp(src);
8105            match (expected_dialect.is_molde_family(), &outcome) {
8106                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
8107                    assert_eq!(
8108                        *dialeto, expected_dialect,
8109                        "DialetoEstrangeiro must carry the same typed arm \
8110                         the classifier returned — a drift here would let \
8111                         from_lisp raise the error while pointing at the \
8112                         wrong dialect (e.g. rejecting a \
8113                         MoldePosicional source as Molde). arm: \
8114                         {expected_dialect:?}"
8115                    );
8116                }
8117                (true, other) => panic!(
8118                    "arm {expected_dialect:?} has is_molde_family() = true \
8119                     so from_lisp must raise DialetoEstrangeiro carrying \
8120                     {expected_dialect:?}; got: {other:?}"
8121                ),
8122                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
8123                    "arm {expected_dialect:?} has is_molde_family() = false \
8124                     so from_lisp must NOT raise DialetoEstrangeiro; got \
8125                     one carrying: {dialeto:?}. This means the typed \
8126                     predicate and the from_lisp partition disagree on \
8127                     this arm — exactly the drift this pin refuses."
8128                ),
8129                (false, _) => {
8130                    // A non-molde arm's source falls through to the
8131                    // derive: Pacote sources parse to Ok(_); Desconhecido
8132                    // sources surface as LeituraError::Leitura from the
8133                    // derive's own unknown-keyword rejection. Either
8134                    // shape is acceptable here — the pin's promise is
8135                    // narrower: "no DialetoEstrangeiro on
8136                    // is_molde_family() == false".
8137                }
8138            }
8139        }
8140    }
8141
8142    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
8143
8144    #[test]
8145    fn limits_round_trip_via_json() {
8146        use crate::LimitsSpec;
8147        use std::time::Duration;
8148        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8149        c.limits = Some(LimitsSpec {
8150            memory: Some(64 * 1024 * 1024),
8151            fuel: Some(1_000_000),
8152            wall_clock: Some(Duration::from_secs(30)),
8153            cpu: Some(500),
8154        });
8155        let json = serde_json::to_string(&c).unwrap();
8156        assert!(json.contains("\"limits\""));
8157        assert!(json.contains("\"64MiB\""));
8158        assert!(json.contains("\"30s\""));
8159        assert!(json.contains("\"500m\""));
8160        let back: Caixa = serde_json::from_str(&json).unwrap();
8161        assert_eq!(c.limits, back.limits);
8162    }
8163
8164    #[test]
8165    fn behavior_round_trip_via_json() {
8166        use crate::BehaviorSpec;
8167        use std::path::PathBuf;
8168        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8169        c.behavior = Some(BehaviorSpec {
8170            on_init: Some(PathBuf::from("lib/init.lisp")),
8171            on_call: Some(PathBuf::from("lib/handlers.lisp")),
8172            ..Default::default()
8173        });
8174        let json = serde_json::to_string(&c).unwrap();
8175        let back: Caixa = serde_json::from_str(&json).unwrap();
8176        assert_eq!(c.behavior, back.behavior);
8177    }
8178
8179    #[test]
8180    fn upgrade_from_round_trip_via_json() {
8181        use crate::{UpgradeFromEntry, UpgradeInstruction};
8182        use std::path::PathBuf;
8183        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8184        c.upgrade_from = vec![UpgradeFromEntry {
8185            from: "0.1.0".into(),
8186            instructions: vec![
8187                UpgradeInstruction::LoadModule {
8188                    module: "demo".into(),
8189                },
8190                UpgradeInstruction::StateChange {
8191                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
8192                },
8193                UpgradeInstruction::SoftPurge {
8194                    module: "demo-old".into(),
8195                },
8196            ],
8197        }];
8198        let json = serde_json::to_string(&c).unwrap();
8199        let back: Caixa = serde_json::from_str(&json).unwrap();
8200        assert_eq!(c.upgrade_from, back.upgrade_from);
8201    }
8202
8203    #[test]
8204    fn supervisor_view_returns_typed_shape() {
8205        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8206        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8207        c.kind = CaixaKind::Supervisor;
8208        c.bibliotecas.clear();
8209        c.estrategia = Some(RestartStrategy::OneForOne);
8210        c.max_restarts = Some(5);
8211        c.restart_window = Some("60s".into());
8212        c.children = vec![ChildSpec {
8213            caixa: "worker".into(),
8214            versao: "^0.1".into(),
8215            restart: RestartPolicy::Permanent,
8216        }];
8217        let view = c.supervisor_view().expect("Supervisor kind has a view");
8218        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
8219        assert_eq!(view.max_restarts, 5);
8220        assert_eq!(
8221            view.restart_window,
8222            Some(std::time::Duration::from_secs(60))
8223        );
8224        assert_eq!(view.children.len(), 1);
8225        view.validate().unwrap();
8226    }
8227
8228    #[test]
8229    fn supervisor_view_none_for_non_supervisor_kinds() {
8230        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8231        assert!(c.supervisor_view().is_none());
8232    }
8233
8234    #[test]
8235    fn declared_mesh_slots_empty_for_bare_caixa() {
8236        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8237        assert!(c.declared_mesh_slots().is_empty());
8238    }
8239
8240    #[test]
8241    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
8242        use crate::{Entrada, Membro};
8243        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8244        // Set a non-adjacent pair (:membros + :entrada) to pin that the
8245        // canonical declaration order is preserved regardless of which
8246        // subset is populated.
8247        c.membros = vec![Membro {
8248            caixa: "a".into(),
8249            versao: "^0.1".into(),
8250        }];
8251        c.entrada = Some(Entrada {
8252            host: "x.example.com".into(),
8253            para: "a".into(),
8254            paths: vec![],
8255            port: 8080,
8256        });
8257        assert_eq!(
8258            c.declared_mesh_slots(),
8259            vec![
8260                crate::render::M3_AUTHOR_KEY_MEMBROS,
8261                crate::render::M3_AUTHOR_KEY_ENTRADA,
8262            ]
8263        );
8264    }
8265
8266    #[test]
8267    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8268        // Scalar-value pin: the five author-facing kebab-case labels the
8269        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
8270        // mesh slot axis, one arm per typed slot. Mirrors the peer
8271        // scalar-value pin the sibling
8272        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8273        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8274        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
8275        // carry (f49c8b0), so both altitudes of the typed-slot algebra
8276        // (per-Servico M2 + per-Aplicacao M3) share the same
8277        // "one canonical byte-string per arm" discipline. A future
8278        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
8279        // `:politicas` → `:policies`, `:placement` → `:distribution`,
8280        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
8281        // and every consumer that reaches for the label picks it up at
8282        // build time rather than at runtime as a downstream mismatch.
8283        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
8284        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
8285        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
8286        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
8287        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
8288    }
8289
8290    #[test]
8291    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
8292        // Production-through-const pin: the five per-arm labels the
8293        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
8294        // `Vec` route through the lifted
8295        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
8296        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
8297        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
8298        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
8299        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
8300        // declaration order. A future re-order or drift at the tagger
8301        // (a rename that reaches the tagger but not the const, or vice
8302        // versa) surfaces here at build time rather than at runtime as
8303        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
8304        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8305        // commit. Mirror of the peer
8306        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8307        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
8308        // axis.
8309        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
8310        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8311        c.membros = vec![Membro {
8312            caixa: "a".into(),
8313            versao: "^0.1".into(),
8314        }];
8315        c.contratos = vec![WitContract {
8316            de: "a".into(),
8317            para: "a".into(),
8318            wit: "wasi:http/proxy".into(),
8319            endpoint: Some("/x".into()),
8320            subject: None,
8321            slot: None,
8322        }];
8323        c.politicas = Some(MeshPolicy::default());
8324        c.placement = Some(Placement {
8325            estrategia: PlacementStrategy::Replicated,
8326            clusters: vec!["rio".into()],
8327            affinity: None,
8328            shard_key: None,
8329        });
8330        c.entrada = Some(Entrada {
8331            host: "x.example.com".into(),
8332            para: "a".into(),
8333            paths: vec![],
8334            port: 8080,
8335        });
8336        assert_eq!(
8337            c.declared_mesh_slots(),
8338            vec![
8339                crate::render::M3_AUTHOR_KEY_MEMBROS,
8340                crate::render::M3_AUTHOR_KEY_CONTRATOS,
8341                crate::render::M3_AUTHOR_KEY_POLITICAS,
8342                crate::render::M3_AUTHOR_KEY_PLACEMENT,
8343                crate::render::M3_AUTHOR_KEY_ENTRADA,
8344            ]
8345        );
8346    }
8347
8348    #[test]
8349    fn declared_supervisor_slots_empty_for_bare_caixa() {
8350        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8351        assert!(c.declared_supervisor_slots().is_empty());
8352    }
8353
8354    #[test]
8355    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
8356        use crate::RestartStrategy;
8357        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8358        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
8359        // that the canonical declaration order is preserved regardless
8360        // of which subset is populated.
8361        c.estrategia = Some(RestartStrategy::OneForOne);
8362        c.restart_window = Some("60s".into());
8363        assert_eq!(
8364            c.declared_supervisor_slots(),
8365            vec![
8366                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8367                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8368            ]
8369        );
8370    }
8371
8372    #[test]
8373    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8374        // Scalar-value pin: the four author-facing kebab-case labels the
8375        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
8376        // supervision-tree slot axis, one arm per typed slot. Mirrors the
8377        // peer scalar-value pins the sibling
8378        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
8379        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
8380        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
8381        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
8382        // top-level M3 slot consts carry, so all three kind-scoped
8383        // typed-slot-family author-facing-label axes route through one
8384        // canonical per-arm declaration. A future rebrand
8385        // (`:estrategia` → `:strategy` for English uniformity,
8386        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
8387        // `MaxIntensity` name, `:restart-window` → `:period` matching
8388        // OTP's `Period` name, `:children` → `:workers` matching Elixir
8389        // idiom) lands as an edit to exactly one const, and every
8390        // consumer that reaches for the label picks it up at build time
8391        // rather than at runtime as a downstream mismatch.
8392        assert_eq!(
8393            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8394            ":estrategia"
8395        );
8396        assert_eq!(
8397            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8398            ":max-restarts"
8399        );
8400        assert_eq!(
8401            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8402            ":restart-window"
8403        );
8404        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
8405    }
8406
8407    #[test]
8408    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
8409        // Production-through-const pin: the four per-arm labels the
8410        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
8411        // return `Vec` route through the lifted
8412        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
8413        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
8414        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
8415        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
8416        // canonical declaration order. A future re-order or drift at the
8417        // tagger (a rename that reaches the tagger but not the const, or
8418        // vice versa) surfaces here at build time rather than at runtime
8419        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
8420        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8421        // commit. Mirror of the peer
8422        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8423        // (f49c8b0) and
8424        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
8425        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
8426        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8427        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8428        c.estrategia = Some(RestartStrategy::OneForOne);
8429        c.max_restarts = Some(5);
8430        c.restart_window = Some("60s".into());
8431        c.children = vec![ChildSpec {
8432            caixa: "worker".into(),
8433            versao: "^0.1".into(),
8434            restart: RestartPolicy::Permanent,
8435        }];
8436        assert_eq!(
8437            c.declared_supervisor_slots(),
8438            vec![
8439                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8440                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8441                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8442                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
8443            ]
8444        );
8445    }
8446
8447    #[test]
8448    fn declared_servico_slots_empty_for_bare_caixa() {
8449        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8450        assert!(c.declared_servico_slots().is_empty());
8451    }
8452
8453    #[test]
8454    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
8455        use crate::{UpgradeFromEntry, UpgradeInstruction};
8456        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8457        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
8458        // the canonical declaration order is preserved regardless of
8459        // which subset is populated.
8460        c.limits = Some(crate::LimitsSpec {
8461            fuel: Some(1_000_000),
8462            ..Default::default()
8463        });
8464        c.upgrade_from = vec![UpgradeFromEntry {
8465            from: "0.1.0".into(),
8466            instructions: vec![UpgradeInstruction::Restart],
8467        }];
8468        assert_eq!(
8469            c.declared_servico_slots(),
8470            vec![
8471                crate::render::M2_AUTHOR_KEY_LIMITS,
8472                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8473            ]
8474        );
8475    }
8476
8477    #[test]
8478    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8479        // Scalar-value pin: the three author-facing kebab-case labels
8480        // the `(defcaixa … :<slot> (…))` surface admits on the M2
8481        // top-level slot axis, one arm per typed slot. Mirrors the peer
8482        // scalar-value pin the sibling renderer-side
8483        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
8484        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
8485        // consts carry, so both halves of the M2 top-level slot dual
8486        // axis (author-facing kebab-case label + renderer-side
8487        // camelCase overlay-container wire key) route through one
8488        // canonical per-arm declaration. A future rebrand
8489        // (`:limits` → `:sandbox` matching Lunatic per-process
8490        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
8491        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
8492        // matching Erlang's verbatim appup name) lands as an edit to
8493        // exactly one const, and every consumer that reaches for the
8494        // label picks it up at build time rather than at runtime as a
8495        // downstream mismatch.
8496        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
8497        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
8498        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
8499    }
8500
8501    #[test]
8502    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
8503        // Production-through-const pin: the three per-arm labels the
8504        // [`Caixa::declared_servico_slots`] tagger pushes onto its
8505        // return `Vec` route through the lifted
8506        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8507        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8508        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
8509        // declaration order. A future re-order or drift at the tagger
8510        // (a rename that reaches the tagger but not the const, or vice
8511        // versa) surfaces here at build time rather than at runtime as
8512        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
8513        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8514        // commit. Mirror of the peer
8515        // [`crate::behavior::BehaviorSpec::declared_slots`] production
8516        // tagger pin (889dc18) on the sibling per-callback axis.
8517        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
8518        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8519        c.limits = Some(crate::LimitsSpec {
8520            fuel: Some(1_000_000),
8521            ..Default::default()
8522        });
8523        c.behavior = Some(BehaviorSpec {
8524            on_init: Some(PathBuf::from("lib/init.lisp")),
8525            ..Default::default()
8526        });
8527        c.upgrade_from = vec![UpgradeFromEntry {
8528            from: "0.1.0".into(),
8529            instructions: vec![UpgradeInstruction::Restart],
8530        }];
8531        assert_eq!(
8532            c.declared_servico_slots(),
8533            vec![
8534                crate::render::M2_AUTHOR_KEY_LIMITS,
8535                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
8536                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8537            ]
8538        );
8539    }
8540
8541    #[test]
8542    fn existing_manifests_unaffected_by_new_optional_slots() {
8543        // Regression test: a caixa.lisp authored before M2 typed slots
8544        // should still parse + serialize cleanly. The bare `defcaixa`
8545        // emitted by `Caixa::template` has none of the new fields.
8546        let src = Caixa::template("legacy");
8547        let c = Caixa::from_lisp(&src).unwrap();
8548        assert!(c.limits.is_none());
8549        assert!(c.behavior.is_none());
8550        assert!(c.upgrade_from.is_empty());
8551        assert!(c.estrategia.is_none());
8552        assert!(c.children.is_empty());
8553
8554        // And to_lisp emits a manifest with the new slots in the
8555        // empty/default state — round-trippable.
8556        let emitted = c.to_lisp();
8557        let back = Caixa::from_lisp(&emitted).unwrap();
8558        assert_eq!(c, back);
8559    }
8560
8561    #[test]
8562    fn validate_deps_accepts_canonical_caixa() {
8563        // Positive control: the bare template — zero deps, zero
8564        // deps_dev — passes the gate trivially. A future axis added to
8565        // `Dep::validate` mustn't regress an empty-deps caixa to a
8566        // build error.
8567        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8568        c.validate_deps().unwrap();
8569    }
8570
8571    #[test]
8572    fn validate_deps_rejects_invalid_versao_in_deps() {
8573        // Fail-before-pass-after pin: a malformed `:deps :versao`
8574        // surfaces at validate_deps() time, not at lacre-resolve time.
8575        // Mirrors `rejects_invalid_membro_versao_requirement` and
8576        // `validate_rejects_invalid_child_versao_requirement` on the
8577        // other two `:versao` axes.
8578        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8579        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
8580        let err = c.validate_deps().unwrap_err();
8581        assert!(
8582            matches!(
8583                err,
8584                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8585                    if nome == "caixa-teia" && versao == "^bad-version"
8586            ),
8587            "got {err:?}"
8588        );
8589    }
8590
8591    #[test]
8592    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
8593        // Parity pin: `:deps-dev` must run through the same per-entry
8594        // validator as `:deps` — a typo in either axis surfaces the
8595        // same diagnostic. Without this leg, `:deps-dev` would be a
8596        // second-class citizen of the typed surface and an author
8597        // could land a build that passes validate_deps but fails at
8598        // `feira lock`-time when the dev-dep is resolved for a test
8599        // build.
8600        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8601        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
8602        let err = c.validate_deps().unwrap_err();
8603        assert!(
8604            matches!(
8605                err,
8606                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8607                    if nome == "tatara-check" && versao == "^^0.1"
8608            ),
8609            "got {err:?}"
8610        );
8611    }
8612
8613    #[test]
8614    fn validate_deps_runs_deps_before_deps_dev() {
8615        // Order pin: when both lists carry typos, the `:deps`
8616        // diagnostic surfaces first. The author's mental model is
8617        // "runtime deps are load-bearing; dev deps are scaffolding";
8618        // surfacing the runtime axis first matches that hierarchy.
8619        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8620        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
8621        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
8622        let err = c.validate_deps().unwrap_err();
8623        assert!(
8624            matches!(
8625                err,
8626                crate::dep::DepError::VersaoInvalid { ref nome, .. }
8627                    if nome == "runtime-dep"
8628            ),
8629            "expected `:deps` typo to surface first, got {err:?}"
8630        );
8631    }
8632
8633    #[test]
8634    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
8635        // Positive control sweep across both lists. Pin every
8636        // canonical Cargo-shaped form so a future tightening of the
8637        // accepted set surfaces here as a test failure (parity with
8638        // `accepts_canonical_membro_versao_forms` and
8639        // `validate_accepts_canonical_child_versao_forms`).
8640        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8641        c.deps = vec![
8642            Dep::simple("caret", "^0.1"),
8643            Dep::simple("tilde", "~0.1.2"),
8644            Dep::simple("exact", "0.1.0"),
8645            Dep::simple("wildcard", "*"),
8646            Dep::simple("multi-range", ">=0.1, <2"),
8647        ];
8648        c.deps_dev = vec![
8649            Dep::simple("dev-caret", "^0.1"),
8650            Dep::simple("dev-wildcard", "*"),
8651        ];
8652        c.validate_deps().unwrap();
8653    }
8654
8655    #[test]
8656    fn validate_deps_diagnostic_carries_offending_dep() {
8657        // Diagnostic-shape pin: the error names the offending entry's
8658        // `:nome` + `:versao` verbatim and carries a non-empty
8659        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
8660        // run can render the diagnostic without re-parsing.
8661        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8662        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
8663        let err = c.validate_deps().unwrap_err();
8664        let crate::dep::DepError::VersaoInvalid {
8665            nome,
8666            versao,
8667            reason,
8668        } = err
8669        else {
8670            panic!("expected VersaoInvalid, got other variant");
8671        };
8672        assert_eq!(nome, "caixa-teia");
8673        assert_eq!(versao, "not-a-req");
8674        assert!(
8675            !reason.is_empty(),
8676            "VersaoInvalid `reason` must carry the parser's wording verbatim"
8677        );
8678    }
8679
8680    #[test]
8681    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
8682        // Cross-axis pin: `validate_deps` walks both :deps and
8683        // :deps-dev through `Dep::validate`, and the new fonte gate
8684        // (`:tag` + `:branch` both set — the canonical "pin drift"
8685        // footgun) must surface from the :deps-dev arm with the
8686        // offending entry's :nome named. Pin the :deps-dev arm
8687        // explicitly so a future shortcut that only walks :deps
8688        // surfaces here as a regression.
8689        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8690        c.deps_dev = vec![Dep {
8691            nome: "dev-only".into(),
8692            versao: "^0.1".into(),
8693            fonte: Some(crate::DepSource::Git {
8694                repo: "github:p/x".into(),
8695                tag: Some("v1".into()),
8696                rev: None,
8697                branch: Some("main".into()),
8698            }),
8699            opcional: false,
8700            caracteristicas: vec![],
8701        }];
8702        let err = c.validate_deps().unwrap_err();
8703        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
8704            panic!("expected FontePinAmbiguous from :deps-dev walk");
8705        };
8706        assert_eq!(nome, "dev-only");
8707        assert!(pins.contains(":tag") && pins.contains(":branch"));
8708    }
8709
8710    #[test]
8711    fn validate_deps_rejects_empty_repo_in_deps() {
8712        // Parity pin on the :deps arm: an empty :repo on the runtime
8713        // deps list surfaces the same FonteRepoEmpty diagnostic the
8714        // dep.rs per-entry tests pin, naming the offending entry.
8715        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8716        c.deps = vec![Dep {
8717            nome: "runtime".into(),
8718            versao: "^0.1".into(),
8719            fonte: Some(crate::DepSource::Git {
8720                repo: String::new(),
8721                tag: Some("v1".into()),
8722                rev: None,
8723                branch: None,
8724            }),
8725            opcional: false,
8726            caracteristicas: vec![],
8727        }];
8728        let err = c.validate_deps().unwrap_err();
8729        assert!(
8730            matches!(
8731                err,
8732                crate::dep::DepError::FonteRepoEmpty { ref nome }
8733                    if nome == "runtime"
8734            ),
8735            "got {err:?}"
8736        );
8737    }
8738
8739    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
8740
8741    #[test]
8742    fn validate_deps_rejects_duplicate_nome_in_deps() {
8743        // Fail-before-pass-after pin: two `:deps` entries naming the same
8744        // caixa carry two `:versao` / `:fonte` / feature triples that the
8745        // caixa-resolver's lacre pipeline collapses (the second silently
8746        // overwrites the first at `concrete_versao`-resolve time). The
8747        // gate surfaces the duplicate at validate-time, naming the
8748        // offending caixa + the list, before the resolver-side silent
8749        // drop. Mirrors the peer typed-graph duplicate gates
8750        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
8751        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8752        c.deps = vec![
8753            Dep::simple("caixa-teia", "^0.1"),
8754            Dep::simple("caixa-teia", "^0.2"),
8755        ];
8756        let err = c.validate_deps().unwrap_err();
8757        assert!(
8758            matches!(
8759                err,
8760                crate::dep::DepError::DuplicateNome { ref nome, list }
8761                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8762            ),
8763            "got {err:?}"
8764        );
8765    }
8766
8767    #[test]
8768    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
8769        // Parity pin: `:deps-dev` runs through the same per-list
8770        // duplicate check as `:deps` — neither axis is a second-class
8771        // citizen of the set-not-multiset discipline.
8772        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8773        c.deps_dev = vec![
8774            Dep::simple("tatara-check", "*"),
8775            Dep::simple("tatara-check", "^0.1"),
8776        ];
8777        let err = c.validate_deps().unwrap_err();
8778        assert!(
8779            matches!(
8780                err,
8781                crate::dep::DepError::DuplicateNome { ref nome, list }
8782                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
8783            ),
8784            "got {err:?}"
8785        );
8786    }
8787
8788    #[test]
8789    fn validate_deps_accepts_cross_list_same_nome() {
8790        // The Cargo `[dependencies]` + `[dev-dependencies]` override
8791        // convention is preserved: a name appearing in *both* lists is
8792        // valid (the dev-pin overrides at test/dev time). Only
8793        // within-list duplicates are structurally incoherent — pin the
8794        // permissive cross-list semantics so a future shortcut that
8795        // collapses the two seen-sets into one surfaces here as a test
8796        // failure.
8797        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8798        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
8799        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
8800        c.validate_deps().unwrap();
8801    }
8802
8803    #[test]
8804    fn validate_deps_accepts_distinct_nome_in_both_lists() {
8805        // Positive control: distinct names within each list pass — the
8806        // gate's identity element on the canonical authoring shape.
8807        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8808        c.deps = vec![
8809            Dep::simple("caixa-teia", "^0.1"),
8810            Dep::simple("pleme-mesh", "*"),
8811        ];
8812        c.deps_dev = vec![
8813            Dep::simple("tatara-check", "*"),
8814            Dep::simple("dev-shim", "^0.1"),
8815        ];
8816        c.validate_deps().unwrap();
8817    }
8818
8819    #[test]
8820    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
8821        // Diagnostic-precedence pin: a malformed `:versao` on the
8822        // duplicating entry surfaces its narrower `VersaoInvalid`
8823        // diagnostic first, before the cross-entry duplicate gate fires
8824        // — the canonical "per-entry shape before cross-entry uniqueness"
8825        // precedence every peer set-not-multiset gate establishes
8826        // (`*_invalid_fires_before_duplicate_check` pins on
8827        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
8828        // `validate_upgrade_from`).
8829        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8830        c.deps = vec![
8831            Dep::simple("caixa-teia", "^0.1"),
8832            Dep::simple("caixa-teia", "^bad-version"),
8833        ];
8834        let err = c.validate_deps().unwrap_err();
8835        assert!(
8836            matches!(
8837                err,
8838                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8839                    if nome == "caixa-teia" && versao == "^bad-version"
8840            ),
8841            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
8842        );
8843    }
8844
8845    #[test]
8846    fn validate_deps_duplicate_diagnostic_names_first_collision() {
8847        // First-collision determinism pin: with three entries naming the
8848        // same caixa, the first colliding pair surfaces — not the last.
8849        // Mirrors the peer first-collision posture on every
8850        // duplicate-target gate
8851        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
8852        // — the second entry is the first collision; this gate uses the
8853        // same shape: the second entry's `:nome` lands in the diagnostic
8854        // because `seen.insert(first.nome)` already populated the set).
8855        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8856        c.deps = vec![
8857            Dep::simple("caixa-teia", "^0.1"),
8858            Dep::simple("caixa-teia", "^0.2"),
8859            Dep::simple("caixa-teia", "^0.3"),
8860        ];
8861        let err = c.validate_deps().unwrap_err();
8862        // The diagnostic carries the offending caixa name; the
8863        // implementation surfaces on the *second* entry (the first
8864        // collision), so the test pins the `:nome` value.
8865        assert!(
8866            matches!(
8867                err,
8868                crate::dep::DepError::DuplicateNome { ref nome, list }
8869                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8870            ),
8871            "got {err:?}"
8872        );
8873    }
8874
8875    #[test]
8876    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
8877        // Cross-list precedence pin: when both lists carry duplicates,
8878        // the `:deps` diagnostic surfaces first — same author-mental-
8879        // model ordering the `validate_deps_runs_deps_before_deps_dev`
8880        // pin establishes for malformed `:versao` (runtime axis before
8881        // dev axis).
8882        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8883        c.deps = vec![
8884            Dep::simple("runtime-dep", "^0.1"),
8885            Dep::simple("runtime-dep", "^0.2"),
8886        ];
8887        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
8888        let err = c.validate_deps().unwrap_err();
8889        assert!(
8890            matches!(
8891                err,
8892                crate::dep::DepError::DuplicateNome { ref nome, list }
8893                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8894            ),
8895            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
8896        );
8897    }
8898
8899    #[test]
8900    fn validate_deps_empty_lists_pass_duplicate_gate() {
8901        // Empty-set identity pin: the bare template (zero deps, zero
8902        // deps_dev) passes the duplicate gate as the gate's identity
8903        // element. A future tighten that conflates "empty" with
8904        // "missing" would regress this baseline.
8905        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8906        c.validate_deps().unwrap();
8907    }
8908
8909    #[test]
8910    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
8911        // Diagnostic-shape pin: the `list:` field tags which list the
8912        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
8913        // `feira lint` run can route the author to the right block in
8914        // their caixa.lisp without re-deriving the list from context.
8915        // Same self-locating shape every peer per-axis diagnostic
8916        // already exposes.
8917        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8918        c.deps_dev = vec![
8919            Dep::simple("dev-thing", "*"),
8920            Dep::simple("dev-thing", "^0.1"),
8921        ];
8922        let err = c.validate_deps().unwrap_err();
8923        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
8924            panic!("expected DuplicateNome from :deps-dev walk");
8925        };
8926        assert_eq!(nome, "dev-thing");
8927        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
8928    }
8929
8930    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
8931
8932    #[test]
8933    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
8934        // Thread-through pin on `:deps`: the per-entry
8935        // `Dep::validate_caracteristicas` gate fires inside
8936        // `Caixa::validate_deps`'s linear walk, so a malformed feature
8937        // list on any `:deps` entry surfaces as a `DepError` from
8938        // `validate_deps` — the same reachability shape every per-entry
8939        // `Dep::validate` arm threads through. Without this pin a future
8940        // shortcut that skips the per-entry `Dep::validate` call on the
8941        // cross-entry-uniqueness path would mask the within-entry
8942        // `:caracteristicas` gates.
8943        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8944        c.deps = vec![Dep {
8945            nome: "caixa-teia".into(),
8946            versao: "^0.1".into(),
8947            fonte: None,
8948            opcional: false,
8949            caracteristicas: vec!["http".into(), "http".into()],
8950        }];
8951        let err = c.validate_deps().unwrap_err();
8952        let crate::dep::DepError::CaracteristicaDuplicate {
8953            nome,
8954            caracteristica,
8955        } = err
8956        else {
8957            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
8958        };
8959        assert_eq!(nome, "caixa-teia");
8960        assert_eq!(caracteristica, "http");
8961    }
8962
8963    #[test]
8964    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
8965        // Peer thread-through pin on `:deps-dev`: same reachability as
8966        // the `:deps` arm above, on the dev-only authoring axis. Pins
8967        // that the `validate_deps` walk visits both lists' per-entry
8968        // gates uniformly. The empty-feature arm carries here so both
8969        // new `:caracteristicas` arms are surfaced via at least one
8970        // `validate_deps` thread-through.
8971        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8972        c.deps_dev = vec![Dep {
8973            nome: "caixa-teia".into(),
8974            versao: "^0.1".into(),
8975            fonte: None,
8976            opcional: false,
8977            caracteristicas: vec![String::new()],
8978        }];
8979        let err = c.validate_deps().unwrap_err();
8980        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
8981            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
8982        };
8983        assert_eq!(nome, "caixa-teia");
8984    }
8985
8986    #[test]
8987    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
8988        // Thread-through pin on `:deps`: the per-entry
8989        // `Dep::validate_caracteristicas` value-shape gate (lifted via
8990        // `crate::render::is_cargo_feature_name`) fires inside
8991        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
8992        // a structurally invalid feature name on any `:deps` entry
8993        // surfaces as `DepError::CaracteristicaInvalid` from
8994        // `validate_deps` — the same reachability shape every per-entry
8995        // `Dep::validate` arm threads through. Without this pin a
8996        // future shortcut that skips the per-entry `Dep::validate` call
8997        // on the cross-entry-uniqueness path would mask the within-
8998        // entry `:caracteristicas` value-shape gate.
8999        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9000        c.deps = vec![Dep {
9001            nome: "caixa-teia".into(),
9002            versao: "^0.1".into(),
9003            fonte: None,
9004            opcional: false,
9005            caracteristicas: vec!["+http".into()],
9006        }];
9007        let err = c.validate_deps().unwrap_err();
9008        let crate::dep::DepError::CaracteristicaInvalid {
9009            nome,
9010            caracteristica,
9011            ..
9012        } = err
9013        else {
9014            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
9015        };
9016        assert_eq!(nome, "caixa-teia");
9017        assert_eq!(caracteristica, "+http");
9018    }
9019
9020    #[test]
9021    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
9022        // Peer thread-through pin on `:deps-dev`: same reachability as
9023        // the `:deps` arm above, on the dev-only authoring axis. The
9024        // `http/json` shape carries here so the segment-separator
9025        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
9026        // confusion footgun) is surfaced via the cross-entry walk too —
9027        // pinning that the `:deps-dev` list visits the same per-entry
9028        // value-shape gate as the `:deps` list.
9029        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9030        c.deps_dev = vec![Dep {
9031            nome: "caixa-teia".into(),
9032            versao: "^0.1".into(),
9033            fonte: None,
9034            opcional: false,
9035            caracteristicas: vec!["http/json".into()],
9036        }];
9037        let err = c.validate_deps().unwrap_err();
9038        let crate::dep::DepError::CaracteristicaInvalid {
9039            nome,
9040            caracteristica,
9041            ..
9042        } = err
9043        else {
9044            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
9045        };
9046        assert_eq!(nome, "caixa-teia");
9047        assert_eq!(caracteristica, "http/json");
9048    }
9049
9050    #[test]
9051    fn to_lisp_preserves_deps() {
9052        let src = r#"
9053(defcaixa
9054  :nome "x"
9055  :versao "0.1.0"
9056  :kind Biblioteca
9057  :deps ((:nome "a" :versao "^0.1")
9058         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
9059"#;
9060        let c1 = Caixa::from_lisp(src).unwrap();
9061        let emitted = c1.to_lisp();
9062        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
9063        assert_eq!(c1.deps, c2.deps);
9064    }
9065
9066    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
9067
9068    fn caixa_with_nome(nome: &str) -> Caixa {
9069        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
9070        c.nome = nome.to_string();
9071        c
9072    }
9073
9074    #[test]
9075    fn validate_nome_accepts_canonical_template() {
9076        // Positive control: the bare `feira init`-style template's
9077        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
9078        // not regress this baseline shape. A future tightening of the
9079        // accepted set surfaces here as a test failure first.
9080        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9081        c.validate_nome().unwrap();
9082    }
9083
9084    #[test]
9085    fn validate_nome_accepts_canonical_forms() {
9086        // Positive-set sweep: each realistic caixa-name shape the K8s
9087        // apiserver accepts as a `metadata.name` label must pass —
9088        // single-word, hyphen-joined, version-suffixed, single-char,
9089        // two-char, digit-start (DNS-1123 allows this; the stricter
9090        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
9091        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
9092        // the peer member-name axis.
9093        for nome in [
9094            "checkout",
9095            "cart-v2",
9096            "a",
9097            "db",
9098            "3rd-party-shim",
9099            "payment-retry",
9100            "0",
9101        ] {
9102            caixa_with_nome(nome)
9103                .validate_nome()
9104                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
9105        }
9106    }
9107
9108    #[test]
9109    fn validate_nome_rejects_empty() {
9110        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9111        // an empty `:nome` (the derive macro stores the raw String);
9112        // the gate's empty arm names the offending axis with a narrower
9113        // diagnostic than the `NomeInvalid` parse arm would emit.
9114        let c = caixa_with_nome("");
9115        let err = c.validate_nome().unwrap_err();
9116        assert_eq!(err, ManifestError::NomeEmpty);
9117    }
9118
9119    #[test]
9120    fn validate_nome_rejects_uppercase() {
9121        // The canonical "I copied the TitleCase display name verbatim"
9122        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
9123        // admission on every derived artifact (Helm chart, ComputeUnit,
9124        // CNP, HTTPRoute, label values); the gate moves the diagnostic
9125        // to the source `caixa.lisp` and the reason suggests the
9126        // lowercased fix verbatim.
9127        let c = caixa_with_nome("MyApp");
9128        let err = c.validate_nome().unwrap_err();
9129        let ManifestError::NomeInvalid { nome, reason } = err else {
9130            panic!("expected NomeInvalid for uppercase :nome");
9131        };
9132        assert_eq!(nome, "MyApp");
9133        assert!(
9134            reason.contains("uppercase") && reason.contains("myapp"),
9135            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
9136        );
9137    }
9138
9139    #[test]
9140    fn validate_nome_rejects_underscore() {
9141        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
9142        // `_`; the apiserver rejects on admission across every derived
9143        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
9144        // and `:children :caixa` (31bfa43).
9145        let c = caixa_with_nome("my_app");
9146        let err = c.validate_nome().unwrap_err();
9147        assert!(
9148            matches!(
9149                err,
9150                ManifestError::NomeInvalid { ref nome, ref reason }
9151                    if nome == "my_app" && reason.contains('_')
9152            ),
9153            "got {err:?}"
9154        );
9155    }
9156
9157    #[test]
9158    fn validate_nome_rejects_dot() {
9159        // A `:nome` is a single DNS-1123 label, not a subdomain. The
9160        // "I want to namespace with `.`" footgun the gate redirects to
9161        // `-` via the shared predicate's reason wording.
9162        let c = caixa_with_nome("team.app");
9163        let err = c.validate_nome().unwrap_err();
9164        assert!(
9165            matches!(
9166                err,
9167                ManifestError::NomeInvalid { ref nome, ref reason }
9168                    if nome == "team.app" && reason.contains('.')
9169            ),
9170            "got {err:?}"
9171        );
9172    }
9173
9174    #[test]
9175    fn validate_nome_rejects_leading_hyphen() {
9176        // DNS-1123 boundary rule: the label must start with an ASCII
9177        // alphanumeric. Pin the leading-`-` arm explicitly.
9178        let c = caixa_with_nome("-app");
9179        let err = c.validate_nome().unwrap_err();
9180        assert!(
9181            matches!(
9182                err,
9183                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
9184            ),
9185            "got {err:?}"
9186        );
9187    }
9188
9189    #[test]
9190    fn validate_nome_rejects_trailing_hyphen() {
9191        // Symmetric arm of the boundary rule, pinned separately so a
9192        // future relaxation that only checks the leading position
9193        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
9194        // and `_with_trailing_hyphen` on the supervisor / aplicacao
9195        // axes.
9196        let c = caixa_with_nome("app-");
9197        let err = c.validate_nome().unwrap_err();
9198        assert!(
9199            matches!(
9200                err,
9201                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
9202            ),
9203            "got {err:?}"
9204        );
9205    }
9206
9207    #[test]
9208    fn validate_nome_rejects_unicode() {
9209        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
9210        // bytes are rejected by the K8s apiserver on every name axis.
9211        let c = caixa_with_nome("café");
9212        let err = c.validate_nome().unwrap_err();
9213        assert!(
9214            matches!(
9215                err,
9216                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
9217            ),
9218            "got {err:?}"
9219        );
9220    }
9221
9222    #[test]
9223    fn validate_nome_rejects_whitespace() {
9224        // The paste-from-sketch / paste-from-spec footgun. Internal
9225        // whitespace is rejected by every K8s name axis.
9226        let c = caixa_with_nome("my app");
9227        let err = c.validate_nome().unwrap_err();
9228        assert!(
9229            matches!(
9230                err,
9231                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
9232            ),
9233            "got {err:?}"
9234        );
9235    }
9236
9237    #[test]
9238    fn validate_nome_rejects_too_long() {
9239        // 64-byte boundary pin: the K8s apiserver rejects any
9240        // `metadata.name` over 63 bytes at admission; the diagnostic
9241        // names both the 63-byte cap and the actual length so the
9242        // author can shorten in one edit. Mirrors `_too_long` on the
9243        // peer member-/cluster-/child-name axes.
9244        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
9245        let c = caixa_with_nome(&over);
9246        let err = c.validate_nome().unwrap_err();
9247        let ManifestError::NomeInvalid { nome, reason } = err else {
9248            panic!("expected NomeInvalid for over-cap :nome");
9249        };
9250        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
9251        assert!(
9252            reason.contains("63") && reason.contains("64"),
9253            "diagnostic must name the cap + actual length, got {reason:?}"
9254        );
9255    }
9256
9257    #[test]
9258    fn nome_max_length_validates() {
9259        // The 63-byte cap exactly — the boundary-accepting case pinned
9260        // alongside `validate_nome_rejects_too_long` so a future cap
9261        // shift surfaces both arms simultaneously. Mirrors
9262        // `membro_caixa_max_length_validates`,
9263        // `placement_cluster_max_length_validates`,
9264        // `child_caixa_max_length_validates`.
9265        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9266        caixa_with_nome(&at_cap).validate_nome().unwrap();
9267    }
9268
9269    #[test]
9270    fn nome_empty_takes_precedence_over_invalid() {
9271        // Order pin: the empty arm fires before the predicate is
9272        // consulted. Empty < invalid in self-locating-ness — the
9273        // narrower `NomeEmpty` diagnostic doesn't carry a useless
9274        // `nome: ""` reference into the parser-shaped reason. Mirrors
9275        // `membro_caixa_empty_takes_precedence_over_invalid` on the
9276        // peer axis (3f9d7a0).
9277        let c = caixa_with_nome("");
9278        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
9279    }
9280
9281    #[test]
9282    fn nome_invalid_diagnostic_carries_offending_nome() {
9283        // Diagnostic-shape pin: the error names the offending `:nome`
9284        // verbatim with a non-empty parser-shaped reason, so a `feira
9285        // lint` run can render the diagnostic without re-parsing.
9286        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
9287        let c = caixa_with_nome("MyApp");
9288        let err = c.validate_nome().unwrap_err();
9289        let ManifestError::NomeInvalid { nome, reason } = err else {
9290            panic!("expected NomeInvalid variant");
9291        };
9292        assert_eq!(nome, "MyApp");
9293        assert!(
9294            !reason.is_empty(),
9295            "NomeInvalid `reason` must carry the predicate's wording verbatim"
9296        );
9297    }
9298
9299    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
9300    //
9301    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
9302    // via DNS-1123; this second-axis gate caps the joint
9303    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
9304    // canonical [`crate::lareira_chart_name`] helper's doc comment
9305    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
9306    // "the M4 admission webhook will pin the joint-length invariant
9307    // when it lands". These tests pin it at the manifest-validate
9308    // layer instead, fail-before-pass-after on the 56-byte boundary.
9309
9310    #[test]
9311    fn validate_nome_chart_name_budget_accepts_canonical_template() {
9312        // Positive control: the bare `feira init`-style template's
9313        // `:nome` ("demo") sits far below the cap; the gate must not
9314        // regress this baseline. Same shape every peer
9315        // value-shape-gate baseline pin uses.
9316        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9317        c.validate_nome_chart_name_budget().unwrap();
9318    }
9319
9320    #[test]
9321    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
9322        // Positive-set sweep across the canonical author surface every
9323        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
9324        // `worker`, the `checkout-aplicacao` example members, the
9325        // `example-attest` caixa-tatara fixture). Every value sits
9326        // far below the 55-byte per-`:nome` budget. Same shape every
9327        // peer per-axis baseline pin uses.
9328        for nome in [
9329            "hello-rio",
9330            "cart",
9331            "checkout",
9332            "worker",
9333            "example-attest",
9334            "demo",
9335            "a",
9336        ] {
9337            caixa_with_nome(nome)
9338                .validate_nome_chart_name_budget()
9339                .unwrap_or_else(|e| {
9340                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
9341                });
9342        }
9343    }
9344
9345    #[test]
9346    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
9347        // Boundary-accepting case at the 55-byte per-`:nome` budget —
9348        // the joint chart name is exactly 63 bytes, the DNS-1123 label
9349        // cap. Pinned alongside the rejecting-arm test so a future cap
9350        // shift surfaces both arms simultaneously. Mirrors
9351        // `nome_max_length_validates` on the peer bare-`:nome` axis.
9352        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
9353        caixa_with_nome(&at_cap)
9354            .validate_nome_chart_name_budget()
9355            .unwrap();
9356    }
9357
9358    #[test]
9359    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
9360        // Fail-before-pass-after pin on the 56-byte boundary: the
9361        // smallest `:nome` length that overflows the joint chart-name
9362        // cap. The inner [`is_dns_1123_label`] gate
9363        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
9364        // this gate it silently passed the manifest-validate cascade
9365        // and surfaced as a `helm lint` / apiserver rejection on the
9366        // rendered chart name far from the source `caixa.lisp`, with
9367        // no field naming the overflow. With this gate the diagnostic
9368        // names the offending `:nome` verbatim alongside the rendered
9369        // chart name and the budget, so the author can shorten in one
9370        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
9371        // bare-`:nome` axis.
9372        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9373        let c = caixa_with_nome(&over);
9374        let err = c.validate_nome_chart_name_budget().unwrap_err();
9375        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9376            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
9377        };
9378        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9379        assert_eq!(nome, over);
9380        assert!(
9381            reason.contains("63") && reason.contains("64") && reason.contains("55"),
9382            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
9383             and the per-`:nome` budget (55), got {reason:?}"
9384        );
9385    }
9386
9387    #[test]
9388    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
9389        // The 63-byte `:nome` boundary — passes the bare-`:nome`
9390        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
9391        // joint chart name that overflows the DNS-1123 label cap
9392        // structurally. The most stringent fail-before-pass-after
9393        // surface: every `:nome` in the 56..=63-byte range passed the
9394        // prior cascade and broke at admission.
9395        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9396        let c = caixa_with_nome(&bare_max);
9397        // The bare-`:nome` gate accepts the 63-byte length.
9398        c.validate_nome().unwrap();
9399        // The new joint-length gate rejects it.
9400        let err = c.validate_nome_chart_name_budget().unwrap_err();
9401        assert!(
9402            matches!(
9403                err,
9404                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
9405                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
9406            ),
9407            "got {err:?}"
9408        );
9409    }
9410
9411    #[test]
9412    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
9413        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
9414        // name appears verbatim in the diagnostic so the author sees
9415        // exactly the string the apiserver / `helm lint` would have
9416        // rejected — no re-derivation required to grep the source.
9417        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
9418        // on the bare-`:nome` axis.
9419        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
9420        let c = caixa_with_nome(&over);
9421        let err = c.validate_nome_chart_name_budget().unwrap_err();
9422        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9423            panic!("expected NomeChartNameBudgetExceeded variant");
9424        };
9425        assert_eq!(nome, over);
9426        let expected_chart = crate::lareira_chart_name(&over);
9427        assert!(
9428            reason.contains(&expected_chart),
9429            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
9430             got {reason:?}"
9431        );
9432        assert!(
9433            reason.contains("lareira-"),
9434            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
9435        );
9436    }
9437
9438    #[test]
9439    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
9440        // Order pin on the layout cascade: the narrower
9441        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
9442        // joint-length budget. A structurally-malformed `:nome` (here:
9443        // uppercase) surfaces its specific shape error rather than
9444        // the chart-name-budget error, even when the joint length
9445        // would also overflow — the narrower diagnostic is more
9446        // self-locating. Mirrors the cascade-precedence pins peer
9447        // gates already use (e.g. `EntradaParaEmpty` before
9448        // `EntradaParaInvalid`).
9449        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9450        let c = caixa_with_nome(&over);
9451        // The bare-shape gate fires first.
9452        let err = c.validate_nome().unwrap_err();
9453        assert!(
9454            matches!(err, ManifestError::NomeInvalid { .. }),
9455            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
9456        );
9457        // And the layout verify cascade surfaces that diagnostic, not
9458        // the budget arm. Inject a path-exists oracle so the cascade
9459        // gets past the manifest-presence check and into the
9460        // value-shape gates.
9461        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9462        let err = crate::LayoutInvariants::verify(
9463            &layout,
9464            &c,
9465            std::path::Path::new("/tmp/caixa-test-fake-root"),
9466        )
9467        .unwrap_err();
9468        let issue = err.to_string();
9469        assert!(
9470            issue.contains("DNS-1123") || issue.contains("uppercase"),
9471            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
9472             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
9473        );
9474    }
9475
9476    #[test]
9477    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
9478        // Cross-axis envelope pin: the layout cascade wraps both
9479        // bare-`:nome` and joint-length-`:nome` failures through the
9480        // same [`LayoutError::NomeViolation`] envelope, since both
9481        // arms are on the `:nome` axis. The user's diagnostic stays
9482        // self-locating ("which axis"), and a future consumer that
9483        // dispatches on the layout-error variant (e.g. a `feira lint`
9484        // exit-code mapping) sees a single per-axis envelope. The
9485        // wrapped `issue:` carries the full inner diagnostic.
9486        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9487        let c = caixa_with_nome(&over);
9488        // The bare-shape gate accepts.
9489        c.validate_nome().unwrap();
9490        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9491        let err = crate::LayoutInvariants::verify(
9492            &layout,
9493            &c,
9494            std::path::Path::new("/tmp/caixa-test-fake-root"),
9495        )
9496        .unwrap_err();
9497        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
9498            panic!("expected LayoutError::NomeViolation, got {err:?}");
9499        };
9500        assert_eq!(caixa, over);
9501        assert!(
9502            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
9503            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
9504        );
9505    }
9506
9507    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
9508
9509    fn caixa_with_versao(versao: &str) -> Caixa {
9510        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9511        c.versao = versao.to_string();
9512        c
9513    }
9514
9515    #[test]
9516    fn validate_versao_accepts_canonical_template() {
9517        // Positive control: the bare `feira init`-style template's
9518        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
9519        // must not regress this baseline shape. A future tightening of
9520        // the accepted set surfaces here as a test failure first.
9521        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9522        c.validate_versao().unwrap();
9523    }
9524
9525    #[test]
9526    fn validate_versao_accepts_canonical_forms() {
9527        // Positive-set sweep: each realistic SemVer-2 shape the
9528        // substrate's downstream consumers accept must pass — bare
9529        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
9530        // build metadata (`+build.42`), the combined form, and the
9531        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
9532        // the peer `:nome` axis (6c992f8).
9533        for versao in [
9534            "0.1.0",
9535            "0.0.0",
9536            "1.0.0",
9537            "0.2.0-rc.1",
9538            "1.0.0-alpha.0",
9539            "1.0.0+build.42",
9540            "1.0.0-rc.1+build.42",
9541            "10.20.30",
9542        ] {
9543            caixa_with_versao(versao)
9544                .validate_versao()
9545                .unwrap_or_else(|e| {
9546                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
9547                });
9548        }
9549    }
9550
9551    #[test]
9552    fn validate_versao_rejects_empty() {
9553        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9554        // an empty `:versao` (the derive macro stores the raw String);
9555        // the gate's empty arm names the offending axis with a narrower
9556        // diagnostic than the `VersaoInvalid` parse arm would emit.
9557        // Mirrors `validate_nome_rejects_empty` (6c992f8).
9558        let c = caixa_with_versao("");
9559        let err = c.validate_versao().unwrap_err();
9560        assert_eq!(err, ManifestError::VersaoEmpty);
9561    }
9562
9563    #[test]
9564    fn validate_versao_rejects_git_tag_shape() {
9565        // The canonical "I copied the git tag verbatim" footgun —
9566        // `feira publish` *emits* `v<versao>` git tags, so a leaked
9567        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
9568        // shift every downstream consumer's version axis. `semver`
9569        // rejects the leading `v` at parse time; the gate moves the
9570        // diagnostic to the source `caixa.lisp`.
9571        let c = caixa_with_versao("v0.1.0");
9572        let err = c.validate_versao().unwrap_err();
9573        let ManifestError::VersaoInvalid { versao, reason } = err else {
9574            panic!("expected VersaoInvalid for git-tag-shape :versao");
9575        };
9576        assert_eq!(versao, "v0.1.0");
9577        assert!(
9578            !reason.is_empty(),
9579            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
9580        );
9581    }
9582
9583    #[test]
9584    fn validate_versao_rejects_missing_patch() {
9585        // The canonical "I shortened it" footgun — SemVer-2 requires
9586        // three parts. Cargo's `version =` field accepts the shortened
9587        // form as a requirement, conflating the two leaks across the
9588        // typed `:deps :versao` vs top-level `:versao` axes; the gate
9589        // pins the top-level axis to the strict three-part shape.
9590        let c = caixa_with_versao("0.1");
9591        let err = c.validate_versao().unwrap_err();
9592        assert!(
9593            matches!(
9594                err,
9595                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
9596            ),
9597            "got {err:?}"
9598        );
9599    }
9600
9601    #[test]
9602    fn validate_versao_rejects_requirement_shape() {
9603        // The canonical "I leaked a requirement into a version" footgun —
9604        // the typed `:deps :versao` / `:membros :versao` axes accept
9605        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
9606        // concrete `Version`. Without this gate the two typed surfaces
9607        // would silently overlap, and a top-level `^0.1` would surface
9608        // at `helm install` time as a Chart.yaml version rejection far
9609        // from the source `caixa.lisp`.
9610        let c = caixa_with_versao("^0.1");
9611        let err = c.validate_versao().unwrap_err();
9612        assert!(
9613            matches!(
9614                err,
9615                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
9616            ),
9617            "got {err:?}"
9618        );
9619    }
9620
9621    #[test]
9622    fn validate_versao_rejects_docker_tag_shape() {
9623        // The "I confused it with a docker tag" footgun — `latest`,
9624        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
9625        // SemVer rejects at parse time; the gate moves the diagnostic
9626        // to the source `caixa.lisp`.
9627        for bad in ["latest", "main", "stable"] {
9628            let c = caixa_with_versao(bad);
9629            let err = c.validate_versao().unwrap_err();
9630            assert!(
9631                matches!(
9632                    err,
9633                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
9634                ),
9635                "got {err:?} for {bad:?}"
9636            );
9637        }
9638    }
9639
9640    #[test]
9641    fn validate_versao_rejects_four_part_form() {
9642        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
9643        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
9644        // semver crate rejects the extra `.0` at parse time.
9645        let c = caixa_with_versao("0.1.0.0");
9646        let err = c.validate_versao().unwrap_err();
9647        assert!(
9648            matches!(
9649                err,
9650                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
9651            ),
9652            "got {err:?}"
9653        );
9654    }
9655
9656    #[test]
9657    fn versao_empty_takes_precedence_over_invalid() {
9658        // Order pin: the empty arm fires before the parser is consulted.
9659        // Empty < invalid in self-locating-ness — the narrower
9660        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
9661        // reference into the parser-shaped reason. Mirrors
9662        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
9663        // peer axis.
9664        let c = caixa_with_versao("");
9665        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
9666    }
9667
9668    #[test]
9669    fn versao_invalid_diagnostic_carries_offending_versao() {
9670        // Diagnostic-shape pin: the error names the offending `:versao`
9671        // verbatim with a non-empty parser-shaped reason, so a `feira
9672        // lint` run can render the diagnostic without re-parsing.
9673        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
9674        let c = caixa_with_versao("v0.1.0");
9675        let err = c.validate_versao().unwrap_err();
9676        let ManifestError::VersaoInvalid { versao, reason } = err else {
9677            panic!("expected VersaoInvalid variant");
9678        };
9679        assert_eq!(versao, "v0.1.0");
9680        assert!(
9681            !reason.is_empty(),
9682            "VersaoInvalid `reason` must carry the parser's wording verbatim"
9683        );
9684    }
9685
9686    #[test]
9687    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
9688        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
9689        // for `:upgrade-from :from` must also pass `validate_versao` —
9690        // the two `:versao`-typed surfaces (top-level `:versao`,
9691        // `:upgrade-from :from`) consume the *same* `semver::Version`
9692        // parser, so they must agree on the accepted set. Without this
9693        // pin, a future tightening of one axis could silently diverge
9694        // from the other. Mirrors the `:versao` requirement-axis
9695        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
9696        // commits established.
9697        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
9698            // From the canonical UpgradeFromEntry round-trip fixture
9699            // (`upgrade::tests::round_trip_load_module` peers).
9700            let entry = crate::UpgradeFromEntry {
9701                from: versao.to_string(),
9702                instructions: Vec::new(),
9703            };
9704            entry
9705                .validate()
9706                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
9707            caixa_with_versao(versao)
9708                .validate_versao()
9709                .unwrap_or_else(|e| {
9710                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
9711                });
9712        }
9713    }
9714
9715    // ── Caixa::validate_restart_window — supervisor restart-window
9716    //    folds through the shared `supervisor::duration_codec` ────────
9717
9718    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
9719        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
9720        c.kind = CaixaKind::Supervisor;
9721        c.restart_window = window.map(str::to_string);
9722        c
9723    }
9724
9725    #[test]
9726    fn validate_restart_window_accepts_none() {
9727        // The canonical "omit the slot to express no reset" shape — a
9728        // `None` raw string is the absence of the typed
9729        // `:restart-window` slot, which is exactly the SupervisorSpec
9730        // "never reset" semantics. The gate must be a no-op here; a
9731        // future tightening that rejected `None` would force every
9732        // supervisor caixa to authoring-time pin a window even when
9733        // the OTP semantics call for none.
9734        caixa_with_restart_window(None)
9735            .validate_restart_window()
9736            .unwrap();
9737    }
9738
9739    #[test]
9740    fn validate_restart_window_accepts_canonical_forms() {
9741        // Positive-set sweep across the canonical authoring units the
9742        // shared `supervisor::duration_codec::parse` accepts —
9743        // matches the codec-side `parse_accepts_integer_canonical_units`
9744        // pin in supervisor::tests so a future codec-side tightening
9745        // surfaces simultaneously on both axes.
9746        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
9747            caixa_with_restart_window(Some(window))
9748                .validate_restart_window()
9749                .unwrap_or_else(|e| {
9750                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
9751                });
9752        }
9753    }
9754
9755    #[test]
9756    fn validate_restart_window_rejects_fractional_seconds() {
9757        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
9758        // as f64 to 1.5 → renders back as `"1500ms"` on first
9759        // serialize). Prior to the fold + this gate, the inline
9760        // `parse_window_inline` accepted f64 magnitudes and silently
9761        // produced a `Duration::from_secs_f64(1.5)`, divergent from
9762        // the shared codec's integer-magnitude discipline on the
9763        // serde-routed siblings. The gate now surfaces a self-locating
9764        // diagnostic at the manifest layer.
9765        let err = caixa_with_restart_window(Some("1.5s"))
9766            .validate_restart_window()
9767            .unwrap_err();
9768        let ManifestError::RestartWindowMalformed {
9769            restart_window,
9770            reason,
9771        } = err
9772        else {
9773            panic!("expected RestartWindowMalformed for fractional seconds");
9774        };
9775        assert_eq!(restart_window, "1.5s");
9776        assert!(
9777            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
9778            "diagnostic must carry shared-codec wording, got {reason:?}"
9779        );
9780    }
9781
9782    #[test]
9783    fn validate_restart_window_rejects_decimal_shaped_integer() {
9784        // The `"1.0s"` class — numerically `1s` exactly, but the
9785        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
9786        // gets the same canonical-form diagnostic.
9787        let err = caixa_with_restart_window(Some("1.0s"))
9788            .validate_restart_window()
9789            .unwrap_err();
9790        assert!(
9791            matches!(
9792                err,
9793                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9794                    if restart_window == "1.0s"
9795            ),
9796            "got {err:?}"
9797        );
9798    }
9799
9800    #[test]
9801    fn validate_restart_window_rejects_half_unit_minute() {
9802        // `"0.5m"` is the unit-fraction footgun — author writes a
9803        // human-readable half-minute, the prior inline parser silently
9804        // produced `Duration::from_secs_f64(30.0)` and serde
9805        // re-emitted as `"30s"`, rewriting author intent. The gate
9806        // closes the loop at the manifest layer.
9807        let err = caixa_with_restart_window(Some("0.5m"))
9808            .validate_restart_window()
9809            .unwrap_err();
9810        let ManifestError::RestartWindowMalformed {
9811            restart_window,
9812            reason,
9813        } = err
9814        else {
9815            panic!("expected RestartWindowMalformed");
9816        };
9817        assert_eq!(restart_window, "0.5m");
9818        assert!(
9819            reason.contains("\"30s\""),
9820            "diagnostic must point at the canonical-form remediation, got {reason:?}"
9821        );
9822    }
9823
9824    #[test]
9825    fn validate_restart_window_rejects_leading_sign() {
9826        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
9827        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
9828        // and was caught by the `num < 0.0` arm which silently
9829        // returned `None`, dropping the author-supplied window). The
9830        // shared codec's digit-only gate rejects both with a unified
9831        // canonical-form diagnostic; the manifest-layer wrapper names
9832        // the offending value.
9833        for bad in ["+30s", "-30s"] {
9834            let err = caixa_with_restart_window(Some(bad))
9835                .validate_restart_window()
9836                .unwrap_err();
9837            assert!(
9838                matches!(
9839                    err,
9840                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
9841                        if restart_window == bad
9842                ),
9843                "got {err:?} for {bad:?}"
9844            );
9845        }
9846    }
9847
9848    #[test]
9849    fn validate_restart_window_rejects_unknown_unit() {
9850        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
9851        // unit dispatch surfaces an `unknown duration unit` reason;
9852        // the manifest-layer wrapper names the offending value.
9853        let err = caixa_with_restart_window(Some("30x"))
9854            .validate_restart_window()
9855            .unwrap_err();
9856        let ManifestError::RestartWindowMalformed {
9857            restart_window,
9858            reason,
9859        } = err
9860        else {
9861            panic!("expected RestartWindowMalformed for unknown unit");
9862        };
9863        assert_eq!(restart_window, "30x");
9864        assert!(
9865            reason.contains("unknown duration unit"),
9866            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
9867        );
9868    }
9869
9870    #[test]
9871    fn validate_restart_window_rejects_garbage() {
9872        // Pure non-numeric magnitude (`"abc"`) falls through to the
9873        // shared codec's narrower `"bad duration magnitude"` arm. Same
9874        // diagnostic shape as the codec-side
9875        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
9876        let err = caixa_with_restart_window(Some("abc"))
9877            .validate_restart_window()
9878            .unwrap_err();
9879        let ManifestError::RestartWindowMalformed {
9880            restart_window,
9881            reason,
9882        } = err
9883        else {
9884            panic!("expected RestartWindowMalformed for garbage");
9885        };
9886        assert_eq!(restart_window, "abc");
9887        assert!(
9888            reason.contains("bad duration magnitude"),
9889            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
9890        );
9891    }
9892
9893    #[test]
9894    fn validate_restart_window_rejects_empty_string() {
9895        // The empty-after-trim edge case — distinct from the `None`
9896        // canonical "omit the slot" shape. The shared codec's
9897        // digit-only gate refuses an empty magnitude; the manifest
9898        // layer names the offending `""` so the author can grep for
9899        // the literal empty value in their `caixa.lisp` and either
9900        // remove the slot (the canonical "no reset" shape) or pin a
9901        // positive duration.
9902        let err = caixa_with_restart_window(Some(""))
9903            .validate_restart_window()
9904            .unwrap_err();
9905        assert!(
9906            matches!(
9907                err,
9908                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9909                    if restart_window.is_empty()
9910            ),
9911            "got {err:?}"
9912        );
9913    }
9914
9915    #[test]
9916    fn validate_restart_window_diagnostic_carries_offending_value() {
9917        // Diagnostic-shape pin (peer with
9918        // `nome_invalid_diagnostic_carries_offending_nome` /
9919        // `versao_invalid_diagnostic_carries_offending_versao`): the
9920        // error names the offending raw `:restart-window` verbatim
9921        // with a non-empty shared-codec-shaped reason, so a `feira
9922        // lint` run can render the diagnostic without re-parsing.
9923        let err = caixa_with_restart_window(Some("1.5s"))
9924            .validate_restart_window()
9925            .unwrap_err();
9926        let ManifestError::RestartWindowMalformed {
9927            restart_window,
9928            reason,
9929        } = err
9930        else {
9931            panic!("expected RestartWindowMalformed variant");
9932        };
9933        assert_eq!(restart_window, "1.5s");
9934        assert!(
9935            !reason.is_empty(),
9936            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
9937        );
9938    }
9939
9940    #[test]
9941    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
9942        // Behavioral parity pin after the fold (`parse_window_inline`
9943        // deletion): the canonical `"60s"` still produces
9944        // `Duration::from_secs(60)` on the typed view — the fold is
9945        // semantically equivalent to the prior inline parser on the
9946        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
9947        // pin, narrowed to the parser-side contract.
9948        let c = caixa_with_restart_window(Some("60s"));
9949        let view = c.supervisor_view().expect("Supervisor kind has a view");
9950        assert_eq!(
9951            view.restart_window,
9952            Some(std::time::Duration::from_secs(60))
9953        );
9954    }
9955
9956    #[test]
9957    fn supervisor_view_soft_swallows_what_validate_rejects() {
9958        // Parity pin between the view-construction path and the
9959        // manifest-level validator: the same `"1.5s"` that surfaces
9960        // `RestartWindowMalformed` at `validate_restart_window` time
9961        // becomes `restart_window: None` on the typed view (the fold
9962        // preserves the existing best-effort shape of `supervisor_view`).
9963        // The contract is: a layout-verifier / `feira lint` flow that
9964        // cares about the malformed-window axis MUST consult
9965        // `validate_restart_window` — relying solely on the view's
9966        // `None` swallows the diagnostic silently. This pin makes the
9967        // expectation a typed invariant.
9968        let c = caixa_with_restart_window(Some("1.5s"));
9969        let view = c.supervisor_view().expect("Supervisor kind has a view");
9970        assert_eq!(
9971            view.restart_window, None,
9972            "view-construction path soft-swallows the parse error to None"
9973        );
9974        // And the manifest-level validator does NOT soft-swallow:
9975        assert!(
9976            matches!(
9977                c.validate_restart_window().unwrap_err(),
9978                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9979                    if restart_window == "1.5s"
9980            ),
9981            "validator must surface the offending value",
9982        );
9983    }
9984
9985    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
9986
9987    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
9988        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9989        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
9990        c.exe = exe.into_iter().map(String::from).collect();
9991        c.servicos = servicos.into_iter().map(String::from).collect();
9992        c
9993    }
9994
9995    #[test]
9996    fn validate_code_paths_accepts_canonical_template() {
9997        // The bare `Caixa::template` shape is the gate's identity element
9998        // on the canonical authoring shape — `:bibliotecas
9999        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
10000        // that the gate is non-disruptive against every existing caixa.
10001        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10002        c.validate_code_paths().unwrap();
10003    }
10004
10005    #[test]
10006    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
10007        // Positive control sweep: a canonical-shaped path on every slot
10008        // passes. Mirrors the peer
10009        // `behavior::validate_every_slot_relative_is_ok` pin.
10010        let c = caixa_with_code_paths(
10011            vec!["lib/demo.lisp", "lib/helpers.lisp"],
10012            vec!["exe/demo", "exe/tool"],
10013            vec!["servicos/demo.computeunit.yaml"],
10014        );
10015        c.validate_code_paths().unwrap();
10016    }
10017
10018    #[test]
10019    fn validate_code_paths_accepts_all_empty_lists() {
10020        // The empty-list identity element: every Caixa with no declared
10021        // code paths trivially passes (Supervisor / Aplicacao kinds rely
10022        // on this — the OwnCode gate already rejected them before the
10023        // path-shape gate runs in the layout, but the validator itself
10024        // must accept the empty shape).
10025        let c = caixa_with_code_paths(vec![], vec![], vec![]);
10026        c.validate_code_paths().unwrap();
10027    }
10028
10029    #[test]
10030    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
10031        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10032        let err = c.validate_code_paths().unwrap_err();
10033        assert!(
10034            matches!(
10035                err,
10036                ManifestError::CodePathEmpty {
10037                    slot: ":bibliotecas"
10038                }
10039            ),
10040            "got {err:?}",
10041        );
10042    }
10043
10044    #[test]
10045    fn validate_code_paths_rejects_empty_exe_entry() {
10046        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
10047        let err = c.validate_code_paths().unwrap_err();
10048        assert!(
10049            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
10050            "got {err:?}",
10051        );
10052    }
10053
10054    #[test]
10055    fn validate_code_paths_rejects_empty_servicos_entry() {
10056        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
10057        let err = c.validate_code_paths().unwrap_err();
10058        assert!(
10059            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
10060            "got {err:?}",
10061        );
10062    }
10063
10064    #[test]
10065    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
10066        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
10067        // so an absolute path that resolves on disk silently passes the
10068        // layout's existence check — the canonical sandbox-escape on
10069        // the biblioteca axis.
10070        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10071        let err = c.validate_code_paths().unwrap_err();
10072        let ManifestError::CodePathAbsolute { slot, path } = err else {
10073            panic!("expected CodePathAbsolute, got {err:?}");
10074        };
10075        assert_eq!(slot, ":bibliotecas");
10076        assert_eq!(path, PathBuf::from("/etc/passwd"));
10077    }
10078
10079    #[test]
10080    fn validate_code_paths_rejects_absolute_exe_entry() {
10081        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
10082        let err = c.validate_code_paths().unwrap_err();
10083        let ManifestError::CodePathAbsolute { slot, path } = err else {
10084            panic!("expected CodePathAbsolute, got {err:?}");
10085        };
10086        assert_eq!(slot, ":exe");
10087        assert_eq!(path, PathBuf::from("/usr/bin/env"));
10088    }
10089
10090    #[test]
10091    fn validate_code_paths_rejects_absolute_servicos_entry() {
10092        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
10093        let err = c.validate_code_paths().unwrap_err();
10094        let ManifestError::CodePathAbsolute { slot, path } = err else {
10095            panic!("expected CodePathAbsolute, got {err:?}");
10096        };
10097        assert_eq!(slot, ":servicos");
10098        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
10099    }
10100
10101    #[test]
10102    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
10103        // Canonical "I want a lib from a sibling caixa" footgun on the
10104        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
10105        // downstream, so a leading `..` traverses to the parent of the
10106        // caixa root with no diagnostic at layout time if the resolved
10107        // target exists.
10108        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
10109        let err = c.validate_code_paths().unwrap_err();
10110        let ManifestError::CodePathParentEscape { slot, path } = err else {
10111            panic!("expected CodePathParentEscape, got {err:?}");
10112        };
10113        assert_eq!(slot, ":bibliotecas");
10114        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
10115    }
10116
10117    #[test]
10118    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
10119        // Mid-path `..` defeats the layout's component-aware
10120        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
10121        // `starts_with(<root>/exe)` is true, but the canonical resolution
10122        // lives outside the caixa root. Caught regardless of where the
10123        // `..` sits — mirrors the peer
10124        // `behavior::validate_rejects_parent_escape_mid_path` pin.
10125        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
10126        let err = c.validate_code_paths().unwrap_err();
10127        let ManifestError::CodePathParentEscape { slot, path } = err else {
10128            panic!("expected CodePathParentEscape, got {err:?}");
10129        };
10130        assert_eq!(slot, ":exe");
10131        assert_eq!(path, PathBuf::from("exe/../../escape"));
10132    }
10133
10134    #[test]
10135    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
10136        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
10137        let err = c.validate_code_paths().unwrap_err();
10138        let ManifestError::CodePathParentEscape { slot, path } = err else {
10139            panic!("expected CodePathParentEscape, got {err:?}");
10140        };
10141        assert_eq!(slot, ":servicos");
10142        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
10143    }
10144
10145    #[test]
10146    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
10147        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
10148        // `:servicos`. A manifest with malformed entries on all three
10149        // surfaces surfaces the `:bibliotecas` defect first, mirroring
10150        // the canonical declaration order
10151        // `Caixa::declared_foreign_code_slots` already establishes for
10152        // the foreign-code-slot diagnostic.
10153        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
10154        let err = c.validate_code_paths().unwrap_err();
10155        assert!(
10156            matches!(
10157                err,
10158                ManifestError::CodePathEmpty {
10159                    slot: ":bibliotecas"
10160                }
10161            ),
10162            "got {err:?}",
10163        );
10164    }
10165
10166    #[test]
10167    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
10168        // Within-slot precedence pin: empty → absolute → parent-escape,
10169        // matching the [`PathShapeViolation`] arm-ordering every peer
10170        // `is_sandboxed_relative_path` caller follows (b0c8389
10171        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
10172        // `:bibliotecas` list whose first entry is empty *and* whose
10173        // later entries are absolute/parent-escape surfaces the empty
10174        // arm first, on the lexicographically-earliest offending entry.
10175        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
10176        let err = c.validate_code_paths().unwrap_err();
10177        assert!(
10178            matches!(
10179                err,
10180                ManifestError::CodePathEmpty {
10181                    slot: ":bibliotecas"
10182                }
10183            ),
10184            "got {err:?}",
10185        );
10186    }
10187
10188    #[test]
10189    fn validate_code_paths_first_offender_per_slot_wins() {
10190        // Within a single slot, the first declaration-order offender
10191        // surfaces — pins that the gate is left-to-right deterministic
10192        // (peer of every `*_first_collision_*` pin on duplicate gates).
10193        let c = caixa_with_code_paths(
10194            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
10195            vec![],
10196            vec![],
10197        );
10198        let err = c.validate_code_paths().unwrap_err();
10199        let ManifestError::CodePathAbsolute { slot, path } = err else {
10200            panic!("expected CodePathAbsolute, got {err:?}");
10201        };
10202        assert_eq!(slot, ":bibliotecas");
10203        assert_eq!(path, PathBuf::from("/etc/escape"));
10204    }
10205
10206    #[test]
10207    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
10208        // Diagnostic-shape pin (peer with
10209        // `nome_invalid_diagnostic_carries_offending_nome` /
10210        // `versao_invalid_diagnostic_carries_offending_versao`): the
10211        // error's Display surfaces both the offending `:slot` tag and
10212        // the offending path verbatim, so a `feira lint` run can render
10213        // the diagnostic without re-parsing.
10214        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10215        let rendered = c.validate_code_paths().unwrap_err().to_string();
10216        assert!(
10217            rendered.contains(":bibliotecas"),
10218            "diagnostic must name the offending slot: {rendered}",
10219        );
10220        assert!(
10221            rendered.contains("/etc/passwd"),
10222            "diagnostic must quote the offending path: {rendered}",
10223        );
10224    }
10225
10226    #[test]
10227    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
10228        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
10229        // axis. Without the gate `feira build` re-parses the same lib
10230        // twice, wasting work and silently masking the author's intent
10231        // to declare a *second* biblioteca.
10232        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
10233        let err = c.validate_code_paths().unwrap_err();
10234        let ManifestError::CodePathDuplicate { slot, path } = err else {
10235            panic!("expected CodePathDuplicate, got {err:?}");
10236        };
10237        assert_eq!(slot, ":bibliotecas");
10238        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
10239    }
10240
10241    #[test]
10242    fn validate_code_paths_rejects_duplicate_exe_entry() {
10243        // Same footgun on the Binario surface. The future `caixa-flake`
10244        // emitter that materializes each `:exe` entry as a flake
10245        // `packages.<name>` derivation would collide on the duplicate
10246        // package key — surfaced here at the typed-validate layer with a
10247        // self-locating diagnostic instead.
10248        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
10249        let err = c.validate_code_paths().unwrap_err();
10250        let ManifestError::CodePathDuplicate { slot, path } = err else {
10251            panic!("expected CodePathDuplicate, got {err:?}");
10252        };
10253        assert_eq!(slot, ":exe");
10254        assert_eq!(path, PathBuf::from("exe/cli"));
10255    }
10256
10257    #[test]
10258    fn validate_code_paths_rejects_duplicate_servicos_entry() {
10259        // Same footgun on the Servico surface. The peer caixa-helm /
10260        // caixa-flux renderers refuse `:servicos.len() != 1` with the
10261        // narrower `UnsupportedServicoCount` diagnostic, but that
10262        // diagnostic surfaces "too many servicos" without naming
10263        // "duplicate entry" — the typed self-locating framing only lands
10264        // at this gate.
10265        let c = caixa_with_code_paths(
10266            vec![],
10267            vec![],
10268            vec![
10269                "servicos/demo.computeunit.yaml",
10270                "servicos/demo.computeunit.yaml",
10271            ],
10272        );
10273        let err = c.validate_code_paths().unwrap_err();
10274        let ManifestError::CodePathDuplicate { slot, path } = err else {
10275            panic!("expected CodePathDuplicate, got {err:?}");
10276        };
10277        assert_eq!(slot, ":servicos");
10278        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
10279    }
10280
10281    #[test]
10282    fn validate_code_paths_accepts_same_path_across_slots() {
10283        // Per-list scope pin: a `:bibliotecas` entry that happens to
10284        // collide with an `:exe` or `:servicos` entry as a *string* is
10285        // not a duplicate by this gate (each list gets its own HashSet),
10286        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
10287        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
10288        // shape on the dep axis). The structural `starts_with(<exe |
10289        // servicos>_dir)` fence at layout time prevents the realistic
10290        // cross-slot collision case from existing on disk, but the gate's
10291        // per-list scope is correct independent of that downstream fence.
10292        let c = caixa_with_code_paths(
10293            vec!["lib/x.lisp"],
10294            vec!["exe/x"],
10295            vec!["servicos/x.computeunit.yaml"],
10296        );
10297        c.validate_code_paths().unwrap();
10298    }
10299
10300    #[test]
10301    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
10302        // Within-slot ordering pin: structural defects (empty / absolute
10303        // / parent-escape) fire before the duplicate gate on the same
10304        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
10305        // surfaces the narrower `CodePathEmpty` for the empty entry
10306        // first, not the duplicate on the later pair — same arm-ordering
10307        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
10308        // `:autores` 86c769b, `:deps` 359fba5).
10309        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
10310        let err = c.validate_code_paths().unwrap_err();
10311        assert!(
10312            matches!(
10313                err,
10314                ManifestError::CodePathEmpty {
10315                    slot: ":bibliotecas"
10316                }
10317            ),
10318            "got {err:?}",
10319        );
10320    }
10321
10322    #[test]
10323    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
10324        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
10325        // duplicates surface before `:exe` duplicates, matching the
10326        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
10327        // order every peer per-slot diagnostic on this surface follows.
10328        let c = caixa_with_code_paths(
10329            vec!["lib/x.lisp", "lib/x.lisp"],
10330            vec!["exe/y", "exe/y"],
10331            vec![],
10332        );
10333        let err = c.validate_code_paths().unwrap_err();
10334        let ManifestError::CodePathDuplicate { slot, path } = err else {
10335            panic!("expected CodePathDuplicate, got {err:?}");
10336        };
10337        assert_eq!(slot, ":bibliotecas");
10338        assert_eq!(path, PathBuf::from("lib/x.lisp"));
10339    }
10340
10341    #[test]
10342    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
10343        // Diagnostic-shape pin (peer with
10344        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10345        // on the structural arm): the duplicate-arm Display surfaces both
10346        // the offending `:slot` tag and the offending path verbatim, so a
10347        // `feira lint` run can render the diagnostic without re-parsing.
10348        let c = caixa_with_code_paths(
10349            vec![],
10350            vec![],
10351            vec![
10352                "servicos/demo.computeunit.yaml",
10353                "servicos/demo.computeunit.yaml",
10354            ],
10355        );
10356        let rendered = c.validate_code_paths().unwrap_err().to_string();
10357        assert!(
10358            rendered.contains(":servicos"),
10359            "diagnostic must name the offending slot: {rendered}",
10360        );
10361        assert!(
10362            rendered.contains("servicos/demo.computeunit.yaml"),
10363            "diagnostic must quote the offending path: {rendered}",
10364        );
10365    }
10366
10367    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
10368    //
10369    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
10370    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
10371    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
10372    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
10373    // at parse time — the same downstream consumer the peer `:behavior
10374    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
10375    // `:upgrade-from :state-change :script` (33cc830,
10376    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
10377    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
10378    // nix-built executable surface (`"exe/<name>"` shape per the canonical
10379    // [`crate::LayoutError::ExeOutsideDir`] error message and every
10380    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
10381    // is the `.computeunit.yaml` ComputeUnit-CR axis.
10382
10383    #[test]
10384    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
10385        // Canonical "I dragged the wrong file from the workspace tree"
10386        // footgun on the biblioteca axis. Without the gate `feira build`
10387        // hands the extensionless path to `tatara_lisp::read` and fails
10388        // with a parser-shaped diagnostic far from the source caixa.lisp,
10389        // with no field naming the offending `:bibliotecas` entry.
10390        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
10391            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10392            let err = c.validate_code_paths().unwrap_err();
10393            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10394                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10395            };
10396            assert_eq!(slot, ":bibliotecas");
10397            assert_eq!(path, PathBuf::from(relpath));
10398        }
10399    }
10400
10401    #[test]
10402    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
10403        // Wrong-extension sweep across common authoring footguns. Same
10404        // sweep posture as the peer
10405        // `behavior::validate_rejects_wrong_extension` (c97815a) and
10406        // `upgrade::tests::state_change_rejects_wrong_extension_script`
10407        // (33cc830) cases.
10408        for relpath in [
10409            "lib/demo.rs",
10410            "lib/demo.txt",
10411            "lib/demo.md",
10412            "lib/demo.json",
10413            "lib/demo.yaml",
10414            "lib/demo.toml",
10415            "lib/demo.lisp.bak",
10416            "lib/demo.lispx",
10417            "lib/demo.lis",
10418        ] {
10419            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10420            let err = c.validate_code_paths().unwrap_err();
10421            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10422                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10423            };
10424            assert_eq!(slot, ":bibliotecas");
10425            assert_eq!(path, PathBuf::from(relpath));
10426        }
10427    }
10428
10429    #[test]
10430    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
10431        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
10432        // contract. An uppercase `.LISP` shape that the layout's existence
10433        // check would (case-insensitively, on case-insensitive volumes)
10434        // match the on-disk file still mismatches the canonical form the
10435        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
10436        // contract. Mirrors the peer
10437        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
10438        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
10439        // (33cc830) sweeps.
10440        for relpath in [
10441            "lib/demo.LISP",
10442            "lib/demo.Lisp",
10443            "lib/demo.LiSp",
10444            "lib/demo.lISP",
10445        ] {
10446            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10447            let err = c.validate_code_paths().unwrap_err();
10448            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10449                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10450            };
10451            assert_eq!(slot, ":bibliotecas");
10452            assert_eq!(path, PathBuf::from(relpath));
10453        }
10454    }
10455
10456    #[test]
10457    fn validate_code_paths_accepts_canonical_lisp_shapes() {
10458        // Positive-control sweep through every canonical authoring shape
10459        // every in-tree fixture and the `Caixa::template` scaffold use.
10460        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
10461        // (c97815a) and the lifted predicate's own
10462        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
10463        // (33cc830).
10464        for relpath in [
10465            "lib/demo.lisp",
10466            "lib/handlers.lisp",
10467            "lib/migrations/v01-to-v02.lisp",
10468            "demo.lisp",
10469            "a.lisp",
10470            "./lib/demo.lisp",
10471            "lib/./handlers.lisp",
10472            "lib/migrations/v.0.1.lisp",
10473        ] {
10474            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10475            c.validate_code_paths()
10476                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10477        }
10478    }
10479
10480    #[test]
10481    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
10482        // The file-type gate is per-slot — only `:bibliotecas` carries the
10483        // tatara-lisp-source contract. An extensionless `:exe` entry
10484        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
10485        // canonical shapes every in-tree fixture uses, and must continue
10486        // to pass validate. Pins that a future tightening that broadens
10487        // the `.lisp` gate to either axis surfaces as a test failure
10488        // rather than as a silent breaking change to existing valid
10489        // manifests.
10490        let c = caixa_with_code_paths(
10491            vec![],
10492            vec!["exe/demo", "exe/tool"],
10493            vec!["servicos/demo.computeunit.yaml"],
10494        );
10495        c.validate_code_paths().unwrap();
10496    }
10497
10498    #[test]
10499    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
10500        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
10501        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
10502        // sandbox-shape diagnostic first (the `.lisp` remediation would
10503        // be misleading when the offending path can never resolve under
10504        // the caixa root anyway). Mirrors the peer
10505        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
10506        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
10507        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
10508        // on `:upgrade-from :state-change :script` (33cc830).
10509        //
10510        // Empty wins (the strictly-smaller-scope structural arm).
10511        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10512        assert!(
10513            matches!(
10514                c.validate_code_paths().unwrap_err(),
10515                ManifestError::CodePathEmpty {
10516                    slot: ":bibliotecas"
10517                }
10518            ),
10519            "empty must win over non-lisp-extension",
10520        );
10521        // Absolute wins (the path can't resolve under the caixa root).
10522        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10523        let err = c.validate_code_paths().unwrap_err();
10524        let ManifestError::CodePathAbsolute { slot, .. } = err else {
10525            panic!("absolute must win over non-lisp-extension, got {err:?}");
10526        };
10527        assert_eq!(slot, ":bibliotecas");
10528        // ParentEscape wins (the path escapes the caixa root).
10529        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
10530        let err = c.validate_code_paths().unwrap_err();
10531        let ManifestError::CodePathParentEscape { slot, .. } = err else {
10532            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
10533        };
10534        assert_eq!(slot, ":bibliotecas");
10535    }
10536
10537    #[test]
10538    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
10539        // Within-slot precedence pin: the per-entry file-type shape gate
10540        // fires before the cross-entry duplicate gate, so the narrower
10541        // structural defect dominates the uniqueness diagnostic. A
10542        // `("lib/x.txt" "lib/x.txt")` shape surfaces
10543        // `CodePathNonLispExtension` on the first entry rather than
10544        // `CodePathDuplicate` on the pair — same posture every per-entry
10545        // shape-gate-precedes-duplicate cascade follows on this surface
10546        // (the empty / absolute / parent-escape arms already precede the
10547        // duplicate arm; the lifted file-type arm joins that set).
10548        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
10549        let err = c.validate_code_paths().unwrap_err();
10550        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10551            panic!("expected CodePathNonLispExtension, got {err:?}");
10552        };
10553        assert_eq!(slot, ":bibliotecas");
10554        assert_eq!(path, PathBuf::from("lib/x.txt"));
10555    }
10556
10557    #[test]
10558    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
10559        // Diagnostic-shape pin (peer with
10560        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10561        // on the sandbox-shape arms and
10562        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
10563        // on the duplicate arm): the file-type-arm Display surfaces both
10564        // the offending `:slot` tag, the offending path verbatim, and the
10565        // expected `.lisp` extension named in the remediation text, so a
10566        // `feira lint` run can render the diagnostic without re-parsing.
10567        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
10568        let rendered = c.validate_code_paths().unwrap_err().to_string();
10569        assert!(
10570            rendered.contains(":bibliotecas"),
10571            "diagnostic must name the offending slot: {rendered}",
10572        );
10573        assert!(
10574            rendered.contains("lib/demo.rs"),
10575            "diagnostic must quote the offending path: {rendered}",
10576        );
10577        assert!(
10578            rendered.contains(".lisp"),
10579            "diagnostic must name the expected extension: {rendered}",
10580        );
10581    }
10582
10583    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
10584    //
10585    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
10586    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
10587    // contract. The peer caixa-helm / caixa-flux renderers consume each
10588    // `:servicos` entry through `serde_yaml::from_str` as a typed
10589    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
10590    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
10591    // axis `Path::extension` can't express on its own.
10592
10593    #[test]
10594    fn validate_code_paths_rejects_no_extension_servicos_entry() {
10595        // Canonical "I dragged the wrong file from the workspace tree"
10596        // footgun on the Servico axis. Without the gate the peer
10597        // caixa-helm / caixa-flux renderers hand the extensionless path
10598        // to `serde_yaml::from_str` and fail with a parser-shaped
10599        // diagnostic far from the source caixa.lisp, with no field
10600        // naming the offending `:servicos` entry.
10601        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
10602            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10603            let err = c.validate_code_paths().unwrap_err();
10604            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10605                panic!(
10606                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10607                     got {err:?}"
10608                );
10609            };
10610            assert_eq!(slot, ":servicos");
10611            assert_eq!(path, PathBuf::from(relpath));
10612        }
10613    }
10614
10615    #[test]
10616    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
10617        // Wrong-extension sweep across common authoring footguns on the
10618        // Servico axis. Bare `.yaml` is the canonical "I forgot the
10619        // `.computeunit` segment" typo; the off-by-one-segment shapes
10620        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
10621        // bare `Path::extension` view but mismatch the typed compound
10622        // suffix the renderers' `serde_yaml::from_str` consumer demands.
10623        // Same sweep-posture as the peer
10624        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
10625        // (64772a9) on the sibling tatara-lisp-source axis.
10626        for relpath in [
10627            "servicos/demo.yaml",
10628            "servicos/demo.yml",
10629            "servicos/demo.json",
10630            "servicos/demo.toml",
10631            "servicos/demo.txt",
10632            "servicos/demo.computeunit.yaml.bak",
10633            "servicos/demo.computeunit.yam",
10634            "servicos/demo.computeunit",
10635            "servicos/demo-computeunit.yaml",
10636            "servicos/demo_computeunit.yaml",
10637        ] {
10638            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10639            let err = c.validate_code_paths().unwrap_err();
10640            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10641                panic!(
10642                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10643                     got {err:?}"
10644                );
10645            };
10646            assert_eq!(slot, ":servicos");
10647            assert_eq!(path, PathBuf::from(relpath));
10648        }
10649    }
10650
10651    #[test]
10652    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
10653        // Case-sensitivity sweep — pins the strict lowercase
10654        // `.computeunit.yaml` contract. A case-folded shape that the
10655        // layout's existence check would (case-insensitively, on
10656        // case-insensitive volumes) match the on-disk file still
10657        // mismatches the canonical form the codec emits, breaking the
10658        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
10659        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
10660        // (64772a9) sweep on the sibling tatara-lisp-source axis.
10661        for relpath in [
10662            "servicos/demo.ComputeUnit.yaml",
10663            "servicos/demo.COMPUTEUNIT.yaml",
10664            "servicos/demo.computeunit.YAML",
10665            "servicos/demo.computeunit.Yaml",
10666            "servicos/demo.COMPUTEUNIT.YAML",
10667        ] {
10668            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10669            let err = c.validate_code_paths().unwrap_err();
10670            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10671                panic!(
10672                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10673                     got {err:?}"
10674                );
10675            };
10676            assert_eq!(slot, ":servicos");
10677            assert_eq!(path, PathBuf::from(relpath));
10678        }
10679    }
10680
10681    #[test]
10682    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
10683        // Degenerate hidden-file shape: a file name exactly equal to the
10684        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
10685        // the structural "Servico declared with no identity" footgun.
10686        // The substrate identifies each ComputeUnit by the file-stem
10687        // segment that precedes `.computeunit.yaml` (the rendered
10688        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
10689        // the M3 `:contratos` membership lookup), so an empty stem
10690        // leaves the Servico unidentifiable. Pinned at the typed-axis
10691        // level so a future regression that drops the `name.len() >
10692        // SUFFIX.len()` bound at the predicate surfaces here, not
10693        // piecemeal as a `lareira-` chart-name collision at render time.
10694        for relpath in ["servicos/.computeunit.yaml"] {
10695            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10696            let err = c.validate_code_paths().unwrap_err();
10697            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10698                panic!(
10699                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10700                     got {err:?}"
10701                );
10702            };
10703            assert_eq!(slot, ":servicos");
10704            assert_eq!(path, PathBuf::from(relpath));
10705        }
10706    }
10707
10708    #[test]
10709    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
10710        // Positive-control sweep through every canonical authoring shape
10711        // every in-tree fixture and the `Caixa::template` scaffold use.
10712        // Mirrors the peer
10713        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
10714        // and the lifted predicate's own
10715        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
10716        // render.rs.
10717        for relpath in [
10718            "servicos/demo.computeunit.yaml",
10719            "servicos/hello-rio.computeunit.yaml",
10720            "servicos/my-service.computeunit.yaml",
10721            "servicos/a.computeunit.yaml",
10722            "./servicos/demo.computeunit.yaml",
10723            "servicos/./demo.computeunit.yaml",
10724            "servicos/sub/nested.computeunit.yaml",
10725            "servicos/v0.1.computeunit.yaml",
10726        ] {
10727            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10728            c.validate_code_paths()
10729                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10730        }
10731    }
10732
10733    #[test]
10734    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
10735        // The file-type gate is per-slot — only `:servicos` carries the
10736        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
10737        // entry and an extensionless `:exe` entry are the canonical
10738        // shapes every in-tree fixture uses, and must continue to pass
10739        // validate. Peer of
10740        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
10741        // (64772a9) — together pin that the typed
10742        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
10743        // cross-axis leakage in either direction.
10744        let c = caixa_with_code_paths(
10745            vec!["lib/demo.lisp"],
10746            vec!["exe/demo", "exe/tool"],
10747            vec!["servicos/demo.computeunit.yaml"],
10748        );
10749        c.validate_code_paths().unwrap();
10750    }
10751
10752    #[test]
10753    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
10754        // Cross-arm precedence pin: a `:servicos` entry that is *both*
10755        // sandbox-escaping and wrong-extension surfaces the more
10756        // fundamental sandbox-shape diagnostic first (the
10757        // `.computeunit.yaml` remediation would be misleading when the
10758        // offending path can never resolve under the caixa root
10759        // anyway). Mirrors the peer
10760        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
10761        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
10762        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
10763        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
10764        // table establishes.
10765        //
10766        // Empty wins (the strictly-smaller-scope structural arm).
10767        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
10768        assert!(
10769            matches!(
10770                c.validate_code_paths().unwrap_err(),
10771                ManifestError::CodePathEmpty { slot: ":servicos" }
10772            ),
10773            "empty must win over non-computeunit-yaml-extension",
10774        );
10775        // Absolute wins (the path can't resolve under the caixa root).
10776        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
10777        let err = c.validate_code_paths().unwrap_err();
10778        let ManifestError::CodePathAbsolute { slot, .. } = err else {
10779            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
10780        };
10781        assert_eq!(slot, ":servicos");
10782        // ParentEscape wins (the path escapes the caixa root).
10783        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
10784        let err = c.validate_code_paths().unwrap_err();
10785        let ManifestError::CodePathParentEscape { slot, .. } = err else {
10786            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
10787        };
10788        assert_eq!(slot, ":servicos");
10789    }
10790
10791    #[test]
10792    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
10793        // Within-slot precedence pin: the per-entry file-type shape gate
10794        // fires before the cross-entry duplicate gate, so the narrower
10795        // structural defect dominates the uniqueness diagnostic. A
10796        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
10797        // `CodePathNonComputeUnitYamlExtension` on the first entry
10798        // rather than `CodePathDuplicate` on the pair — same posture
10799        // every per-entry shape-gate-precedes-duplicate cascade follows
10800        // on this surface, peer of the 64772a9 `:bibliotecas`
10801        // `("lib/x.txt" "lib/x.txt")` ordering.
10802        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
10803        let err = c.validate_code_paths().unwrap_err();
10804        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10805            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
10806        };
10807        assert_eq!(slot, ":servicos");
10808        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
10809    }
10810
10811    #[test]
10812    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
10813     {
10814        // Diagnostic-shape pin (peer with
10815        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
10816        // on the sibling tatara-lisp-source axis): the file-type-arm
10817        // Display surfaces both the offending `:slot` tag, the
10818        // offending path verbatim, and the expected
10819        // `.computeunit.yaml` compound suffix named in the remediation
10820        // text, so a `feira lint` run can render the diagnostic without
10821        // re-parsing.
10822        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
10823        let rendered = c.validate_code_paths().unwrap_err().to_string();
10824        assert!(
10825            rendered.contains(":servicos"),
10826            "diagnostic must name the offending slot: {rendered}",
10827        );
10828        assert!(
10829            rendered.contains("servicos/demo.yaml"),
10830            "diagnostic must quote the offending path: {rendered}",
10831        );
10832        assert!(
10833            rendered.contains(".computeunit.yaml"),
10834            "diagnostic must name the expected compound suffix: {rendered}",
10835        );
10836    }
10837
10838    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
10839
10840    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
10841        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10842        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
10843        c
10844    }
10845
10846    #[test]
10847    fn validate_etiquetas_accepts_empty_list() {
10848        // The empty-list identity: every caixa with no declared tags
10849        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
10850        // so the gate is non-disruptive against every existing manifest.
10851        let c = caixa_with_etiquetas(vec![]);
10852        c.validate_etiquetas().unwrap();
10853    }
10854
10855    #[test]
10856    fn validate_etiquetas_accepts_canonical_forms() {
10857        // Positive control sweep: a canonical-shaped non-empty distinct
10858        // tag list passes, mirroring the example checkout-aplicacao
10859        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
10860        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
10861        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
10862        c.validate_etiquetas().unwrap();
10863    }
10864
10865    #[test]
10866    fn validate_etiquetas_rejects_empty_entry() {
10867        // Canonical paste-from-blank-doc footgun. Without the gate the
10868        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
10869        // no-op tag indexing nothing in the future caixa-registry.
10870        let c = caixa_with_etiquetas(vec![""]);
10871        let err = c.validate_etiquetas().unwrap_err();
10872        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10873    }
10874
10875    #[test]
10876    fn validate_etiquetas_rejects_duplicate_entry() {
10877        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
10878        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
10879        // collect at chart render — a "second wins / one silently
10880        // disappears" shape divergent from every peer typed-graph set
10881        // gate. The duplicate-arm names the offending tag verbatim.
10882        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
10883        let err = c.validate_etiquetas().unwrap_err();
10884        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
10885            panic!("expected EtiquetaDuplicate, got {err:?}");
10886        };
10887        assert_eq!(etiqueta, "demo");
10888    }
10889
10890    #[test]
10891    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
10892        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
10893        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
10894        // structural "this entry has no value" defect dominates the
10895        // cross-entry uniqueness diagnostic. Mirrors the peer
10896        // empty-before-duplicate cascades on `:caracteristicas`
10897        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
10898        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
10899        // `MembroDuplicate`).
10900        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
10901        let err = c.validate_etiquetas().unwrap_err();
10902        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10903    }
10904
10905    #[test]
10906    fn validate_etiquetas_duplicate_reports_first_collision() {
10907        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
10908        // duplicate (the lexicographically-earliest offending position
10909        // — the second `"a"` at index 2 collides with the first `"a"`
10910        // at index 0), not the later `"b"` collision at index 3,
10911        // peer with every other first-collision diagnostic posture on
10912        // this surface (`validate_load_singularity_reports_first_collision`,
10913        // `validate_cleanup_singularity_reports_first_collision`).
10914        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
10915        let err = c.validate_etiquetas().unwrap_err();
10916        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
10917            panic!("expected EtiquetaDuplicate, got {err:?}");
10918        };
10919        assert_eq!(etiqueta, "a");
10920    }
10921
10922    #[test]
10923    fn validate_etiquetas_case_sensitive() {
10924        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
10925        // mirroring the peer `:membros :caixa` / `:children :caixa`
10926        // exact-string-match discipline. The shape gate this routine
10927        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
10928        // grammar) accepts mixed case — crates.io's keyword rule is
10929        // "case-insensitive" at the index layer but admits mixed case
10930        // at the entry layer (the canonical Helm chart `keywords:`
10931        // shape is lowercase by convention, but the grammar admits
10932        // uppercase). Case-sensitivity at the duplicate-set layer
10933        // remains structural — two distinct strings are two distinct
10934        // entries.
10935        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
10936        c.validate_etiquetas().unwrap();
10937    }
10938
10939    #[test]
10940    fn validate_etiquetas_diagnostic_carries_offending_tag() {
10941        // Diagnostic-shape pin (peer with
10942        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
10943        // the error's Display surfaces the offending tag verbatim, so a
10944        // `feira lint` run can render the diagnostic without re-parsing
10945        // and the author can grep their caixa.lisp for the offending
10946        // value.
10947        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
10948        let rendered = c.validate_etiquetas().unwrap_err().to_string();
10949        assert!(
10950            rendered.contains(":etiquetas"),
10951            "diagnostic must name the offending slot: {rendered}",
10952        );
10953        assert!(
10954            rendered.contains("demo"),
10955            "diagnostic must quote the offending tag: {rendered}",
10956        );
10957    }
10958
10959    #[test]
10960    fn validate_etiquetas_rejects_leading_whitespace_entry() {
10961        // Canonical paste-from-aligned-doc footgun. Without the shape
10962        // gate `" mesh"` silently passed validate and landed as a
10963        // YAML plain-style scalar with leading whitespace in the
10964        // rendered Chart.yaml `keywords:` array — every YAML 1.2
10965        // dumper trims leading whitespace from plain-style scalars,
10966        // so the authored space round-tripped inconsistently back
10967        // through `caixa.lisp`. Mirrors the peer
10968        // `validate_autores_rejects_leading_whitespace_entry`.
10969        let c = caixa_with_etiquetas(vec![" mesh"]);
10970        let err = c.validate_etiquetas().unwrap_err();
10971        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10972            panic!("expected EtiquetaInvalid, got {err:?}");
10973        };
10974        assert_eq!(etiqueta, " mesh");
10975        assert!(reason.contains("whitespace"), "got: {reason}");
10976    }
10977
10978    #[test]
10979    fn validate_etiquetas_rejects_embedded_newline_entry() {
10980        // Canonical paste-from-multiline-doc footgun — the author
10981        // pasted a multi-tag block into one `:etiquetas` entry
10982        // instead of splitting into one entry per tag. Without the
10983        // shape gate `"mesh\nhttp"` silently passed validate and
10984        // landed as a YAML-illegal multi-line scalar in the rendered
10985        // Chart.yaml `keywords:` array.
10986        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
10987        let err = c.validate_etiquetas().unwrap_err();
10988        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10989            panic!("expected EtiquetaInvalid, got {err:?}");
10990        };
10991        assert_eq!(etiqueta, "mesh\nhttp");
10992        assert!(reason.contains("newline"), "got: {reason}");
10993    }
10994
10995    #[test]
10996    fn validate_etiquetas_rejects_embedded_comma_entry() {
10997        // Canonical CSV-list-separator-confusion footgun: the author
10998        // confused the CSV-style separator convention with the
10999        // `:etiquetas` list grammar. Without the shape gate
11000        // `"mesh,http,grpc"` silently passed validate and landed as a
11001        // single malformed search tag in the rendered Chart.yaml
11002        // `keywords:` array — Artifact Hub's keyword index would
11003        // either silently drop the tag or index it as
11004        // `mesh,http,grpc` instead of three separate tags.
11005        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
11006        let err = c.validate_etiquetas().unwrap_err();
11007        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11008            panic!("expected EtiquetaInvalid, got {err:?}");
11009        };
11010        assert_eq!(etiqueta, "mesh,http,grpc");
11011        assert!(reason.contains('`'), "got: {reason}");
11012        assert!(reason.contains(','), "got: {reason}");
11013    }
11014
11015    #[test]
11016    fn validate_etiquetas_rejects_embedded_slash_entry() {
11017        // Canonical path-separator-confusion footgun: the author
11018        // confused namespace-path notation with the keyword grammar.
11019        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
11020        let err = c.validate_etiquetas().unwrap_err();
11021        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11022            panic!("expected EtiquetaInvalid, got {err:?}");
11023        };
11024        assert_eq!(etiqueta, "caixa/servico");
11025        assert!(reason.contains('/'), "got: {reason}");
11026    }
11027
11028    #[test]
11029    fn validate_etiquetas_rejects_leading_digit_entry() {
11030        // Canonical paste-from-numbered-list footgun: the author
11031        // copied `1. mesh` from a numbered doc and the `1` leaked
11032        // into the tag.
11033        let c = caixa_with_etiquetas(vec!["1mesh"]);
11034        let err = c.validate_etiquetas().unwrap_err();
11035        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11036            panic!("expected EtiquetaInvalid, got {err:?}");
11037        };
11038        assert_eq!(etiqueta, "1mesh");
11039        assert!(reason.contains("digit"), "got: {reason}");
11040    }
11041
11042    #[test]
11043    fn validate_etiquetas_rejects_leading_hyphen_entry() {
11044        // Canonical kebab-leak footgun.
11045        let c = caixa_with_etiquetas(vec!["-foo"]);
11046        let err = c.validate_etiquetas().unwrap_err();
11047        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11048            panic!("expected EtiquetaInvalid, got {err:?}");
11049        };
11050        assert_eq!(etiqueta, "-foo");
11051        assert!(reason.contains('-'), "got: {reason}");
11052    }
11053
11054    #[test]
11055    fn validate_etiquetas_rejects_non_ascii_entry() {
11056        // Canonical paste-from-Unicode-doc footgun. Every legitimate
11057        // search tag is strict ASCII; raw non-ASCII silently
11058        // round-trips inconsistently across NFC/NFD normalization on
11059        // APFS / case-folding filesystems and breaks the Artifact Hub
11060        // keyword search index lookup.
11061        let c = caixa_with_etiquetas(vec!["café"]);
11062        let err = c.validate_etiquetas().unwrap_err();
11063        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11064            panic!("expected EtiquetaInvalid, got {err:?}");
11065        };
11066        assert_eq!(etiqueta, "café");
11067        assert!(reason.contains("non-ASCII"), "got: {reason}");
11068    }
11069
11070    #[test]
11071    fn validate_etiquetas_rejects_period_entry() {
11072        // Canonical namespace-confusion / version-suffix footgun
11073        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
11074        // excludes `.` from the continuation set even though the
11075        // sibling `:caracteristicas` axis (Cargo's feature-name
11076        // grammar) admits it. Tighter than the sibling axis, peer
11077        // with Cargo's own crates.io keyword shape.
11078        let c = caixa_with_etiquetas(vec!["http.1"]);
11079        let err = c.validate_etiquetas().unwrap_err();
11080        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11081            panic!("expected EtiquetaInvalid, got {err:?}");
11082        };
11083        assert_eq!(etiqueta, "http.1");
11084        assert!(reason.contains('.'), "got: {reason}");
11085    }
11086
11087    #[test]
11088    fn validate_etiquetas_empty_takes_precedence_over_shape() {
11089        // Per-entry empty-first cascade pin: an entry that is both
11090        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
11091        // narrower "this entry has no value" structural defect
11092        // dominates the broader shape-predicate diagnostic). The
11093        // empty arm fires before the shape predicate is consulted,
11094        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
11095        // cascade established on the sibling universal-axis Vec<String>
11096        // surface.
11097        let c = caixa_with_etiquetas(vec![""]);
11098        let err = c.validate_etiquetas().unwrap_err();
11099        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11100    }
11101
11102    #[test]
11103    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
11104        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11105        // entry that is malformed surfaces `EtiquetaInvalid` even when
11106        // a later entry would have collided on duplicate. The
11107        // per-entry shape arm fires inside the same loop iteration as
11108        // the empty arm, before the seen-set insert at end-of-iteration
11109        // — structural per-entry defects dominate the cross-entry
11110        // uniqueness diagnostic. Mirrors the peer
11111        // `validate_autores_shape_takes_precedence_over_duplicate`.
11112        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
11113        let err = c.validate_etiquetas().unwrap_err();
11114        assert!(
11115            matches!(err, ManifestError::EtiquetaInvalid { .. }),
11116            "got {err:?}",
11117        );
11118    }
11119
11120    #[test]
11121    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
11122        // Diagnostic-shape pin on the new shape arm (peer with
11123        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
11124        // the rendered Display surfaces both the offending slot name
11125        // and the offending value verbatim, so a `feira lint` run
11126        // points the author at the exact `:etiquetas` entry to fix.
11127        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11128        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11129        assert!(
11130            rendered.contains(":etiquetas"),
11131            "diagnostic must name the offending slot: {rendered}",
11132        );
11133        assert!(
11134            rendered.contains("mesh\\nhttp"),
11135            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11136        );
11137    }
11138
11139    #[test]
11140    fn validate_etiquetas_rejects_at_21_byte_boundary() {
11141        // The 20-byte cap pin — boundary-exceeding case rejected,
11142        // boundary-accepting case passes. Mirrors the peer
11143        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
11144        // side pin, surfaced at the per-axis caller so the cap
11145        // propagates through validate end-to-end. Constructed as a
11146        // single all-`a` token so only the cap arm fires.
11147        let max_ok = "a".repeat(20);
11148        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
11149        c.validate_etiquetas().unwrap();
11150        let too_long = "a".repeat(21);
11151        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
11152        let err = c.validate_etiquetas().unwrap_err();
11153        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
11154            panic!("expected EtiquetaInvalid, got {err:?}");
11155        };
11156        assert!(reason.contains("20"), "got: {reason}");
11157        assert!(reason.contains("21"), "got: {reason}");
11158    }
11159
11160    #[test]
11161    fn validate_etiquetas_accepts_canonical_shaped_forms() {
11162        // Positive control sweep: every canonical-shaped tag from the
11163        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
11164        // example fixtures plus the substrate-fixed tags caixa-helm
11165        // unions in at chart render. Drift between this list and the
11166        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
11167        // sweep surfaces here — one source of truth for the rule.
11168        let c = caixa_with_etiquetas(vec![
11169            "example",
11170            "aplicacao",
11171            "mesh",
11172            "ecommerce",
11173            "demo",
11174            "infrastructure",
11175            "aws",
11176            "akeyless",
11177            "pangea-native",
11178            "hello-world",
11179            "wasm",
11180            "rust",
11181            "tatara-lisp",
11182            "caixa-servico",
11183            "lareira",
11184        ]);
11185        c.validate_etiquetas().unwrap();
11186    }
11187
11188    // ── validate_autores — universal-axis maintainer shape ────────────
11189
11190    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
11191        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11192        c.autores = autores.into_iter().map(String::from).collect();
11193        c
11194    }
11195
11196    #[test]
11197    fn validate_autores_accepts_empty_list() {
11198        // The empty-list identity: `Caixa::template` emits `:autores ()`,
11199        // so the gate is non-disruptive against every existing manifest.
11200        let c = caixa_with_autores(vec![]);
11201        c.validate_autores().unwrap();
11202    }
11203
11204    #[test]
11205    fn validate_autores_accepts_canonical_forms() {
11206        // Positive control sweep: every canonical-shaped non-empty
11207        // distinct maintainer list passes — the hello-rio / checkout-
11208        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
11209        // multi-author shape downstream packaging surfaces emit.
11210        let c = caixa_with_autores(vec!["pleme-io"]);
11211        c.validate_autores().unwrap();
11212        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
11213        c.validate_autores().unwrap();
11214    }
11215
11216    #[test]
11217    fn validate_autores_rejects_empty_entry() {
11218        // Canonical paste-from-blank-doc footgun. Without the gate the
11219        // empty entry rendered as `maintainers: [{name: "", email: null}]`
11220        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
11221        // to.
11222        let c = caixa_with_autores(vec![""]);
11223        let err = c.validate_autores().unwrap_err();
11224        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11225    }
11226
11227    #[test]
11228    fn validate_autores_rejects_duplicate_entry() {
11229        // Canonical copy-paste-the-wrong-author footgun. Unlike the
11230        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
11231        // dedups the rendered `keywords:` array), the `maintainers:`
11232        // rendering has *no* dedup — duplicates stack verbatim. The
11233        // duplicate-arm names the offending author verbatim.
11234        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11235        let err = c.validate_autores().unwrap_err();
11236        let ManifestError::AutorDuplicate { autor } = err else {
11237            panic!("expected AutorDuplicate, got {err:?}");
11238        };
11239        assert_eq!(autor, "pleme-io");
11240    }
11241
11242    #[test]
11243    fn validate_autores_empty_takes_precedence_over_duplicate() {
11244        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
11245        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
11246        // "this entry has no value" defect dominates the cross-entry
11247        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
11248        // cascades on `:etiquetas` (`EtiquetaEmpty` before
11249        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
11250        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11251        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
11252        // `MembroDuplicate`).
11253        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
11254        let err = c.validate_autores().unwrap_err();
11255        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11256    }
11257
11258    #[test]
11259    fn validate_autores_duplicate_reports_first_collision() {
11260        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11261        // duplicate (the lexicographically-earliest offending position
11262        // — the second `"a"` at index 2 collides with the first `"a"`
11263        // at index 0), not the later `"b"` collision at index 3,
11264        // peer with every other first-collision diagnostic posture on
11265        // this surface.
11266        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
11267        let err = c.validate_autores().unwrap_err();
11268        let ManifestError::AutorDuplicate { autor } = err else {
11269            panic!("expected AutorDuplicate, got {err:?}");
11270        };
11271        assert_eq!(autor, "a");
11272    }
11273
11274    #[test]
11275    fn validate_autores_case_sensitive() {
11276        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
11277        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
11278        // / `:children :caixa` exact-string-match discipline.
11279        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
11280        c.validate_autores().unwrap();
11281    }
11282
11283    #[test]
11284    fn validate_autores_diagnostic_carries_offending_author() {
11285        // Diagnostic-shape pin (peer with
11286        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
11287        // error's Display surfaces the offending author verbatim, so a
11288        // `feira lint` run can render the diagnostic without re-parsing
11289        // and the author can grep their caixa.lisp for the offending
11290        // value.
11291        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11292        let rendered = c.validate_autores().unwrap_err().to_string();
11293        assert!(
11294            rendered.contains(":autores"),
11295            "diagnostic must name the offending slot: {rendered}",
11296        );
11297        assert!(
11298            rendered.contains("pleme-io"),
11299            "diagnostic must quote the offending author: {rendered}",
11300        );
11301    }
11302
11303    #[test]
11304    fn validate_autores_rejects_leading_whitespace_entry() {
11305        // Canonical paste-from-aligned-doc footgun. Without the shape
11306        // gate `" pleme-io"` silently passed validate and landed as a
11307        // YAML plain-style scalar with leading whitespace in the
11308        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
11309        // dumper trims leading whitespace from plain-style scalars, so
11310        // the authored space round-tripped inconsistently back through
11311        // `caixa.lisp`. Mirrors the peer
11312        // `validate_descricao_rejects_leading_whitespace`.
11313        let c = caixa_with_autores(vec![" pleme-io"]);
11314        let err = c.validate_autores().unwrap_err();
11315        let ManifestError::AutorInvalid { autor, reason } = err else {
11316            panic!("expected AutorInvalid, got {err:?}");
11317        };
11318        assert_eq!(autor, " pleme-io");
11319        assert!(reason.contains("whitespace"), "got: {reason}");
11320    }
11321
11322    #[test]
11323    fn validate_autores_rejects_trailing_whitespace_entry() {
11324        // Canonical paste-from-doc footgun.
11325        let c = caixa_with_autores(vec!["pleme-io "]);
11326        let err = c.validate_autores().unwrap_err();
11327        let ManifestError::AutorInvalid { autor, reason } = err else {
11328            panic!("expected AutorInvalid, got {err:?}");
11329        };
11330        assert_eq!(autor, "pleme-io ");
11331        assert!(reason.contains("whitespace"), "got: {reason}");
11332    }
11333
11334    #[test]
11335    fn validate_autores_rejects_embedded_newline_entry() {
11336        // Canonical paste-from-multiline-doc footgun — the author
11337        // pasted a multi-line block of author records into one
11338        // `:autores` entry instead of splitting into one entry per
11339        // author. Without the shape gate `"alice\nbob"` silently
11340        // passed validate and landed as a YAML-illegal multi-line
11341        // scalar in the rendered Chart.yaml `maintainers:` array.
11342        let c = caixa_with_autores(vec!["alice\nbob"]);
11343        let err = c.validate_autores().unwrap_err();
11344        let ManifestError::AutorInvalid { autor, reason } = err else {
11345            panic!("expected AutorInvalid, got {err:?}");
11346        };
11347        assert_eq!(autor, "alice\nbob");
11348        assert!(reason.contains("newline"), "got: {reason}");
11349    }
11350
11351    #[test]
11352    fn validate_autores_rejects_embedded_carriage_return_entry() {
11353        // Canonical paste-from-Windows-CRLF-doc footgun.
11354        let c = caixa_with_autores(vec!["alice\rbob"]);
11355        let err = c.validate_autores().unwrap_err();
11356        let ManifestError::AutorInvalid { autor, reason } = err else {
11357            panic!("expected AutorInvalid, got {err:?}");
11358        };
11359        assert_eq!(autor, "alice\rbob");
11360        assert!(reason.contains("carriage return"), "got: {reason}");
11361    }
11362
11363    #[test]
11364    fn validate_autores_rejects_embedded_tab_entry() {
11365        // Canonical tab-from-aligned-doc footgun.
11366        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
11367        let err = c.validate_autores().unwrap_err();
11368        let ManifestError::AutorInvalid { autor, reason } = err else {
11369            panic!("expected AutorInvalid, got {err:?}");
11370        };
11371        assert_eq!(autor, "Pleme\tContributors");
11372        assert!(reason.contains("tab"), "got: {reason}");
11373    }
11374
11375    #[test]
11376    fn validate_autores_rejects_embedded_control_bytes_entry() {
11377        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
11378        // surface the same control-byte arm.
11379        for entry in [
11380            "alice\x00bob",
11381            "alice\x07bob",
11382            "alice\x1bbob",
11383            "alice\x7fbob",
11384        ] {
11385            let c = caixa_with_autores(vec![entry]);
11386            let err = c.validate_autores().unwrap_err();
11387            let ManifestError::AutorInvalid { autor, reason } = err else {
11388                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
11389            };
11390            assert_eq!(autor, entry);
11391            assert!(
11392                reason.contains("control character"),
11393                "{entry:?} reason: {reason}",
11394            );
11395        }
11396    }
11397
11398    #[test]
11399    fn validate_autores_accepts_unicode_entry() {
11400        // Unicode positive control: realistic maintainer names carry
11401        // Unicode (`François`, `日本語`, `naïve`). The predicate must
11402        // round-trip Unicode losslessly, peer with the
11403        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
11404        // sweep.
11405        let c = caixa_with_autores(vec![
11406            "François Dupont",
11407            "日本語の名前",
11408            "naïve <naive@example.com>",
11409        ]);
11410        c.validate_autores().unwrap();
11411    }
11412
11413    #[test]
11414    fn validate_autores_empty_takes_precedence_over_shape() {
11415        // Per-entry empty-first cascade pin: an entry that is both
11416        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
11417        // "this entry has no value" structural defect dominates the
11418        // broader shape-predicate diagnostic). The empty arm fires
11419        // before the shape predicate is consulted, mirroring the peer
11420        // `validate_repositorio_empty_takes_precedence_over_shape`
11421        // cascade on the universal `Option<String>` siblings — and now
11422        // established on the Vec<String> per-entry surface.
11423        let c = caixa_with_autores(vec![""]);
11424        let err = c.validate_autores().unwrap_err();
11425        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11426    }
11427
11428    #[test]
11429    fn validate_autores_shape_takes_precedence_over_duplicate() {
11430        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11431        // entry that is malformed surfaces `AutorInvalid` even when a
11432        // later entry would have collided on duplicate. The per-entry
11433        // shape arm fires inside the same loop iteration as the empty
11434        // arm, before the seen-set insert at end-of-iteration —
11435        // structural per-entry defects dominate the cross-entry
11436        // uniqueness diagnostic.
11437        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
11438        let err = c.validate_autores().unwrap_err();
11439        assert!(
11440            matches!(err, ManifestError::AutorInvalid { .. }),
11441            "got {err:?}",
11442        );
11443    }
11444
11445    #[test]
11446    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
11447        // Diagnostic-shape pin on the new shape arm (peer with
11448        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
11449        // the rendered Display surfaces both the offending slot name
11450        // and the offending value verbatim, so a `feira lint` run
11451        // points the author at the exact `:autores` entry to fix.
11452        let c = caixa_with_autores(vec!["alice\nbob"]);
11453        let rendered = c.validate_autores().unwrap_err().to_string();
11454        assert!(
11455            rendered.contains(":autores"),
11456            "diagnostic must name the offending slot: {rendered}",
11457        );
11458        assert!(
11459            rendered.contains("alice\\nbob"),
11460            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11461        );
11462    }
11463
11464    #[test]
11465    fn validate_autores_rejects_at_129_byte_boundary() {
11466        // The 128-byte cap pin — boundary-exceeding case rejected,
11467        // boundary-accepting case passes. Mirrors the peer
11468        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
11469        // substrate-side pin, surfaced at the per-axis caller so the
11470        // cap propagates through validate end-to-end. Constructed as
11471        // a single all-`a` token so only the cap arm fires.
11472        let max_ok = "a".repeat(128);
11473        let c = caixa_with_autores(vec![max_ok.as_str()]);
11474        c.validate_autores().unwrap();
11475        let too_long = "a".repeat(129);
11476        let c = caixa_with_autores(vec![too_long.as_str()]);
11477        let err = c.validate_autores().unwrap_err();
11478        let ManifestError::AutorInvalid { reason, .. } = err else {
11479            panic!("expected AutorInvalid, got {err:?}");
11480        };
11481        assert!(reason.contains("128"), "got: {reason}");
11482        assert!(reason.contains("129"), "got: {reason}");
11483    }
11484
11485    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
11486
11487    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
11488        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11489        c.repositorio = repositorio.map(String::from);
11490        c
11491    }
11492
11493    #[test]
11494    fn validate_repositorio_accepts_none() {
11495        // The omit-the-slot identity: `:repositorio` is optional. The
11496        // gate is a no-op when the author didn't declare a value —
11497        // every caixa without a `:repositorio` line trivially passes,
11498        // and the substrate-side renderers fall back to their
11499        // documented placeholder (`caixa-helm`'s `home: None`,
11500        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
11501        // URL). Mirrors the peer `validate_restart_window_accepts_none`
11502        // posture on the other `Option<String>` Caixa slot.
11503        let c = caixa_with_repositorio(None);
11504        c.validate_repositorio().unwrap();
11505    }
11506
11507    #[test]
11508    fn validate_repositorio_accepts_canonical_forms() {
11509        // Positive control sweep across every documented `:repositorio`
11510        // authoring shape — the same union the shared
11511        // `crate::render::is_git_repo_url` predicate accepts and the
11512        // peer `:deps :fonte :repo` axis already routes through.
11513        // Covers the `github:` shorthand (the canonical pleme-io
11514        // convention used in the `:repositorio` field of every
11515        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
11516        // `examples/`), the `https://…` URL the README quickstart uses,
11517        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
11518        // `file://` URL schemes the shared predicate documents.
11519        for repo in [
11520            "github:pleme-io/hello-rio",
11521            "github:pleme-io/checkout",
11522            "https://github.com/pleme-io/hello-rio",
11523            "ssh://git@github.com/pleme-io/hello-rio.git",
11524            "git://github.com/pleme-io/hello-rio.git",
11525            "git@github.com:pleme-io/hello-rio.git",
11526            "file:///srv/pleme/hello-rio",
11527        ] {
11528            let c = caixa_with_repositorio(Some(repo));
11529            c.validate_repositorio()
11530                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
11531        }
11532    }
11533
11534    #[test]
11535    fn validate_repositorio_rejects_empty_some() {
11536        // Canonical paste-from-blank-doc footgun. The narrower
11537        // [`ManifestError::RepositorioEmpty`] arm fires before the
11538        // shape predicate is consulted, mirroring the empty-first
11539        // cascade every peer per-axis identity gate uses
11540        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
11541        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
11542        // the empty `Some("")` silently passed the renderer's
11543        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
11544        // on `None`) and landed as `home: ""` in `Chart.yaml` /
11545        // `url: ""` in the FluxCD `GitRepository`.
11546        let c = caixa_with_repositorio(Some(""));
11547        let err = c.validate_repositorio().unwrap_err();
11548        assert!(
11549            matches!(err, ManifestError::RepositorioEmpty),
11550            "got {err:?}",
11551        );
11552    }
11553
11554    #[test]
11555    fn validate_repositorio_rejects_whitespace() {
11556        // Paste-from-doc whitespace footgun. The shared
11557        // `is_git_repo_url` predicate refuses any whitespace byte; a
11558        // trailing space in a `:repositorio` value silently broke
11559        // `git clone '<value> '` at clone time. The diagnostic names
11560        // the offending value verbatim.
11561        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
11562        let err = c.validate_repositorio().unwrap_err();
11563        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
11564            panic!("expected RepositorioInvalid, got {err:?}");
11565        };
11566        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
11567    }
11568
11569    #[test]
11570    fn validate_repositorio_rejects_control_char() {
11571        // Paste-from-multiline-doc CRLF footgun — control characters
11572        // at the URL boundary are a class of subprocess-arg injection
11573        // and break git's URL parser at every porcelain entry point.
11574        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
11575        let err = c.validate_repositorio().unwrap_err();
11576        assert!(
11577            matches!(err, ManifestError::RepositorioInvalid { .. }),
11578            "got {err:?}",
11579        );
11580    }
11581
11582    #[test]
11583    fn validate_repositorio_rejects_leading_dash() {
11584        // Canonical CLI-argument-injection footgun: `git clone <repo>`
11585        // interprets a leading `-` as a CLI flag, so a
11586        // `-upload-pack=…` value escapes the subprocess argument
11587        // boundary. The shared predicate refuses every leading-`-`
11588        // shape at validate time.
11589        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
11590        let err = c.validate_repositorio().unwrap_err();
11591        assert!(
11592            matches!(err, ManifestError::RepositorioInvalid { .. }),
11593            "got {err:?}",
11594        );
11595    }
11596
11597    #[test]
11598    fn validate_repositorio_rejects_missing_colon_separator() {
11599        // The bare `org/repo` ambiguity footgun — `git clone` reads
11600        // a no-`:` form as a relative filesystem path rather than the
11601        // GitHub-shorthand expansion the author probably intended.
11602        // The shared predicate refuses every shape without a `:`
11603        // separator.
11604        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
11605        let err = c.validate_repositorio().unwrap_err();
11606        assert!(
11607            matches!(err, ManifestError::RepositorioInvalid { .. }),
11608            "got {err:?}",
11609        );
11610    }
11611
11612    #[test]
11613    fn validate_repositorio_rejects_fragment_anchor() {
11614        // Paste-from-browser-address-bar footgun on the
11615        // `:repositorio` axis — an author copies a GitHub permalink
11616        // to a README section / line-permalink and forgets to trim
11617        // the `#fragment` tail. The shared `is_git_repo_url`
11618        // predicate refuses the byte at the URL-grammar layer
11619        // (libcurl strips the fragment before opening the
11620        // transport, so the byte rides verbatim into the rendered
11621        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
11622        // fields but is silently dropped on the wire — two
11623        // manifest variants whose values differ only in their
11624        // fragment anchor lock to two distinct rendered artifacts
11625        // for the byte-identical clone, defeating the THEORY.md
11626        // §V.2 render-determinism contract on the `:repositorio`
11627        // axis the peer `:fonte :repo` axis already closes).
11628        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
11629        let err = c.validate_repositorio().unwrap_err();
11630        let ManifestError::RepositorioInvalid {
11631            repositorio,
11632            reason,
11633        } = err
11634        else {
11635            panic!("expected RepositorioInvalid, got {err:?}");
11636        };
11637        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
11638        assert!(
11639            reason.contains("must not contain `#`"),
11640            "reason must surface the fragment-`#` arm, got {reason:?}"
11641        );
11642    }
11643
11644    #[test]
11645    fn validate_repositorio_rejects_query_string() {
11646        // Paste-from-browser-address-bar footgun on the
11647        // `:repositorio` axis (peer with the a68f818 fragment-`#`
11648        // arm on the same axis). An author copies a GitHub tab
11649        // deep-link out of the address bar and forgets to trim
11650        // the `?tab=…` query tail. The shared `is_git_repo_url`
11651        // predicate refuses the byte at the URL-grammar layer
11652        // (GitHub / GitLab / Bitbucket silently ignore the
11653        // `?query` tail and serve the same repo regardless, so
11654        // the byte rides verbatim into the rendered `Chart.yaml`
11655        // `home:` and FluxCD `GitRepository` `url:` fields but
11656        // is silently masked at the wire — two manifest variants
11657        // whose values differ only in their query tail lock to
11658        // two distinct rendered artifacts for the byte-identical
11659        // clone, defeating the THEORY.md §V.2 render-determinism
11660        // contract on the `:repositorio` axis the peer `:fonte
11661        // :repo` axis already closes).
11662        let c = caixa_with_repositorio(Some(
11663            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
11664        ));
11665        let err = c.validate_repositorio().unwrap_err();
11666        let ManifestError::RepositorioInvalid {
11667            repositorio,
11668            reason,
11669        } = err
11670        else {
11671            panic!("expected RepositorioInvalid, got {err:?}");
11672        };
11673        assert_eq!(
11674            repositorio,
11675            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
11676        );
11677        assert!(
11678            reason.contains("must not contain `?`"),
11679            "reason must surface the query-`?` arm, got {reason:?}"
11680        );
11681    }
11682
11683    #[test]
11684    fn validate_repositorio_rejects_embedded_backslash() {
11685        // Windows-file-path-confusion footgun on the `:repositorio`
11686        // axis (peer with the prior fragment-`#` / query-`?` arms on
11687        // the same axis, and peer with the new dep-level `:fonte :repo`
11688        // backslash arm on the URL-grammar trajectory). An author
11689        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
11690        // hello-rio` into the `:repositorio` slot, expecting the
11691        // `lareira-<nome>` chart's `home:` field and the FluxCD
11692        // `GitRepository` `url:` field to render the canonical local
11693        // file-URI. The shared `is_git_repo_url` predicate refuses
11694        // the byte at the URL-grammar layer (libcurl silently
11695        // translates `\` → `/` on some platforms and refuses it on
11696        // others, so the byte rides verbatim into the rendered
11697        // artifacts but is silently rewritten or rejected at the wire
11698        // — two manifest variants whose values differ only in
11699        // backslash-vs-forward-slash lock to two distinct rendered
11700        // artifacts for the byte-identical clone, defeating the
11701        // THEORY.md §V.2 render-determinism contract on the
11702        // `:repositorio` axis the peer `:fonte :repo` axis already
11703        // closes).
11704        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
11705        let err = c.validate_repositorio().unwrap_err();
11706        let ManifestError::RepositorioInvalid {
11707            repositorio,
11708            reason,
11709        } = err
11710        else {
11711            panic!("expected RepositorioInvalid, got {err:?}");
11712        };
11713        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
11714        assert!(
11715            reason.contains("must not contain `\\`"),
11716            "reason must surface the backslash-`\\` arm, got {reason:?}"
11717        );
11718    }
11719
11720    #[test]
11721    fn validate_repositorio_rejects_uri_template_placeholder() {
11722        // URI Template (RFC 6570) placeholder footgun on the
11723        // `:repositorio` axis (peer with the prior fragment-`#` /
11724        // query-`?` / backslash-`\` arms on the same axis, and peer
11725        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
11726        // URL-grammar trajectory). An author pastes a quick-start
11727        // README snippet / OpenAPI `servers:` URL / Helm chart
11728        // `home:` template carrying unresolved `{org}` / `{repo}`
11729        // placeholders into the `:repositorio` slot, expecting the
11730        // substrate to resolve the placeholder downstream. The
11731        // shared `is_git_repo_url` predicate refuses the byte at the
11732        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
11733        // `%7B` / `%7D` on the wire, so the byte round-trips
11734        // inconsistently between the rendered `Chart.yaml home:` /
11735        // FluxCD `GitRepository url:` and the resolver's `git clone`
11736        // invocation, defeating the THEORY.md §V.2 render-
11737        // determinism contract on the `:repositorio` axis the peer
11738        // `:fonte :repo` axis already closes; every git porcelain
11739        // entry-point additionally fetches a nonexistent literal-
11740        // `{placeholder}`-named path far from the source caixa.lisp).
11741        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
11742        let err = c.validate_repositorio().unwrap_err();
11743        let ManifestError::RepositorioInvalid {
11744            repositorio,
11745            reason,
11746        } = err
11747        else {
11748            panic!("expected RepositorioInvalid, got {err:?}");
11749        };
11750        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
11751        assert!(
11752            reason.contains("must not contain `{`"),
11753            "reason must surface the open-brace `{{` arm, got {reason:?}"
11754        );
11755        assert!(
11756            reason.contains("URI Template") || reason.contains("RFC 6570"),
11757            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
11758        );
11759    }
11760
11761    #[test]
11762    fn validate_repositorio_empty_takes_precedence_over_shape() {
11763        // Empty-first cascade pin: the empty `Some("")` surfaces the
11764        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
11765        // `RepositorioInvalid`, mirroring the peer
11766        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
11767        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
11768        // `is_git_repo_url` predicate also rejects the empty input
11769        // (defensively, with its own `"must not be empty"` reason),
11770        // but the manifest-layer empty arm runs first to surface the
11771        // narrower diagnostic verbatim.
11772        let c = caixa_with_repositorio(Some(""));
11773        let err = c.validate_repositorio().unwrap_err();
11774        assert!(
11775            matches!(err, ManifestError::RepositorioEmpty),
11776            "got {err:?}",
11777        );
11778    }
11779
11780    #[test]
11781    fn validate_repositorio_diagnostic_carries_offending_value() {
11782        // Diagnostic-shape pin (peer with
11783        // `validate_autores_diagnostic_carries_offending_author`): the
11784        // error's Display surfaces the offending value + slot name
11785        // verbatim, so a `feira lint` run can render the diagnostic
11786        // without re-parsing and the author can grep their caixa.lisp
11787        // for the offending `:repositorio` value.
11788        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
11789        let rendered = c.validate_repositorio().unwrap_err().to_string();
11790        assert!(
11791            rendered.contains(":repositorio"),
11792            "diagnostic must name the offending slot: {rendered}",
11793        );
11794        assert!(
11795            rendered.contains("pleme-io/hello-rio"),
11796            "diagnostic must quote the offending value: {rendered}",
11797        );
11798    }
11799
11800    // ── validate_descricao — universal-axis Chart.yaml description shape ──
11801
11802    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
11803        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11804        c.descricao = descricao.map(String::from);
11805        c
11806    }
11807
11808    #[test]
11809    fn validate_descricao_accepts_none() {
11810        // The omit-the-slot identity: `:descricao` is optional. The
11811        // gate is a no-op when the author didn't declare a value —
11812        // every caixa without a `:descricao` line trivially passes,
11813        // and the substrate-side renderers fall back to their
11814        // documented `caixa.nome`-derived placeholder. Mirrors the
11815        // peer `validate_repositorio_accepts_none` posture on the
11816        // sibling `Option<String>` Caixa slot.
11817        let c = caixa_with_descricao(None);
11818        c.validate_descricao().unwrap();
11819    }
11820
11821    #[test]
11822    fn validate_descricao_accepts_canonical_summary() {
11823        // Positive control: the canonical pleme-io descricao shape —
11824        // a short free-form prose summary — passes the gate. Covers
11825        // the fixture shapes the `caixa-helm` / `caixa-flux` /
11826        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
11827        // wasip2 caixa Servico."`, `"Checkout flow."`).
11828        for desc in [
11829            "Canonical Rust→wasm32-wasip2 caixa Servico.",
11830            "Checkout flow.",
11831            "AWS provider caixa for tatara-lisp",
11832            "FIXME — describe this caixa",
11833            "x",
11834        ] {
11835            let c = caixa_with_descricao(Some(desc));
11836            c.validate_descricao()
11837                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
11838        }
11839    }
11840
11841    #[test]
11842    fn validate_descricao_rejects_empty_some() {
11843        // Canonical paste-from-blank-doc footgun. Without this gate
11844        // the empty `Some("")` silently passed the renderer's
11845        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
11846        // on `None`) and landed as `description: ""` in `Chart.yaml`
11847        // and a blank `README.md` header. Mirrors the peer
11848        // [`ManifestError::RepositorioEmpty`] empty-arm on the
11849        // sibling `Option<String>` Caixa slot.
11850        let c = caixa_with_descricao(Some(""));
11851        let err = c.validate_descricao().unwrap_err();
11852        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
11853    }
11854
11855    #[test]
11856    fn validate_descricao_rejects_leading_whitespace() {
11857        // Paste-from-aligned-doc footgun: a leading ASCII space the
11858        // bare empty-arm gate accepted, the shape predicate now
11859        // refuses. The diagnostic carries the offending value
11860        // verbatim (with the leading space preserved) so the author
11861        // can grep their caixa.lisp for the exact `:descricao` line
11862        // and fix the round-trip-inconsistent leading whitespace.
11863        // Mirrors the peer
11864        // `validate_licenca_rejects_leading_whitespace` arm on the
11865        // sibling `:licenca` axis.
11866        let c = caixa_with_descricao(Some(" Checkout flow."));
11867        let err = c.validate_descricao().unwrap_err();
11868        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
11869            panic!("expected DescricaoInvalid, got {err:?}");
11870        };
11871        assert_eq!(descricao, " Checkout flow.");
11872        assert!(reason.contains("whitespace"), "got: {reason:?}");
11873    }
11874
11875    #[test]
11876    fn validate_descricao_rejects_trailing_whitespace() {
11877        // Paste-from-doc footgun: a trailing ASCII space the bare
11878        // empty-arm gate accepted, the shape predicate now refuses.
11879        let c = caixa_with_descricao(Some("Checkout flow. "));
11880        let err = c.validate_descricao().unwrap_err();
11881        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
11882            panic!("expected DescricaoInvalid, got {err:?}");
11883        };
11884        assert_eq!(descricao, "Checkout flow. ");
11885        assert!(reason.contains("whitespace"), "got: {reason:?}");
11886    }
11887
11888    #[test]
11889    fn validate_descricao_rejects_embedded_newline() {
11890        // Paste-from-multiline-doc footgun: an embedded LF the bare
11891        // empty-arm gate accepted, the shape predicate now refuses.
11892        // Without this gate the embedded newline silently landed in
11893        // the rendered Chart.yaml as a multi-line YAML block scalar,
11894        // and every chart-aware UI (`helm list`, `helm search`,
11895        // Artifact Hub) renders the description in a single-line
11896        // column so the embedded newline is silently dropped at
11897        // every downstream consumer.
11898        let c = caixa_with_descricao(Some("Checkout\nflow."));
11899        let err = c.validate_descricao().unwrap_err();
11900        assert!(
11901            matches!(err, ManifestError::DescricaoInvalid { .. }),
11902            "got {err:?}",
11903        );
11904        assert!(err.to_string().contains("newline"), "got {err}");
11905    }
11906
11907    #[test]
11908    fn validate_descricao_rejects_embedded_carriage_return() {
11909        // Paste-from-Windows-CRLF-doc footgun.
11910        let c = caixa_with_descricao(Some("Checkout\rflow."));
11911        let err = c.validate_descricao().unwrap_err();
11912        assert!(
11913            matches!(err, ManifestError::DescricaoInvalid { .. }),
11914            "got {err:?}",
11915        );
11916        assert!(err.to_string().contains("carriage return"), "got {err}");
11917    }
11918
11919    #[test]
11920    fn validate_descricao_rejects_embedded_tab() {
11921        // Tab-from-aligned-doc footgun.
11922        let c = caixa_with_descricao(Some("Checkout\tflow."));
11923        let err = c.validate_descricao().unwrap_err();
11924        assert!(
11925            matches!(err, ManifestError::DescricaoInvalid { .. }),
11926            "got {err:?}",
11927        );
11928        assert!(err.to_string().contains("tab"), "got {err}");
11929    }
11930
11931    #[test]
11932    fn validate_descricao_rejects_embedded_control_bytes() {
11933        // Paste-from-binary-blob footgun: every other control byte
11934        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
11935        // the peer SPDX-expression control-byte arm.
11936        for s in [
11937            "Checkout\x00flow.",
11938            "Checkout\x07flow.",
11939            "Checkout\x1bflow.",
11940            "Checkout\x7fflow.",
11941        ] {
11942            let c = caixa_with_descricao(Some(s));
11943            let err = c.validate_descricao().unwrap_err();
11944            assert!(
11945                matches!(err, ManifestError::DescricaoInvalid { .. }),
11946                "{s:?} got {err:?}",
11947            );
11948            assert!(
11949                err.to_string().contains("control character"),
11950                "{s:?} got {err}",
11951            );
11952        }
11953    }
11954
11955    #[test]
11956    fn validate_descricao_accepts_unicode_prose() {
11957        // Positive control: Unicode prose is accepted — the
11958        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
11959        // and `Caixa::template`'s `"FIXME — describe this caixa"`
11960        // scaffold every `feira init` emits must continue to pass.
11961        for s in [
11962            "Canonical Rust→wasm32-wasip2 caixa Servico.",
11963            "FIXME — describe this caixa",
11964            "Caixa pour le projet tâche",
11965            "日本語の説明",
11966        ] {
11967            let c = caixa_with_descricao(Some(s));
11968            c.validate_descricao()
11969                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
11970        }
11971    }
11972
11973    #[test]
11974    fn validate_descricao_empty_takes_precedence_over_shape() {
11975        // Cascade pin: a `Some("")` surfaces the narrower
11976        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
11977        // shape-predicate arm. Mirrors the peer
11978        // `validate_licenca_empty_takes_precedence_over_shape` pin
11979        // on the sibling `:licenca` axis.
11980        let c = caixa_with_descricao(Some(""));
11981        let err = c.validate_descricao().unwrap_err();
11982        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
11983    }
11984
11985    #[test]
11986    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
11987        // Diagnostic-shape pin: the error's Display surfaces both
11988        // the `:descricao` slot name and the offending value
11989        // verbatim, so a `feira lint` run can render the diagnostic
11990        // without re-parsing and the author can grep their caixa.lisp
11991        // for the offending `:descricao` line. Mirrors the peer
11992        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
11993        // pin (ee2e888) on the sibling `:licenca` axis.
11994        // The `{descricao:?}` Debug format escapes embedded control
11995        // bytes; the quoted offending value surfaces as
11996        // `"Checkout\nflow."` (literal backslash-n) in the rendered
11997        // diagnostic. The author can grep their caixa.lisp for the
11998        // literal `Checkout` summary prefix.
11999        let c = caixa_with_descricao(Some("Checkout\nflow."));
12000        let rendered = c.validate_descricao().unwrap_err().to_string();
12001        assert!(
12002            rendered.contains(":descricao"),
12003            "diagnostic must name the offending slot: {rendered}",
12004        );
12005        assert!(
12006            rendered.contains("Checkout\\nflow."),
12007            "diagnostic must quote the offending value (debug-escaped): {rendered}",
12008        );
12009    }
12010
12011    #[test]
12012    fn validate_descricao_template_passes() {
12013        // Round-trip pin: the bare `Caixa::template` shape carries
12014        // `:descricao "FIXME — describe this caixa"` (a non-empty
12015        // sentinel), so the template-derived Caixa passes the gate by
12016        // construction. A future template-shape change that omits or
12017        // empties `:descricao` would surface here as a regression.
12018        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12019        c.validate_descricao().unwrap();
12020    }
12021
12022    #[test]
12023    fn validate_descricao_diagnostic_names_offending_slot() {
12024        // Diagnostic-shape pin (peer with
12025        // `validate_repositorio_diagnostic_carries_offending_value`):
12026        // the error's Display surfaces the `:descricao` slot name
12027        // verbatim, so a `feira lint` run can render the diagnostic
12028        // without re-parsing and the author can grep their caixa.lisp
12029        // for the offending `:descricao` line.
12030        let c = caixa_with_descricao(Some(""));
12031        let rendered = c.validate_descricao().unwrap_err().to_string();
12032        assert!(
12033            rendered.contains(":descricao"),
12034            "diagnostic must name the offending slot: {rendered}",
12035        );
12036    }
12037
12038    // ── validate_licenca — universal-axis chart README license shape ──
12039
12040    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
12041        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12042        c.licenca = licenca.map(String::from);
12043        c
12044    }
12045
12046    #[test]
12047    fn validate_licenca_accepts_none() {
12048        // The omit-the-slot identity: `:licenca` is optional. The
12049        // gate is a no-op when the author didn't declare a value —
12050        // every caixa without a `:licenca` line trivially passes,
12051        // and the substrate-side `caixa-helm` renderer falls back to
12052        // the documented `"MIT"` placeholder. Mirrors the peer
12053        // `validate_descricao_accepts_none` posture on the sibling
12054        // `Option<String>` Caixa slot.
12055        let c = caixa_with_licenca(None);
12056        c.validate_licenca().unwrap();
12057    }
12058
12059    #[test]
12060    fn validate_licenca_accepts_canonical_expressions() {
12061        // Positive control: every canonical SPDX expression shape
12062        // pleme-io carries in its existing fixtures + the canonical
12063        // SPDX dual-license / with-exception / `+`-suffix / grouped /
12064        // user-defined-reference shapes all pass the gate. Covers
12065        // the single-license, `OR`-compound, `AND`-compound,
12066        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
12067        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
12068        // production the SPDX 2.1 expression grammar admits that
12069        // sits within the alphabet floor the
12070        // `is_spdx_expression_shape` predicate enforces.
12071        for lic in [
12072            "MIT",
12073            "Apache-2.0",
12074            "Apache-2.0 OR MIT",
12075            "Apache-2.0 AND MIT",
12076            "BSD-3-Clause",
12077            "MPL-2.0",
12078            "GPL-3.0-or-later",
12079            "GPL-2.0+",
12080            "Apache-2.0 WITH LLVM-exception",
12081            "(MIT OR Apache-2.0) AND BSD-3-Clause",
12082            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
12083            "LicenseRef-MyLicense",
12084            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
12085            "x",
12086        ] {
12087            let c = caixa_with_licenca(Some(lic));
12088            c.validate_licenca()
12089                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
12090        }
12091    }
12092
12093    #[test]
12094    fn validate_licenca_rejects_trailing_whitespace() {
12095        // Paste-from-doc whitespace footgun. A trailing space in the
12096        // `:licenca` value would silently break a downstream SPDX
12097        // parser that splits on exact `AND` / `OR` / `WITH` keyword
12098        // boundaries. The shape predicate refuses every trailing
12099        // whitespace byte by construction. Peer with
12100        // `validate_repositorio_rejects_whitespace` and
12101        // `validate_edicao_rejects_trailing_whitespace`.
12102        let c = caixa_with_licenca(Some("MIT "));
12103        let err = c.validate_licenca().unwrap_err();
12104        let ManifestError::LicencaInvalid { licenca, .. } = err else {
12105            panic!("expected LicencaInvalid, got {err:?}");
12106        };
12107        assert_eq!(licenca, "MIT ");
12108    }
12109
12110    #[test]
12111    fn validate_licenca_rejects_leading_whitespace() {
12112        // Symmetric paste-from-doc whitespace footgun on the leading
12113        // boundary — the gate refuses every shape that starts with a
12114        // space byte by construction. Peer with
12115        // `validate_edicao_rejects_leading_whitespace`.
12116        let c = caixa_with_licenca(Some(" MIT"));
12117        let err = c.validate_licenca().unwrap_err();
12118        assert!(
12119            matches!(err, ManifestError::LicencaInvalid { .. }),
12120            "got {err:?}",
12121        );
12122    }
12123
12124    #[test]
12125    fn validate_licenca_rejects_control_char() {
12126        // Paste-from-multiline-doc CRLF footgun — control characters
12127        // at the value boundary land as a malformed line in the
12128        // rendered chart `README.md` `## License` section. Peer with
12129        // `validate_repositorio_rejects_control_char` and
12130        // `validate_edicao_rejects_control_char`.
12131        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
12132            let c = caixa_with_licenca(Some(lic));
12133            let err = c.validate_licenca().unwrap_err();
12134            assert!(
12135                matches!(err, ManifestError::LicencaInvalid { .. }),
12136                "expected LicencaInvalid on {lic:?}, got {err:?}",
12137            );
12138        }
12139    }
12140
12141    #[test]
12142    fn validate_licenca_rejects_tab() {
12143        // Tab-from-aligned-doc footgun — SPDX expressions use a
12144        // single ASCII space between tokens; a tab breaks every
12145        // downstream SPDX parser that splits on exact `" "`
12146        // boundaries.
12147        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
12148        let err = c.validate_licenca().unwrap_err();
12149        assert!(
12150            matches!(err, ManifestError::LicencaInvalid { .. }),
12151            "got {err:?}",
12152        );
12153    }
12154
12155    #[test]
12156    fn validate_licenca_rejects_non_ascii() {
12157        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
12158        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
12159        // ".")` production. The shape predicate refuses every
12160        // non-ASCII byte by construction; peer with
12161        // `validate_edicao_rejects_non_ascii_lookalike`.
12162        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
12163            let c = caixa_with_licenca(Some(lic));
12164            let err = c.validate_licenca().unwrap_err();
12165            assert!(
12166                matches!(err, ManifestError::LicencaInvalid { .. }),
12167                "expected LicencaInvalid on {lic:?}, got {err:?}",
12168            );
12169        }
12170    }
12171
12172    #[test]
12173    fn validate_licenca_rejects_underscore() {
12174        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
12175        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
12176        // snake-case identifier conventions that don't apply to the
12177        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
12178        // "-" / "."`). The shape predicate refuses every underscore
12179        // byte by construction.
12180        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
12181            let c = caixa_with_licenca(Some(lic));
12182            let err = c.validate_licenca().unwrap_err();
12183            assert!(
12184                matches!(err, ManifestError::LicencaInvalid { .. }),
12185                "expected LicencaInvalid on {lic:?}, got {err:?}",
12186            );
12187        }
12188    }
12189
12190    #[test]
12191    fn validate_licenca_rejects_comma_separator() {
12192        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
12193        // SPDX expressions compose multiple licenses via `AND` / `OR`
12194        // keywords, not the comma separator. The shape predicate
12195        // refuses every comma byte by construction.
12196        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
12197            let c = caixa_with_licenca(Some(lic));
12198            let err = c.validate_licenca().unwrap_err();
12199            assert!(
12200                matches!(err, ManifestError::LicencaInvalid { .. }),
12201                "expected LicencaInvalid on {lic:?}, got {err:?}",
12202            );
12203        }
12204    }
12205
12206    #[test]
12207    fn validate_licenca_rejects_slash_dual_license() {
12208        // Slash-dual-license colloquial idiom footgun — the
12209        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
12210        // `package.license` field but non-SPDX; the SPDX equivalent
12211        // is `MIT OR Apache-2.0`. The shape predicate refuses every
12212        // forward-slash byte by construction.
12213        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
12214            let c = caixa_with_licenca(Some(lic));
12215            let err = c.validate_licenca().unwrap_err();
12216            assert!(
12217                matches!(err, ManifestError::LicencaInvalid { .. }),
12218                "expected LicencaInvalid on {lic:?}, got {err:?}",
12219            );
12220        }
12221    }
12222
12223    #[test]
12224    fn validate_licenca_rejects_semicolon_separator() {
12225        // Semicolon-list-separator confusion footgun — adjacent to
12226        // the comma-separator idiom, every list-separator-belongs-
12227        // to-list-grammar confusion lands here.
12228        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
12229        let err = c.validate_licenca().unwrap_err();
12230        assert!(
12231            matches!(err, ManifestError::LicencaInvalid { .. }),
12232            "got {err:?}",
12233        );
12234    }
12235
12236    #[test]
12237    fn validate_licenca_empty_takes_precedence_over_shape() {
12238        // Empty-first cascade pin: the empty `Some("")` surfaces the
12239        // narrower `LicencaEmpty` not the shape-predicate-wrapped
12240        // `LicencaInvalid`, mirroring the peer
12241        // `validate_edicao_empty_takes_precedence_over_shape` and
12242        // `validate_repositorio_empty_takes_precedence_over_shape`
12243        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
12244        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
12245        // The shape predicate also refuses the empty input
12246        // (defensively — `"must not be empty"`), but the manifest-
12247        // layer empty arm runs first to surface the narrower
12248        // diagnostic verbatim.
12249        let c = caixa_with_licenca(Some(""));
12250        let err = c.validate_licenca().unwrap_err();
12251        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12252    }
12253
12254    #[test]
12255    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
12256        // Diagnostic-shape pin on the shape-predicate arm (peer with
12257        // `validate_edicao_invalid_diagnostic_carries_offending_value`
12258        // and `validate_repositorio_diagnostic_carries_offending_value`):
12259        // the error's Display surfaces the offending value + slot
12260        // name verbatim, so a `feira lint` run can render the
12261        // diagnostic without re-parsing and the author can grep
12262        // their caixa.lisp for the offending `:licenca` value.
12263        let c = caixa_with_licenca(Some("Apache_2.0"));
12264        let rendered = c.validate_licenca().unwrap_err().to_string();
12265        assert!(
12266            rendered.contains(":licenca"),
12267            "diagnostic must name the offending slot: {rendered}",
12268        );
12269        assert!(
12270            rendered.contains("Apache_2.0"),
12271            "diagnostic must quote the offending value: {rendered}",
12272        );
12273    }
12274
12275    #[test]
12276    fn validate_licenca_rejects_empty_some() {
12277        // Canonical paste-from-blank-doc footgun. Without this gate
12278        // the empty `Some("")` silently passed the renderer's
12279        // `Option::unwrap_or_else(|| "MIT".into())` (which only
12280        // fires on `None`) and landed as a bare trailing period in
12281        // the rendered chart `README.md` `## License` section.
12282        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
12283        // arm on the sibling `Option<String>` Caixa slot.
12284        let c = caixa_with_licenca(Some(""));
12285        let err = c.validate_licenca().unwrap_err();
12286        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12287    }
12288
12289    #[test]
12290    fn validate_licenca_template_passes() {
12291        // Round-trip pin: the bare `Caixa::template` shape (whether
12292        // it carries `:licenca` or omits it) passes the gate by
12293        // construction. A future template-shape change that
12294        // introduced `(:licenca "")` would surface here as a
12295        // regression. Mirrors the peer
12296        // `validate_descricao_template_passes` pin.
12297        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12298        c.validate_licenca().unwrap();
12299    }
12300
12301    #[test]
12302    fn validate_licenca_diagnostic_names_offending_slot() {
12303        // Diagnostic-shape pin (peer with
12304        // `validate_descricao_diagnostic_names_offending_slot`):
12305        // the error's Display surfaces the `:licenca` slot name
12306        // verbatim, so a `feira lint` run can render the diagnostic
12307        // without re-parsing and the author can grep their caixa.lisp
12308        // for the offending `:licenca` line.
12309        let c = caixa_with_licenca(Some(""));
12310        let rendered = c.validate_licenca().unwrap_err().to_string();
12311        assert!(
12312            rendered.contains(":licenca"),
12313            "diagnostic must name the offending slot: {rendered}",
12314        );
12315    }
12316
12317    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
12318
12319    #[test]
12320    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
12321        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
12322        // pin: [`Caixa::licenca`] must return the `:licenca` typed
12323        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12324        // raw `self.licenca.as_deref()` access across every
12325        // representative value in the accept-set — `None` (the "omit
12326        // the slot to defer to the caixa-helm renderer's `MIT`
12327        // fallback" arm every existing fixture without a `:licenca`
12328        // line carries), `Some("")` (a past-the-guard sentinel that
12329        // pins the accessor doesn't perform a silent
12330        // `Some("") → None` collapse on the empty arm — validate
12331        // rejects `Some("")` through `LicencaEmpty` but the accessor
12332        // must ship the raw slot verbatim so a validate-time gate
12333        // regression surfaces at the caixa-helm emit boundary rather
12334        // than being silently absorbed into the fallback), `Some("MIT")`
12335        // (the canonical single-license shape every `feira init`
12336        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
12337        // canonical `OR`-compound shape the peer
12338        // `validate_licenca_accepts_canonical_expressions` positive
12339        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
12340        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
12341        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
12342        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
12343        // guard sentinels — validate rejects each through
12344        // `LicencaInvalid` but the accessor must ship the raw slot
12345        // verbatim).
12346        //
12347        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
12348        // accessor pin on the substrate primitive — opens the "outer
12349        // [`Caixa`] `Option<&str>` scalar" projection pattern the
12350        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
12351        // future lifts fold on. Sibling in shape to the peer per-`:placement`
12352        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12353        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12354        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12355        // axes, extended onto the outer top-level [`Caixa`] universal-
12356        // axis surface. Pins against a future silent detour that
12357        // returned an owned `Option<String>` (which would type-check
12358        // but silently allocate on every accessor call, breaking the
12359        // zero-cost projection every peer sibling accessor carries), a
12360        // `Some("") → None` collapse (which would silently absorb the
12361        // `LicencaEmpty` refusal case at the accessor boundary and the
12362        // caixa-helm emit path would silently fall back to `"MIT"` on
12363        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
12364        // `None → Some("MIT")` collapse (which would silently reify
12365        // the caixa-helm renderer's `"MIT"` fallback at the accessor
12366        // boundary and every downstream consumer keying off the
12367        // `Option::is_none()` discriminator would lose the "author
12368        // omitted the slot" signal).
12369        for licenca in [
12370            None,
12371            Some(""),
12372            Some("MIT"),
12373            Some("Apache-2.0 OR MIT"),
12374            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
12375            Some("MIT "),
12376            Some(" MIT"),
12377            Some("MIT\n"),
12378            Some("Apache_2.0"),
12379            Some("MIT,Apache-2.0"),
12380        ] {
12381            let c = caixa_with_licenca(licenca);
12382            assert_eq!(
12383                c.licenca(),
12384                licenca,
12385                "Caixa::licenca must return :licenca verbatim (got {:?}, \
12386                 expected {licenca:?})",
12387                c.licenca(),
12388            );
12389            assert_eq!(
12390                c.licenca(),
12391                c.licenca.as_deref(),
12392                "Caixa::licenca must byte-equal the raw \
12393                 `self.licenca.as_deref()` field access across every \
12394                 value in the Option<&str> accept-set",
12395            );
12396        }
12397    }
12398
12399    #[test]
12400    fn validate_licenca_empty_arm_routes_through_accessor() {
12401        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
12402        // must key off [`Caixa::licenca`], not the raw
12403        // `self.licenca.as_deref()` field access. Structurally: a
12404        // `Caixa { licenca: Some(""), .. }` must surface the
12405        // `LicencaEmpty` refusal exactly, and a
12406        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
12407        // single-license form) must pass validate. The pair jointly
12408        // pins the accessor + validate-gate composition: any future
12409        // silent detour that had the accessor return `None` on the
12410        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
12411        // silently absorb the `LicencaEmpty` refusal at the accessor
12412        // boundary and the validate gate would accept a struct-literal
12413        // `Caixa { licenca: Some(""), .. }` — the composition pin
12414        // catches that at caixa-core build time.
12415        //
12416        // Peer of the per-`:politicas :circuit-breaker`
12417        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12418        // accessor-composition pin
12419        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12420        // on the sibling per-M3-mesh-slot required-`u32` axis — same
12421        // "the validate / shape-gate predicate must route through the
12422        // substrate-primitive typed dispatch" discipline extended onto
12423        // the outer top-level [`Caixa`] universal-axis
12424        // `Option<&str>`-composition surface.
12425        let c = caixa_with_licenca(Some(""));
12426        assert!(
12427            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
12428            "validate_licenca must reject licenca == Some(\"\") with \
12429             LicencaEmpty — the accessor and the validate gate must \
12430             route through the same substrate-primitive typed dispatch \
12431             on the :licenca empty arm",
12432        );
12433        let c = caixa_with_licenca(Some("MIT"));
12434        assert!(
12435            c.validate_licenca().is_ok(),
12436            "validate_licenca must accept licenca == Some(\"MIT\") \
12437             (the canonical single-license SPDX shape)",
12438        );
12439    }
12440
12441    #[test]
12442    fn licenca_projects_option_str_by_borrow() {
12443        // The by-borrow pin: [`Caixa::licenca`] returns
12444        // `Option<&str>` by borrow — the `&str` borrows the underlying
12445        // `String` storage of the `Option<String>` slot and the
12446        // accessor must not allocate a fresh `String` on every call.
12447        // Peer of the per-`:placement`
12448        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12449        // borrow pin on the peer per-M3-mesh-slot
12450        // `Option<&str>`-return axis, extended onto the outer top-
12451        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12452        // accessor's returned `&str` must borrow from `&self` (the
12453        // returned reference's lifetime is tied to `&self`), and
12454        // calling the accessor twice on the same [`Caixa`] must yield
12455        // the same `Option<&str>` verbatim (idempotent, no side
12456        // effects on `&self`).
12457        //
12458        // Pins against a future silent detour that returned an owned
12459        // `Option<String>` (which would type-check but silently
12460        // allocate on every call, breaking the zero-cost projection
12461        // every peer sibling accessor carries), or a one-arm-only
12462        // accessor that returned a saturating value on some sentinel
12463        // input (breaking the pass-through invariant the sibling
12464        // required-scalar accessors carry).
12465        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
12466            let c = caixa_with_licenca(licenca);
12467            let first = c.licenca();
12468            let second = c.licenca();
12469            assert_eq!(
12470                first, second,
12471                "Caixa::licenca must be idempotent — two successive \
12472                 calls on the same &self must return the same \
12473                 Option<&str>",
12474            );
12475            assert_eq!(
12476                first, licenca,
12477                "Caixa::licenca must return :licenca verbatim by \
12478                 borrow — got {first:?}, expected {licenca:?}",
12479            );
12480        }
12481    }
12482
12483    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
12484
12485    #[test]
12486    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
12487        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
12488        // pin: [`Caixa::repositorio`] must return the `:repositorio`
12489        // typed byte-string verbatim as an `Option<&str>`, byte-equal
12490        // to the raw `self.repositorio.as_deref()` access across every
12491        // representative value in the accept-set — `None` (the "omit
12492        // the slot to defer to the per-renderer placeholder" arm every
12493        // existing fixture without a `:repositorio` line carries),
12494        // `Some("")` (a past-the-guard sentinel that pins the accessor
12495        // doesn't perform a silent `Some("") → None` collapse on the
12496        // empty arm — validate rejects `Some("")` through
12497        // `RepositorioEmpty` but the accessor must ship the raw slot
12498        // verbatim so a validate-time gate regression surfaces at the
12499        // caixa-helm / caixa-flux emit boundary rather than being
12500        // silently absorbed into the per-renderer fallback),
12501        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
12502        // shorthand every existing manifest fixture across
12503        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
12504        // `Some("https://github.com/pleme-io/checkout")` (the canonical
12505        // `https://` URL the README quickstart uses),
12506        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
12507        // `Some("git://github.com/pleme-io/checkout.git")` /
12508        // `Some("git@github.com:pleme-io/checkout.git")` /
12509        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
12510        // github scheme the shared `is_git_repo_url` predicate
12511        // documents), and five past-the-guard sentinels for the
12512        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
12513        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
12514        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
12515        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
12516        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
12517        // sentinels pin the accessor doesn't silently absorb the
12518        // refusal cases into a fallback).
12519        //
12520        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
12521        // accessor pin on the substrate primitive — sibling of the peer
12522        // [`Caixa::licenca`] (6d5bc28) pin
12523        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
12524        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
12525        // projection pin pattern this pin folds on. Sibling in shape to
12526        // the peer per-`:placement`
12527        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12528        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12529        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12530        // axes, extended onto the outer top-level [`Caixa`] universal-
12531        // axis surface. Pins against a future silent detour that
12532        // returned an owned `Option<String>` (which would type-check
12533        // but silently allocate on every accessor call, breaking the
12534        // zero-cost projection every peer sibling accessor carries), a
12535        // `Some("") → None` collapse (which would silently absorb the
12536        // `RepositorioEmpty` refusal case at the accessor boundary and
12537        // the caixa-helm `Chart.yaml` `home:` fold would silently
12538        // render a `home: null` / omitted field on a struct-literal
12539        // `Caixa { repositorio: Some(""), .. }`), or a
12540        // `None → Some(<default>)` collapse (which would silently reify
12541        // the per-renderer fallback at the accessor boundary and every
12542        // downstream consumer keying off the `Option::is_none()`
12543        // discriminator would lose the "author omitted the slot"
12544        // signal).
12545        for repositorio in [
12546            None,
12547            Some(""),
12548            Some("github:pleme-io/hello-rio"),
12549            Some("https://github.com/pleme-io/checkout"),
12550            Some("ssh://git@github.com/pleme-io/checkout.git"),
12551            Some("git://github.com/pleme-io/checkout.git"),
12552            Some("git@github.com:pleme-io/checkout.git"),
12553            Some("file:///opt/mirrors/pleme-io/checkout"),
12554            Some("pleme-io/checkout"),
12555            Some("-upload-pack=evil"),
12556            Some("github:pleme-io/checkout?ref=main"),
12557            Some("github:pleme-io/checkout#main"),
12558            Some("github:pleme-io/{tpl}"),
12559        ] {
12560            let c = caixa_with_repositorio(repositorio);
12561            assert_eq!(
12562                c.repositorio(),
12563                repositorio,
12564                "Caixa::repositorio must return :repositorio verbatim \
12565                 (got {:?}, expected {repositorio:?})",
12566                c.repositorio(),
12567            );
12568            assert_eq!(
12569                c.repositorio(),
12570                c.repositorio.as_deref(),
12571                "Caixa::repositorio must byte-equal the raw \
12572                 `self.repositorio.as_deref()` field access across every \
12573                 value in the Option<&str> accept-set",
12574            );
12575        }
12576    }
12577
12578    #[test]
12579    fn validate_repositorio_empty_arm_routes_through_accessor() {
12580        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
12581        // gate must key off [`Caixa::repositorio`], not the raw
12582        // `self.repositorio.as_deref()` field access. Structurally: a
12583        // `Caixa { repositorio: Some(""), .. }` must surface the
12584        // `RepositorioEmpty` refusal exactly, and a
12585        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
12586        // (the canonical `github:` shorthand form) must pass validate.
12587        // The pair jointly pins the accessor + validate-gate
12588        // composition: any future silent detour that had the accessor
12589        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
12590        // collapse) would silently absorb the `RepositorioEmpty` refusal
12591        // at the accessor boundary and the validate gate would accept a
12592        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
12593        // composition pin catches that at caixa-core build time.
12594        //
12595        // Peer of the [`Caixa::licenca`] (6d5bc28)
12596        // `validate_licenca_empty_arm_routes_through_accessor`
12597        // composition pin on the sibling outer top-level [`Caixa`]
12598        // `Option<&str>` universal-axis surface — same "the validate /
12599        // shape-gate predicate must route through the substrate-
12600        // primitive typed dispatch" discipline extended onto the second
12601        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
12602        // composition surface.
12603        let c = caixa_with_repositorio(Some(""));
12604        assert!(
12605            matches!(
12606                c.validate_repositorio(),
12607                Err(ManifestError::RepositorioEmpty),
12608            ),
12609            "validate_repositorio must reject repositorio == Some(\"\") \
12610             with RepositorioEmpty — the accessor and the validate gate \
12611             must route through the same substrate-primitive typed \
12612             dispatch on the :repositorio empty arm",
12613        );
12614        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
12615        assert!(
12616            c.validate_repositorio().is_ok(),
12617            "validate_repositorio must accept repositorio == \
12618             Some(\"github:pleme-io/hello-rio\") (the canonical \
12619             `github:` shorthand git-repo-URL shape)",
12620        );
12621    }
12622
12623    #[test]
12624    fn repositorio_projects_option_str_by_borrow() {
12625        // The by-borrow pin: [`Caixa::repositorio`] returns
12626        // `Option<&str>` by borrow — the `&str` borrows the underlying
12627        // `String` storage of the `Option<String>` slot and the
12628        // accessor must not allocate a fresh `String` on every call.
12629        // Peer of the per-`:placement`
12630        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
12631        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
12632        // `Option<&str>`-return axes, extended onto the second outer
12633        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
12634        // the accessor's returned `&str` must borrow from `&self` (the
12635        // returned reference's lifetime is tied to `&self`), and
12636        // calling the accessor twice on the same [`Caixa`] must yield
12637        // the same `Option<&str>` verbatim (idempotent, no side effects
12638        // on `&self`).
12639        //
12640        // Pins against a future silent detour that returned an owned
12641        // `Option<String>` (which would type-check but silently
12642        // allocate on every call, breaking the zero-cost projection
12643        // every peer sibling accessor carries), or a one-arm-only
12644        // accessor that returned a saturating value on some sentinel
12645        // input (breaking the pass-through invariant the sibling
12646        // required-scalar accessors carry).
12647        for repositorio in [
12648            None,
12649            Some(""),
12650            Some("github:pleme-io/hello-rio"),
12651            Some("https://github.com/pleme-io/checkout"),
12652        ] {
12653            let c = caixa_with_repositorio(repositorio);
12654            let first = c.repositorio();
12655            let second = c.repositorio();
12656            assert_eq!(
12657                first, second,
12658                "Caixa::repositorio must be idempotent — two successive \
12659                 calls on the same &self must return the same \
12660                 Option<&str>",
12661            );
12662            assert_eq!(
12663                first, repositorio,
12664                "Caixa::repositorio must return :repositorio verbatim by \
12665                 borrow — got {first:?}, expected {repositorio:?}",
12666            );
12667        }
12668    }
12669
12670    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
12671
12672    #[test]
12673    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
12674        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
12675        // return the author-declared `:repositorio` byte-string verbatim
12676        // on the `Some` arm — no scheme rewrite, no trailing-slash
12677        // canonicalization, no `github:` → `https://github.com/`
12678        // desugaring. The resolved-URL composer is the projection of
12679        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
12680        // the `String`-return arity every substrate-side field-fill
12681        // consumer keys off; on the `Some` arm the projection is
12682        // `str::to_owned` verbatim, so every accept-set value the
12683        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
12684        // across_permutations` pin covers (`https://…`, `github:…`,
12685        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
12686        // guard sentinel `pleme-io/…`) must survive the accessor
12687        // byte-equal. Pins against a future silent detour that rewrote
12688        // the `github:` shorthand to the `https://github.com/` full URL
12689        // at the accessor boundary (which would silently split the
12690        // resolved-URL surface from the raw [`Caixa::repositorio`]
12691        // accessor's documented pass-through invariant), or a trailing-
12692        // slash normalization (which would silently break the
12693        // FluxCD `GitRepository` `spec.url` byte-exact match every
12694        // downstream consumer keys the source-controller reconcile off).
12695        for repositorio in [
12696            "github:pleme-io/hello-rio",
12697            "https://github.com/pleme-io/checkout",
12698            "ssh://git@github.com/pleme-io/checkout.git",
12699            "git://github.com/pleme-io/checkout.git",
12700            "git@github.com:pleme-io/checkout.git",
12701            "file:///opt/mirrors/pleme-io/checkout",
12702        ] {
12703            let c = caixa_with_repositorio(Some(repositorio));
12704            assert_eq!(
12705                c.canonical_git_url(),
12706                repositorio,
12707                "Caixa::canonical_git_url on the Some arm must return \
12708                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
12709                c.canonical_git_url(),
12710            );
12711        }
12712    }
12713
12714    #[test]
12715    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
12716        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
12717        // `None` arm must emit the substrate's canonical pleme-org github
12718        // URL derived from `caixa.nome()` — `https://github.com/<org>/
12719        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
12720        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
12721        // is the exact byte-image of the prior inline
12722        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
12723        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
12724        // re-derived open-coded. Pins against a future silent detour
12725        // that migrated the `<org>` segment to a different constant (a
12726        // fork rebranding that split off a new
12727        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
12728        // to migrate onto), a scheme change (`https://` → `git://` or
12729        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
12730        // override (which would break the substrate-wide single-source-
12731        // of-truth guarantee this method encodes).
12732        let c = caixa_with_repositorio(None);
12733        let expected = format!(
12734            "https://github.com/{org}/{nome}",
12735            org = crate::DEFAULT_PLEME_GIT_ORG,
12736            nome = c.nome(),
12737        );
12738        assert_eq!(
12739            c.canonical_git_url(),
12740            expected,
12741            "Caixa::canonical_git_url on the None arm must fold through \
12742             the substrate's canonical pleme-org github URL fallback \
12743             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
12744             {:?}, expected {expected:?}",
12745            c.canonical_git_url(),
12746        );
12747    }
12748
12749    #[test]
12750    fn canonical_git_url_byte_matches_manual_composition() {
12751        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
12752        // byte-identically to the manual open-coded
12753        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
12754        //  format!("https://github.com/{org}/{nome}", ...))` composition
12755        // every prior substrate-side caller re-derived. Guards the
12756        // paired-site convergence just applied at caixa-flux's
12757        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
12758        // now routes through this accessor): a future implementation of
12759        // this method that reordered the format arguments, swapped the
12760        // `<org>` constant for a different one, or interposed a
12761        // canonicalization pass on the `Some` arm surfaces here as a
12762        // caixa-core build-time test failure rather than as a downstream
12763        // FluxCD `GitRepository` reconcile mismatch far from this
12764        // method's source.
12765        for repositorio in [
12766            None,
12767            Some("github:pleme-io/hello-rio"),
12768            Some("https://github.com/pleme-io/checkout"),
12769            Some("ssh://git@github.com/pleme-io/checkout.git"),
12770        ] {
12771            let c = caixa_with_repositorio(repositorio);
12772            let manual = c.repositorio().map_or_else(
12773                || {
12774                    format!(
12775                        "https://github.com/{org}/{nome}",
12776                        org = crate::DEFAULT_PLEME_GIT_ORG,
12777                        nome = c.nome(),
12778                    )
12779                },
12780                str::to_owned,
12781            );
12782            assert_eq!(
12783                c.canonical_git_url(),
12784                manual,
12785                "Caixa::canonical_git_url must byte-equal the manual \
12786                 open-coded `repositorio().map(str::to_owned)\
12787                 .unwrap_or_else(|| format!(...))` composition across \
12788                 every representative :repositorio input — got {:?}, \
12789                 expected {manual:?}",
12790                c.canonical_git_url(),
12791            );
12792        }
12793    }
12794
12795    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
12796
12797    #[test]
12798    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
12799        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
12800        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
12801        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
12802        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
12803        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
12804        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
12805        // the `0.0.0` boundary case. Every accept-set value the peer
12806        // validate gate lets through must survive the resolved-tag
12807        // projection byte-equal.
12808        for versao in [
12809            "0.1.0",
12810            "0.0.0",
12811            "1.0.0",
12812            "1.2.3-rc.1",
12813            "1.2.3+build.42",
12814            "1.2.3-rc.1+build.42",
12815        ] {
12816            let c = caixa_with_versao(versao);
12817            let expected = format!(
12818                "{prefix}{versao}",
12819                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12820            );
12821            assert_eq!(
12822                c.publish_tag(),
12823                expected,
12824                "Caixa::publish_tag must compose \
12825                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
12826                 :versao ({versao:?}) verbatim — got {got:?}, \
12827                 expected {expected:?}",
12828                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12829                got = c.publish_tag(),
12830            );
12831        }
12832    }
12833
12834    #[test]
12835    fn publish_tag_starts_with_default_publish_tag_prefix() {
12836        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
12837        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
12838        // byte-string on every input, guarding a hypothetical future
12839        // implementation that migrated the prefix segment to an inline
12840        // literal (`"v"`) that would silently drift from any rebrand of
12841        // the lifted constant. Peer to the sibling caixa-flux
12842        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
12843        // test which pins the same prefix invariant at the reader-side
12844        // `GitRefSpec::Tag` emit site.
12845        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
12846            let c = caixa_with_versao(versao);
12847            let tag = c.publish_tag();
12848            assert!(
12849                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
12850                "Caixa::publish_tag emission {tag:?} must start with \
12851                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
12852                 ({prefix:?})",
12853                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12854            );
12855        }
12856    }
12857
12858    #[test]
12859    fn publish_tag_byte_matches_manual_composition() {
12860        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
12861        // identically to the manual open-coded
12862        // `format!("{prefix}{versao}", prefix =
12863        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
12864        //  caixa.versao())` composition every prior substrate-side
12865        // caller re-derived. Guards the paired-site convergence just
12866        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
12867        // `git_ref` composer (which now routes through this accessor):
12868        // a future implementation of this method that reordered the
12869        // format arguments, swapped the `<prefix>` constant for a
12870        // different one, or interposed a canonicalization pass on the
12871        // `:versao` axis surfaces here as a caixa-core build-time test
12872        // failure rather than as a downstream FluxCD `GitRepository`
12873        // reconcile mismatch far from this method's source.
12874        for versao in [
12875            "0.1.0",
12876            "0.0.0",
12877            "1.2.3-rc.1",
12878            "1.2.3+build.42",
12879            "1.2.3-rc.1+build.42",
12880        ] {
12881            let c = caixa_with_versao(versao);
12882            let manual = format!(
12883                "{prefix}{versao}",
12884                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12885                versao = c.versao(),
12886            );
12887            assert_eq!(
12888                c.publish_tag(),
12889                manual,
12890                "Caixa::publish_tag must byte-equal the manual \
12891                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
12892                 composition across every representative :versao input \
12893                 — got {got:?}, expected {manual:?}",
12894                got = c.publish_tag(),
12895            );
12896        }
12897    }
12898
12899    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
12900
12901    #[test]
12902    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
12903        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
12904        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
12905        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
12906        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
12907        // set sweep documents — single-word, hyphen-joined, version-
12908        // suffixed, single-char, two-char, digit-start, retry-suffixed.
12909        // Every accept-set value the peer validate gate lets through must
12910        // survive the resolved-chart-name projection byte-equal.
12911        for nome in [
12912            "checkout",
12913            "cart-v2",
12914            "a",
12915            "db",
12916            "3rd-party-shim",
12917            "payment-retry",
12918            "0",
12919        ] {
12920            let c = caixa_with_nome(nome);
12921            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
12922            assert_eq!(
12923                c.lareira_chart_name(),
12924                expected,
12925                "Caixa::lareira_chart_name must compose \
12926                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
12927                 :nome ({nome:?}) verbatim — got {got:?}, \
12928                 expected {expected:?}",
12929                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
12930                got = c.lareira_chart_name(),
12931            );
12932        }
12933    }
12934
12935    #[test]
12936    fn lareira_chart_name_starts_with_lifted_prefix() {
12937        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
12938        // must begin with the canonical
12939        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
12940        // input, guarding a hypothetical future implementation that
12941        // migrated the prefix segment to an inline literal (`"lareira-"`)
12942        // that would silently drift from any rebrand of the lifted
12943        // constant. Peer to the sibling
12944        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
12945        // the co-resident resolved-publish-tag composer's prefix axis.
12946        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
12947            let c = caixa_with_nome(nome);
12948            let chart = c.lareira_chart_name();
12949            assert!(
12950                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
12951                "Caixa::lareira_chart_name emission {chart:?} must start \
12952                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
12953                 ({prefix:?})",
12954                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
12955            );
12956        }
12957    }
12958
12959    #[test]
12960    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
12961        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
12962        // byte-identically to the manual open-coded
12963        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
12964        // composition every prior substrate-side caller re-derived.
12965        // Guards the paired-site convergence just applied at caixa-helm's
12966        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
12967        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
12968        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
12969        // composer (all of which now route through this accessor): a
12970        // future implementation of this method that reordered the
12971        // composition arguments, swapped the `<prefix>` constant for a
12972        // different one, or interposed a canonicalization pass on the
12973        // `:nome` axis surfaces here as a caixa-core build-time test
12974        // failure rather than as a downstream Helm chart-render / FluxCD
12975        // reconcile / tatara Process-CR mismatch far from this method's
12976        // source.
12977        for nome in [
12978            "checkout",
12979            "cart-v2",
12980            "a",
12981            "db",
12982            "3rd-party-shim",
12983            "payment-retry",
12984        ] {
12985            let c = caixa_with_nome(nome);
12986            let manual = crate::lareira_chart_name(c.nome());
12987            assert_eq!(
12988                c.lareira_chart_name(),
12989                manual,
12990                "Caixa::lareira_chart_name must byte-equal the manual \
12991                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
12992                 composition across every representative :nome input — \
12993                 got {got:?}, expected {manual:?}",
12994                got = c.lareira_chart_name(),
12995            );
12996        }
12997    }
12998
12999    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
13000
13001    #[test]
13002    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
13003        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
13004        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
13005        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
13006        // across the full paired `(registry, :nome)` accept-set — every
13007        // representative registry the substrate-side emitters carry
13008        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
13009        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
13010        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
13011        // inline_format` render-side pin exercises; `registry.example.
13012        // com`, an off-org shape; `localhost:5000`, the local-dev shape
13013        // every `feira chart` iteration path lands under) × every DNS-
13014        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
13015        // forms` positive-set sweep documents (single-word, hyphen-
13016        // joined, single-char, two-char, digit-start, retry-suffixed).
13017        // Every accept-set pair the peer validate gates let through must
13018        // survive the resolved-OCI-ref projection byte-equal.
13019        for registry in [
13020            "ghcr.io/pleme-io/charts",
13021            "ghcr.io/pleme-io",
13022            "registry.example.com",
13023            "localhost:5000",
13024        ] {
13025            for nome in [
13026                "checkout",
13027                "cart-v2",
13028                "a",
13029                "db",
13030                "3rd-party-shim",
13031                "payment-retry",
13032                "0",
13033            ] {
13034                let c = caixa_with_nome(nome);
13035                let expected = format!(
13036                    "{scheme}{registry}/{chart}",
13037                    scheme = crate::OCI_SCHEME_PREFIX,
13038                    chart = crate::lareira_chart_name(nome),
13039                );
13040                assert_eq!(
13041                    c.oci_chart_ref(registry),
13042                    expected,
13043                    "Caixa::oci_chart_ref must compose \
13044                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
13045                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
13046                     expected {expected:?}",
13047                    scheme = crate::OCI_SCHEME_PREFIX,
13048                    got = c.oci_chart_ref(registry),
13049                );
13050            }
13051        }
13052    }
13053
13054    #[test]
13055    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
13056        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
13057        // emission must begin with the canonical
13058        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
13059        // a hypothetical future implementation that migrated the scheme
13060        // segment to an inline literal (`"oci://"`) that would silently
13061        // drift from any rebrand of the lifted constant. Peer to the
13062        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
13063        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
13064        // co-resident resolved-publish-tag / resolved-chart-name
13065        // composers' prefix axes.
13066        for registry in [
13067            "ghcr.io/pleme-io/charts",
13068            "ghcr.io/pleme-io",
13069            "localhost:5000",
13070        ] {
13071            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13072                let c = caixa_with_nome(nome);
13073                let ref_ = c.oci_chart_ref(registry);
13074                assert!(
13075                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
13076                    "Caixa::oci_chart_ref emission {ref_:?} must start \
13077                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
13078                     — registry ({registry:?}), :nome ({nome:?})",
13079                    scheme = crate::OCI_SCHEME_PREFIX,
13080                );
13081            }
13082        }
13083    }
13084
13085    #[test]
13086    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
13087        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
13088        // identically to the manual open-coded
13089        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
13090        // composition every prior substrate-side caller re-derived.
13091        // Guards the paired-site convergence just applied at caixa-
13092        // tatara's [`derive_chart_ref`] helper (which now routes through
13093        // this accessor): a future implementation of this method that
13094        // reordered the composition arguments, swapped the `<scheme>`
13095        // constant for a different one, migrated the `<chart>` segment
13096        // off the paired [`crate::lareira_chart_name`] composer, or
13097        // interposed a canonicalization pass on either input axis
13098        // surfaces here as a caixa-core build-time test failure rather
13099        // than as a downstream `helm install` / FluxCD OCI-source
13100        // reconcile / tatara `Process`-CR mismatch far from this
13101        // method's source. Sibling to the peer
13102        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
13103        // / [`publish_tag_byte_matches_manual_composition`] /
13104        // [`canonical_git_url_byte_matches_manual_composition`] byte-
13105        // parity pins that carry the same discipline on the co-resident
13106        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
13107        // composers.
13108        for registry in [
13109            "ghcr.io/pleme-io/charts",
13110            "ghcr.io/pleme-io",
13111            "registry.example.com",
13112            "localhost:5000",
13113        ] {
13114            for nome in [
13115                "checkout",
13116                "cart-v2",
13117                "a",
13118                "db",
13119                "3rd-party-shim",
13120                "payment-retry",
13121            ] {
13122                let c = caixa_with_nome(nome);
13123                let manual = crate::oci_chart_ref(registry, c.nome());
13124                assert_eq!(
13125                    c.oci_chart_ref(registry),
13126                    manual,
13127                    "Caixa::oci_chart_ref must byte-equal the manual \
13128                     open-coded `caixa_core::oci_chart_ref(registry, \
13129                     caixa.nome())` composition across every representative \
13130                     (registry, :nome) pair — registry ({registry:?}), \
13131                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
13132                    got = c.oci_chart_ref(registry),
13133                );
13134            }
13135        }
13136    }
13137
13138    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
13139
13140    #[test]
13141    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
13142        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
13143        // pin: [`Caixa::descricao`] must return the `:descricao` typed
13144        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13145        // raw `self.descricao.as_deref()` access across every
13146        // representative value in the accept-set — `None` (the "omit
13147        // the slot to defer to the per-renderer `caixa.nome`-derived
13148        // fallback" arm every existing fixture without a `:descricao`
13149        // line carries), `Some("")` (a past-the-guard sentinel that
13150        // pins the accessor doesn't perform a silent `Some("") → None`
13151        // collapse on the empty arm — validate rejects `Some("")`
13152        // through `DescricaoEmpty` but the accessor must ship the raw
13153        // slot verbatim so a validate-time gate regression surfaces at
13154        // the caixa-helm / caixa-feira emit boundary rather than being
13155        // silently absorbed into the per-renderer `caixa.nome`-derived
13156        // fallback), `Some("Checkout flow.")` (the canonical one-line
13157        // prose descriptor the peer
13158        // `validate_descricao_accepts_canonical_value` positive sweep
13159        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
13160        // Servico.")` (the multi-byte Unicode continuation-byte shape
13161        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
13162        // multi-glyph Unicode shape the peer
13163        // `is_chart_description_shape` predicate accepts), and five
13164        // past-the-guard sentinels for the `DescricaoInvalid` refusal
13165        // cases (`Some(" Checkout flow.")` leading-whitespace,
13166        // `Some("Checkout flow. ")` trailing-whitespace,
13167        // `Some("Checkout\nflow.")` embedded-LF,
13168        // `Some("Checkout\tflow.")` embedded-TAB, and
13169        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
13170        // the accessor doesn't silently absorb the refusal cases into
13171        // a fallback).
13172        //
13173        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
13174        // accessor pin on the substrate primitive — sibling of the peer
13175        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
13176        // (cc7332d) pins that opened the "outer [`Caixa`]
13177        // `Option<&str>` scalar" projection pin pattern this pin folds
13178        // on. Sibling in shape to the peer per-`:placement`
13179        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13180        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13181        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13182        // axes, extended onto the outer top-level [`Caixa`] universal-
13183        // axis surface. Pins against a future silent detour that
13184        // returned an owned `Option<String>` (which would type-check
13185        // but silently allocate on every accessor call, breaking the
13186        // zero-cost projection every peer sibling accessor carries), a
13187        // `Some("") → None` collapse (which would silently absorb the
13188        // `DescricaoEmpty` refusal case at the accessor boundary and
13189        // the caixa-helm `Chart.yaml` `description:` fold would
13190        // silently render a `caixa.nome`-derived fallback on a
13191        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
13192        // `None → Some(<default>)` collapse (which would silently
13193        // reify the per-renderer `caixa.nome`-derived fallback at the
13194        // accessor boundary and every downstream consumer keying off
13195        // the `Option::is_none()` discriminator would lose the "author
13196        // omitted the slot" signal).
13197        for descricao in [
13198            None,
13199            Some(""),
13200            Some("Checkout flow."),
13201            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13202            Some("→ — · ✓"),
13203            Some(" Checkout flow."),
13204            Some("Checkout flow. "),
13205            Some("Checkout\nflow."),
13206            Some("Checkout\tflow."),
13207            Some("Checkout\x00flow."),
13208        ] {
13209            let c = caixa_with_descricao(descricao);
13210            assert_eq!(
13211                c.descricao(),
13212                descricao,
13213                "Caixa::descricao must return :descricao verbatim (got \
13214                 {:?}, expected {descricao:?})",
13215                c.descricao(),
13216            );
13217            assert_eq!(
13218                c.descricao(),
13219                c.descricao.as_deref(),
13220                "Caixa::descricao must byte-equal the raw \
13221                 `self.descricao.as_deref()` field access across every \
13222                 value in the Option<&str> accept-set",
13223            );
13224        }
13225    }
13226
13227    #[test]
13228    fn validate_descricao_empty_arm_routes_through_accessor() {
13229        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
13230        // gate must key off [`Caixa::descricao`], not the raw
13231        // `self.descricao.as_deref()` field access. Structurally: a
13232        // `Caixa { descricao: Some(""), .. }` must surface the
13233        // `DescricaoEmpty` refusal exactly, and a
13234        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
13235        // canonical one-line-prose form) must pass validate. The pair
13236        // jointly pins the accessor + validate-gate composition: any
13237        // future silent detour that had the accessor return `None` on
13238        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
13239        // silently absorb the `DescricaoEmpty` refusal at the accessor
13240        // boundary and the validate gate would accept a struct-literal
13241        // `Caixa { descricao: Some(""), .. }` — the composition pin
13242        // catches that at caixa-core build time.
13243        //
13244        // Peer of the [`Caixa::licenca`] (6d5bc28)
13245        // `validate_licenca_empty_arm_routes_through_accessor` and
13246        // [`Caixa::repositorio`] (cc7332d)
13247        // `validate_repositorio_empty_arm_routes_through_accessor`
13248        // composition pins on the sibling outer top-level [`Caixa`]
13249        // `Option<&str>` universal-axis surface — same "the validate /
13250        // shape-gate predicate must route through the substrate-
13251        // primitive typed dispatch" discipline extended onto the third
13252        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
13253        // composition surface.
13254        let c = caixa_with_descricao(Some(""));
13255        assert!(
13256            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
13257            "validate_descricao must reject descricao == Some(\"\") \
13258             with DescricaoEmpty — the accessor and the validate gate \
13259             must route through the same substrate-primitive typed \
13260             dispatch on the :descricao empty arm",
13261        );
13262        let c = caixa_with_descricao(Some("Checkout flow."));
13263        assert!(
13264            c.validate_descricao().is_ok(),
13265            "validate_descricao must accept descricao == \
13266             Some(\"Checkout flow.\") (the canonical one-line-prose \
13267             chart-description shape)",
13268        );
13269    }
13270
13271    #[test]
13272    fn descricao_projects_option_str_by_borrow() {
13273        // The by-borrow pin: [`Caixa::descricao`] returns
13274        // `Option<&str>` by borrow — the `&str` borrows the underlying
13275        // `String` storage of the `Option<String>` slot and the
13276        // accessor must not allocate a fresh `String` on every call.
13277        // Peer of the [`Caixa::licenca`] (6d5bc28) and
13278        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
13279        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
13280        // the per-`:placement`
13281        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13282        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13283        // return axis, extended onto the third outer top-level
13284        // [`Caixa`] universal-axis `Option<&str>` shape — the
13285        // accessor's returned `&str` must borrow from `&self` (the
13286        // returned reference's lifetime is tied to `&self`), and
13287        // calling the accessor twice on the same [`Caixa`] must yield
13288        // the same `Option<&str>` verbatim (idempotent, no side
13289        // effects on `&self`).
13290        //
13291        // Pins against a future silent detour that returned an owned
13292        // `Option<String>` (which would type-check but silently
13293        // allocate on every call, breaking the zero-cost projection
13294        // every peer sibling accessor carries), or a one-arm-only
13295        // accessor that returned a saturating value on some sentinel
13296        // input (breaking the pass-through invariant the sibling
13297        // required-scalar accessors carry).
13298        for descricao in [
13299            None,
13300            Some(""),
13301            Some("Checkout flow."),
13302            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13303        ] {
13304            let c = caixa_with_descricao(descricao);
13305            let first = c.descricao();
13306            let second = c.descricao();
13307            assert_eq!(
13308                first, second,
13309                "Caixa::descricao must be idempotent — two successive \
13310                 calls on the same &self must return the same \
13311                 Option<&str>",
13312            );
13313            assert_eq!(
13314                first, descricao,
13315                "Caixa::descricao must return :descricao verbatim by \
13316                 borrow — got {first:?}, expected {descricao:?}",
13317            );
13318        }
13319    }
13320
13321    // ── validate_edicao — universal-axis language-edition shape ──
13322
13323    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
13324        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13325        c.edicao = edicao.map(String::from);
13326        c
13327    }
13328
13329    #[test]
13330    fn validate_edicao_accepts_none() {
13331        // The omit-the-slot identity: `:edicao` is optional. The
13332        // gate is a no-op when the author didn't declare a value —
13333        // every caixa without an `:edicao` line trivially passes,
13334        // and the substrate-side build pipeline falls back to the
13335        // documented default edition. Mirrors the peer
13336        // `validate_licenca_accepts_none` posture on the sibling
13337        // `Option<String>` Caixa slot.
13338        let c = caixa_with_edicao(None);
13339        c.validate_edicao().unwrap();
13340    }
13341
13342    #[test]
13343    fn validate_edicao_accepts_canonical_value() {
13344        // Positive control: the canonical `"2026"` edition every
13345        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
13346        // `caixa-mesh`) carries by construction passes the gate.
13347        // Future-introduced sibling editions (`"2027"`, `"2030"`,
13348        // `"2049"`) that match the same 4-digit ASCII decimal year
13349        // shape must also trivially pass — the structural shape
13350        // predicate accepts every well-formed year regardless of
13351        // whether the substrate yet understands the specific value
13352        // (a future known-edition allowlist tightens that).
13353        for ed in ["2026", "2027", "2030", "2049"] {
13354            let c = caixa_with_edicao(Some(ed));
13355            c.validate_edicao()
13356                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
13357        }
13358    }
13359
13360    #[test]
13361    fn validate_edicao_rejects_empty_some() {
13362        // Canonical paste-from-blank-doc footgun. Without this gate
13363        // the empty `Some("")` silently lands as `(:edicao "")` in
13364        // the rendered caixa.lisp and a future renderer-side
13365        // consumer's `Option::unwrap_or_else` (which only fires on
13366        // `None`) skips its fallback. Mirrors the peer
13367        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
13368        // `Option<String>` Caixa slot.
13369        let c = caixa_with_edicao(Some(""));
13370        let err = c.validate_edicao().unwrap_err();
13371        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13372    }
13373
13374    #[test]
13375    fn validate_edicao_rejects_free_form_non_year() {
13376        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
13377        // `"nightly"` shapes carry no operational meaning on the
13378        // substrate's build-time edition selector. Until this gate
13379        // landed the bare empty-arm check let every such value
13380        // through and broke far from the source caixa.lisp. Peer
13381        // with the shape-predicate cascade
13382        // `validate_repositorio_rejects_missing_colon_separator`
13383        // establishes past its own empty arm.
13384        for ed in ["x", "latest", "nightly", "stable"] {
13385            let c = caixa_with_edicao(Some(ed));
13386            let err = c.validate_edicao().unwrap_err();
13387            assert!(
13388                matches!(err, ManifestError::EdicaoInvalid { .. }),
13389                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13390            );
13391        }
13392    }
13393
13394    #[test]
13395    fn validate_edicao_rejects_trailing_whitespace() {
13396        // Paste-from-doc whitespace footgun. A trailing space in
13397        // the `:edicao` value would silently break the substrate's
13398        // build-time edition match-table lookup at the rendered
13399        // artifact's edition-selector consumer. The shape predicate
13400        // refuses every whitespace byte by construction (any byte
13401        // outside `0-9` fails `is_ascii_digit`). Peer with
13402        // `validate_repositorio_rejects_whitespace`.
13403        let c = caixa_with_edicao(Some("2026 "));
13404        let err = c.validate_edicao().unwrap_err();
13405        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
13406            panic!("expected EdicaoInvalid, got {err:?}");
13407        };
13408        assert_eq!(edicao, "2026 ");
13409    }
13410
13411    #[test]
13412    fn validate_edicao_rejects_leading_whitespace() {
13413        // Symmetric paste-from-doc whitespace footgun on the leading
13414        // boundary — the gate refuses every shape with a non-digit
13415        // byte by construction.
13416        let c = caixa_with_edicao(Some(" 2026"));
13417        let err = c.validate_edicao().unwrap_err();
13418        assert!(
13419            matches!(err, ManifestError::EdicaoInvalid { .. }),
13420            "got {err:?}",
13421        );
13422    }
13423
13424    #[test]
13425    fn validate_edicao_rejects_control_char() {
13426        // Paste-from-multiline-doc CRLF footgun — control characters
13427        // at the value boundary break the substrate's build-time
13428        // edition-selector parser. Peer with
13429        // `validate_repositorio_rejects_control_char`.
13430        let c = caixa_with_edicao(Some("2026\n"));
13431        let err = c.validate_edicao().unwrap_err();
13432        assert!(
13433            matches!(err, ManifestError::EdicaoInvalid { .. }),
13434            "got {err:?}",
13435        );
13436    }
13437
13438    #[test]
13439    fn validate_edicao_rejects_non_ascii_lookalike() {
13440        // Fullwidth-keyboard look-alike footgun — `"2026"` is
13441        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
13442        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
13443        // edition selector wants an ASCII year, and the gate
13444        // refuses every non-ASCII shape by construction (length in
13445        // bytes is 12 ≠ 4, *and* every byte falls outside
13446        // `is_ascii_digit`'s `0-9` range).
13447        let c = caixa_with_edicao(Some("2026"));
13448        let err = c.validate_edicao().unwrap_err();
13449        assert!(
13450            matches!(err, ManifestError::EdicaoInvalid { .. }),
13451            "got {err:?}",
13452        );
13453    }
13454
13455    #[test]
13456    fn validate_edicao_rejects_version_tag_prefix() {
13457        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
13458        // / `"r2026"` are familiar shapes from git-tag / Rust
13459        // edition / release-tag conventions that don't apply to
13460        // the year-shaped edition axis. The shape predicate refuses
13461        // every leading non-digit prefix.
13462        for ed in ["v2026", "e2026", "r2026"] {
13463            let c = caixa_with_edicao(Some(ed));
13464            let err = c.validate_edicao().unwrap_err();
13465            assert!(
13466                matches!(err, ManifestError::EdicaoInvalid { .. }),
13467                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13468            );
13469        }
13470    }
13471
13472    #[test]
13473    fn validate_edicao_rejects_decimal_shape() {
13474        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
13475        // `"2026.0"` are familiar shapes from semver / float
13476        // conventions that don't apply to the year-shaped edition
13477        // axis. The shape predicate refuses every non-digit byte
13478        // (`.` falls outside `is_ascii_digit`).
13479        for ed in ["2026.1", "2026.0", "2026.0.1"] {
13480            let c = caixa_with_edicao(Some(ed));
13481            let err = c.validate_edicao().unwrap_err();
13482            assert!(
13483                matches!(err, ManifestError::EdicaoInvalid { .. }),
13484                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13485            );
13486        }
13487    }
13488
13489    #[test]
13490    fn validate_edicao_rejects_wrong_length_numeric() {
13491        // Wrong-length numeric footgun — `"26"` (truncated) /
13492        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
13493        // (zero-padded too wide) all parse as integers but don't
13494        // name a 4-digit year. The shape predicate refuses every
13495        // value whose length isn't exactly 4 bytes.
13496        for ed in ["26", "202", "20260", "00026", "9"] {
13497            let c = caixa_with_edicao(Some(ed));
13498            let err = c.validate_edicao().unwrap_err();
13499            assert!(
13500                matches!(err, ManifestError::EdicaoInvalid { .. }),
13501                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13502            );
13503        }
13504    }
13505
13506    #[test]
13507    fn validate_edicao_empty_takes_precedence_over_shape() {
13508        // Empty-first cascade pin: the empty `Some("")` surfaces
13509        // the narrower `EdicaoEmpty` not the shape-predicate-
13510        // wrapped `EdicaoInvalid`, mirroring the peer
13511        // `validate_repositorio_empty_takes_precedence_over_shape`
13512        // (`RepositorioEmpty` → `RepositorioInvalid`),
13513        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
13514        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
13515        // cascades. The shape predicate also refuses the empty
13516        // input (defensively — `s.len() != 4`), but the
13517        // manifest-layer empty arm runs first to surface the
13518        // narrower diagnostic verbatim.
13519        let c = caixa_with_edicao(Some(""));
13520        let err = c.validate_edicao().unwrap_err();
13521        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13522    }
13523
13524    #[test]
13525    fn validate_edicao_template_passes() {
13526        // Round-trip pin: the bare `Caixa::template` shape (which
13527        // carries `:edicao "2026"` verbatim) passes the gate by
13528        // construction. A future template-shape change that
13529        // introduced `(:edicao "")` or a non-year value would
13530        // surface here as a regression. Mirrors the peer
13531        // `validate_licenca_template_passes` pin.
13532        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13533        c.validate_edicao().unwrap();
13534    }
13535
13536    #[test]
13537    fn validate_edicao_diagnostic_names_offending_slot() {
13538        // Diagnostic-shape pin (peer with
13539        // `validate_licenca_diagnostic_names_offending_slot`): the
13540        // error's Display surfaces the `:edicao` slot name verbatim,
13541        // so a `feira lint` run can render the diagnostic without
13542        // re-parsing and the author can grep their caixa.lisp for
13543        // the offending `:edicao` line.
13544        let c = caixa_with_edicao(Some(""));
13545        let rendered = c.validate_edicao().unwrap_err().to_string();
13546        assert!(
13547            rendered.contains(":edicao"),
13548            "diagnostic must name the offending slot: {rendered}",
13549        );
13550    }
13551
13552    #[test]
13553    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
13554        // Diagnostic-shape pin on the shape-predicate arm (peer
13555        // with `validate_repositorio_diagnostic_carries_offending_value`):
13556        // the error's Display surfaces the offending value + slot
13557        // name verbatim, so a `feira lint` run can render the
13558        // diagnostic without re-parsing and the author can grep
13559        // their caixa.lisp for the offending `:edicao` value.
13560        let c = caixa_with_edicao(Some("v2026"));
13561        let rendered = c.validate_edicao().unwrap_err().to_string();
13562        assert!(
13563            rendered.contains(":edicao"),
13564            "diagnostic must name the offending slot: {rendered}",
13565        );
13566        assert!(
13567            rendered.contains("v2026"),
13568            "diagnostic must quote the offending value: {rendered}",
13569        );
13570    }
13571
13572    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
13573
13574    #[test]
13575    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
13576        // The canonical per-`Caixa` `:edicao` language-edition scalar
13577        // pin: [`Caixa::edicao`] must return the `:edicao` typed
13578        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13579        // raw `self.edicao.as_deref()` access across every representative
13580        // value in the accept-set — `None` (the "omit the slot to defer
13581        // to the substrate's default edition" arm every existing
13582        // [`caixa-resolver`] fixture without an `:edicao` line carries),
13583        // `Some("")` (a past-the-guard sentinel that pins the accessor
13584        // doesn't perform a silent `Some("") → None` collapse on the
13585        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
13586        // but the accessor must ship the raw slot verbatim so a
13587        // validate-time gate regression surfaces at any future edition-
13588        // aware consumer's boundary rather than being silently absorbed
13589        // into the substrate's default edition), `Some("2026")` (the
13590        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
13591        // template scaffolds via [`Caixa::template`] and every
13592        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
13593        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
13594        // carries by construction), `Some("2018")` / `Some("2021")` /
13595        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
13596        // peer with Cargo's `[package] edition` grammar every future-
13597        // introduced sibling to `"2026"` will follow), and eight
13598        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
13599        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
13600        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
13601        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
13602        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
13603        // length-numeric, `Some("latest")` free-form-non-year — the
13604        // sentinels pin the accessor doesn't silently absorb the
13605        // refusal cases into a substrate-default-edition fallback).
13606        //
13607        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
13608        // return scalar accessor pin on the substrate primitive —
13609        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
13610        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13611        // (3f16e2f) pins that opened the "outer [`Caixa`]
13612        // `Option<&str>` scalar" projection pin pattern this pin folds
13613        // on. Sibling in shape to the peer per-`:placement`
13614        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13615        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13616        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13617        // axes, extended onto the outer top-level [`Caixa`] universal-
13618        // axis surface's last unlifted `Option<String>` slot. Pins
13619        // against a future silent detour that returned an owned
13620        // `Option<String>` (which would type-check but silently
13621        // allocate on every accessor call, breaking the zero-cost
13622        // projection every peer sibling accessor carries), a
13623        // `Some("") → None` collapse (which would silently absorb the
13624        // `EdicaoEmpty` refusal case at the accessor boundary and any
13625        // future edition-aware consumer would silently fall back to
13626        // the substrate's default edition on a struct-literal
13627        // `Caixa { edicao: Some(""), .. }`), or a
13628        // `None → Some("2026")` collapse (which would silently reify
13629        // the substrate's default edition at the accessor boundary
13630        // and every downstream consumer keying off the
13631        // `Option::is_none()` discriminator would lose the "author
13632        // omitted the slot" signal).
13633        for edicao in [
13634            None,
13635            Some(""),
13636            Some("2026"),
13637            Some("2018"),
13638            Some("2021"),
13639            Some("2024"),
13640            Some("2026 "),
13641            Some(" 2026"),
13642            Some("2026\n"),
13643            Some("2026"),
13644            Some("v2026"),
13645            Some("2026.1"),
13646            Some("26"),
13647            Some("latest"),
13648        ] {
13649            let c = caixa_with_edicao(edicao);
13650            assert_eq!(
13651                c.edicao(),
13652                edicao,
13653                "Caixa::edicao must return :edicao verbatim (got {:?}, \
13654                 expected {edicao:?})",
13655                c.edicao(),
13656            );
13657            assert_eq!(
13658                c.edicao(),
13659                c.edicao.as_deref(),
13660                "Caixa::edicao must byte-equal the raw \
13661                 `self.edicao.as_deref()` field access across every \
13662                 value in the Option<&str> accept-set",
13663            );
13664        }
13665    }
13666
13667    #[test]
13668    fn validate_edicao_empty_arm_routes_through_accessor() {
13669        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
13670        // must key off [`Caixa::edicao`], not the raw
13671        // `self.edicao.as_deref()` field access. Structurally: a
13672        // `Caixa { edicao: Some(""), .. }` must surface the
13673        // `EdicaoEmpty` refusal exactly, and a
13674        // `Caixa { edicao: Some("2026"), .. }` (the canonical
13675        // 4-digit-ASCII-decimal-year form) must pass validate. The
13676        // pair jointly pins the accessor + validate-gate composition:
13677        // any future silent detour that had the accessor return `None`
13678        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
13679        // would silently absorb the `EdicaoEmpty` refusal at the
13680        // accessor boundary and the validate gate would accept a
13681        // struct-literal `Caixa { edicao: Some(""), .. }` — the
13682        // composition pin catches that at caixa-core build time.
13683        //
13684        // Peer of the [`Caixa::licenca`] (6d5bc28)
13685        // `validate_licenca_empty_arm_routes_through_accessor`,
13686        // [`Caixa::repositorio`] (cc7332d)
13687        // `validate_repositorio_empty_arm_routes_through_accessor`,
13688        // and [`Caixa::descricao`] (3f16e2f)
13689        // `validate_descricao_empty_arm_routes_through_accessor`
13690        // composition pins on the sibling outer top-level [`Caixa`]
13691        // `Option<&str>` universal-axis surface — same "the validate /
13692        // shape-gate predicate must route through the substrate-
13693        // primitive typed dispatch" discipline extended onto the
13694        // fourth and final outer top-level [`Caixa`] universal-axis
13695        // `Option<&str>`-composition surface, closing the accessor-
13696        // composition family.
13697        let c = caixa_with_edicao(Some(""));
13698        assert!(
13699            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
13700            "validate_edicao must reject edicao == Some(\"\") with \
13701             EdicaoEmpty — the accessor and the validate gate must \
13702             route through the same substrate-primitive typed dispatch \
13703             on the :edicao empty arm",
13704        );
13705        let c = caixa_with_edicao(Some("2026"));
13706        assert!(
13707            c.validate_edicao().is_ok(),
13708            "validate_edicao must accept edicao == Some(\"2026\") \
13709             (the canonical 4-digit-ASCII-decimal-year shape)",
13710        );
13711    }
13712
13713    #[test]
13714    fn edicao_projects_option_str_by_borrow() {
13715        // The by-borrow pin: [`Caixa::edicao`] returns
13716        // `Option<&str>` by borrow — the `&str` borrows the underlying
13717        // `String` storage of the `Option<String>` slot and the
13718        // accessor must not allocate a fresh `String` on every call.
13719        // Peer of the [`Caixa::licenca`] (6d5bc28),
13720        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13721        // (3f16e2f) by-borrow pins on the peer outer top-level
13722        // [`Caixa`] `Option<&str>`-return axes, and of the
13723        // per-`:placement`
13724        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13725        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13726        // return axis, extended onto the fourth and final outer top-
13727        // level [`Caixa`] universal-axis `Option<&str>` shape — the
13728        // accessor's returned `&str` must borrow from `&self` (the
13729        // returned reference's lifetime is tied to `&self`), and
13730        // calling the accessor twice on the same [`Caixa`] must yield
13731        // the same `Option<&str>` verbatim (idempotent, no side
13732        // effects on `&self`).
13733        //
13734        // Pins against a future silent detour that returned an owned
13735        // `Option<String>` (which would type-check but silently
13736        // allocate on every call, breaking the zero-cost projection
13737        // every peer sibling accessor carries), or a one-arm-only
13738        // accessor that returned a saturating value on some sentinel
13739        // input (breaking the pass-through invariant the sibling
13740        // required-scalar accessors carry).
13741        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
13742            let c = caixa_with_edicao(edicao);
13743            let first = c.edicao();
13744            let second = c.edicao();
13745            assert_eq!(
13746                first, second,
13747                "Caixa::edicao must be idempotent — two successive \
13748                 calls on the same &self must return the same \
13749                 Option<&str>",
13750            );
13751            assert_eq!(
13752                first, edicao,
13753                "Caixa::edicao must return :edicao verbatim by \
13754                 borrow — got {first:?}, expected {edicao:?}",
13755            );
13756        }
13757    }
13758
13759    #[test]
13760    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
13761        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
13762        // label caixa-identity scalar pin: [`Caixa::nome`] must return
13763        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
13764        // the raw field access across every representative value in
13765        // the accept-set — the canonical `"demo"` template baseline
13766        // (the same `feira init`-scaffolded default the sibling
13767        // `validate_nome_accepts_canonical_template` positive-control
13768        // gate pins), plus every sibling per-typed-slot atom accessor's
13769        // canonical positive-arm byte-string (`"catalog"` per
13770        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
13771        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
13772        // `caixa-helm`/`caixa-flux` cross-crate integration-test
13773        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
13774        // canonical example), plus every past-the-guard sentinel for
13775        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
13776        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
13777        // the bare DNS-1123 63-byte cap but overflows the joint
13778        // `lareira-<nome>` chart-name budget the sibling
13779        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
13780        //
13781        // The past-the-guard sentinels pin the accessor doesn't
13782        // silently absorb the refusal cases into a template-derived
13783        // fallback (a future `.nome().is_empty().then(|| "demo")`
13784        // collapse would silently absorb the `NomeEmpty` refusal at
13785        // the accessor boundary and the validate gate would accept a
13786        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
13787        // catches that at caixa-core build time).
13788        //
13789        // First outer top-level [`Caixa`] `&str`-return required-
13790        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
13791        // required-scalar" projection pattern the sibling per-`Caixa`
13792        // `:versao` future lift folds on. Sibling in shape to the peer
13793        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
13794        // required-`String`-carry accessor pin on the sibling per-
13795        // sub-struct required-axis, extended onto the outer top-level
13796        // [`Caixa`] universal-axis required-`String`-carry axis.
13797        for nome in [
13798            "demo",
13799            "catalog",
13800            "cart",
13801            "hello-rio",
13802            "checkout",
13803            "",
13804            "Bad_Name",
13805            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
13806        ] {
13807            let c = caixa_with_nome(nome);
13808            assert_eq!(
13809                c.nome(),
13810                nome,
13811                "Caixa::nome must return :nome verbatim (got {}, \
13812                 expected {nome})",
13813                c.nome(),
13814            );
13815            assert_eq!(
13816                c.nome(),
13817                c.nome.as_str(),
13818                "Caixa::nome must byte-equal the raw .nome field \
13819                 access across every value in the String accept-set",
13820            );
13821        }
13822    }
13823
13824    #[test]
13825    fn validate_nome_empty_arm_routes_through_accessor() {
13826        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
13827        // key off [`Caixa::nome`], not the raw `.nome` field access.
13828        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
13829        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
13830        // template baseline (the peer positive-arm the sibling
13831        // `validate_nome_accepts_canonical_template` gate carves out)
13832        // must pass validate. The pair jointly pins the accessor +
13833        // validate-gate composition: any future silent detour that
13834        // had the accessor return a fresh `"demo"` on the empty arm
13835        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
13836        // would silently absorb the `NomeEmpty` refusal at the
13837        // accessor boundary and the validate gate would accept a
13838        // struct-literal `Caixa { nome: "".into(), .. }` — the
13839        // composition pin catches that at caixa-core build time.
13840        //
13841        // Peer of the sibling per-`Caixa`
13842        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
13843        // / `validate_repositorio_empty_arm_routes_through_accessor`
13844        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
13845        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
13846        // (2641cbd) composition pins on the sibling outer top-level
13847        // [`Caixa`] `Option<&str>` axes — same "the validate /
13848        // shape-gate predicate must route through the substrate-
13849        // primitive typed dispatch" discipline extended onto the peer
13850        // outer top-level [`Caixa`] required-`&str` composition axis.
13851        let c = caixa_with_nome("");
13852        assert!(
13853            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
13854            "validate_nome must reject nome == \"\" with NomeEmpty — \
13855             the accessor and the validate gate must route through the \
13856             same substrate-primitive typed dispatch on the :nome \
13857             empty-arm",
13858        );
13859        let c = caixa_with_nome("demo");
13860        assert!(
13861            c.validate_nome().is_ok(),
13862            "validate_nome must accept nome == \"demo\" (the canonical \
13863             DNS-1123-label template baseline)",
13864        );
13865    }
13866
13867    #[test]
13868    fn nome_projects_str_by_borrow() {
13869        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
13870        // — the `&str` borrows the underlying `String` storage of the
13871        // required `nome` slot and the accessor must not allocate a
13872        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
13873        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
13874        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
13875        // by-borrow pins on the peer outer top-level [`Caixa`]
13876        // `Option<&str>`-return axes, extended onto the first outer
13877        // top-level [`Caixa`] required-`&str`-return axis — the
13878        // accessor's returned `&str` must borrow from `&self` (the
13879        // returned reference's lifetime is tied to `&self`), and
13880        // calling the accessor twice on the same [`Caixa`] must yield
13881        // the same `&str` verbatim (idempotent, no side effects on
13882        // `&self`).
13883        //
13884        // Pins against a future silent detour that returned an owned
13885        // `String` (which would type-check but silently allocate on
13886        // every call, breaking the zero-cost projection every peer
13887        // sibling accessor carries), an accidental
13888        // `.nome.to_lowercase()` detour that returned a fresh
13889        // allocation through an already-DNS-1123-lowercase-only
13890        // string (breaking a future `const fn` regression), or a
13891        // one-arm-only accessor that returned a canonicalized value
13892        // on some sentinel input (breaking the pass-through invariant
13893        // the sibling required-scalar accessors carry).
13894        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
13895            let c = caixa_with_nome(nome);
13896            let first = c.nome();
13897            let second = c.nome();
13898            assert_eq!(
13899                first, second,
13900                "Caixa::nome must be idempotent — two successive calls \
13901                 on the same &self must return the same &str",
13902            );
13903            assert_eq!(
13904                first, nome,
13905                "Caixa::nome must return :nome verbatim by borrow — \
13906                 got {first}, expected {nome}",
13907            );
13908        }
13909    }
13910
13911    #[test]
13912    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
13913        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
13914        // pinned-version scalar pin: [`Caixa::versao`] must return the
13915        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
13916        // raw `.versao` field access across every representative value
13917        // in the accept-set — the canonical `"0.1.0"` template baseline
13918        // (the same `feira init`-scaffolded default the sibling
13919        // `validate_versao_accepts_canonical_template` positive-control
13920        // gate pins), plus every canonical SemVer-2 shape the sibling
13921        // `validate_versao_accepts_canonical_forms` positive-arm sweep
13922        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
13923        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
13924        // `"10.20.30"`), plus every past-the-guard sentinel for the
13925        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
13926        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
13927        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
13928        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
13929        // `"latest"` the docker-tag-shape footgun — the sentinels pin
13930        // the accessor doesn't silently absorb the refusal cases into a
13931        // template-derived fallback like `"0.1.0"`).
13932        //
13933        // The past-the-guard sentinels pin the accessor doesn't silently
13934        // absorb the refusal cases into a template-derived fallback (a
13935        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
13936        // silently absorb the `VersaoEmpty` refusal at the accessor
13937        // boundary and the validate gate would accept a struct-literal
13938        // `Caixa { versao: "".into(), .. }` — the pin catches that at
13939        // caixa-core build time).
13940        //
13941        // Second outer top-level [`Caixa`] `&str`-return required-scalar
13942        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
13943        // scalar" projection pattern the sibling per-`Caixa`
13944        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
13945        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
13946        // (4127bb6) / per-`:children`
13947        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
13948        // / per-`:upgrade-from`
13949        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
13950        // struct `:versao`-shaped `&str`-return accessor pins on the
13951        // sibling per-typed-slot version-carrier axes, extended onto the
13952        // second outer top-level [`Caixa`] universal-axis required-
13953        // `String`-carry axis so the two universal-axis identity-
13954        // carrying scalars every `defcaixa` form supplies (`:nome` +
13955        // `:versao`) share the same "one typed dispatch per axis" pin
13956        // discipline.
13957        for versao in [
13958            "0.1.0",
13959            "0.0.0",
13960            "1.0.0",
13961            "0.2.0-rc.1",
13962            "1.0.0-alpha.0",
13963            "1.0.0+build.42",
13964            "1.0.0-rc.1+build.42",
13965            "10.20.30",
13966            "",
13967            "v0.1.0",
13968            "0.1",
13969            "^0.1",
13970            "0.1.0.0",
13971            "latest",
13972        ] {
13973            let c = caixa_with_versao(versao);
13974            assert_eq!(
13975                c.versao(),
13976                versao,
13977                "Caixa::versao must return :versao verbatim (got {}, \
13978                 expected {versao})",
13979                c.versao(),
13980            );
13981            assert_eq!(
13982                c.versao(),
13983                c.versao.as_str(),
13984                "Caixa::versao must byte-equal the raw .versao field \
13985                 access across every value in the String accept-set",
13986            );
13987        }
13988    }
13989
13990    #[test]
13991    fn validate_versao_empty_arm_routes_through_accessor() {
13992        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
13993        // must key off [`Caixa::versao`], not the raw `.versao` field
13994        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
13995        // surface the `VersaoEmpty` refusal exactly, and the canonical
13996        // `"0.1.0"` template baseline (the peer positive-arm the sibling
13997        // `validate_versao_accepts_canonical_template` gate carves out)
13998        // must pass validate. The pair jointly pins the accessor +
13999        // validate-gate composition: any future silent detour that had
14000        // the accessor return a fresh `"0.1.0"` on the empty arm
14001        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
14002        // would silently absorb the `VersaoEmpty` refusal at the
14003        // accessor boundary and the validate gate would accept a
14004        // struct-literal `Caixa { versao: "".into(), .. }` — the
14005        // composition pin catches that at caixa-core build time.
14006        //
14007        // Peer of the sibling per-`Caixa`
14008        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
14009        // composition pin on the sibling outer top-level [`Caixa`]
14010        // required-`&str` universal-axis surface — same "the validate /
14011        // shape-gate predicate must route through the substrate-
14012        // primitive typed dispatch" discipline extended onto the peer
14013        // outer top-level [`Caixa`] required-`&str` universal-axis
14014        // pinned-version composition axis, closing the second
14015        // coordinate of the "one canonical typed dispatch per per-Caixa
14016        // required-`&str` universal-axis" discipline.
14017        let c = caixa_with_versao("");
14018        assert!(
14019            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
14020            "validate_versao must reject versao == \"\" with VersaoEmpty — \
14021             the accessor and the validate gate must route through the \
14022             same substrate-primitive typed dispatch on the :versao \
14023             empty-arm",
14024        );
14025        let c = caixa_with_versao("0.1.0");
14026        assert!(
14027            c.validate_versao().is_ok(),
14028            "validate_versao must accept versao == \"0.1.0\" (the \
14029             canonical SemVer-2 template baseline)",
14030        );
14031    }
14032
14033    #[test]
14034    fn versao_projects_str_by_borrow() {
14035        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
14036        // — the `&str` borrows the underlying `String` storage of the
14037        // required `versao` slot and the accessor must not allocate a
14038        // fresh `String` on every call. Peer of the [`Caixa::nome`]
14039        // (e6b7d97) by-borrow pin on the sibling outer top-level
14040        // [`Caixa`] required-`&str`-return axis, extended onto the
14041        // second outer top-level [`Caixa`] required-`&str`-return
14042        // universal-axis pinned-version surface — the accessor's
14043        // returned `&str` must borrow from `&self` (the returned
14044        // reference's lifetime is tied to `&self`), and calling the
14045        // accessor twice on the same [`Caixa`] must yield the same
14046        // `&str` verbatim (idempotent, no side effects on `&self`).
14047        //
14048        // Pins against a future silent detour that returned an owned
14049        // `String` (which would type-check but silently allocate on
14050        // every call, breaking the zero-cost projection every peer
14051        // sibling accessor carries), an accidental
14052        // `semver::Version::parse(&self.versao).unwrap().to_string()`
14053        // detour that returned a canonicalized fresh allocation through
14054        // an already-canonical byte-string (breaking a future `const fn`
14055        // regression and silently absorbing the `VersaoInvalid` refusal
14056        // at the accessor boundary), or a one-arm-only accessor that
14057        // returned a canonicalized value on some sentinel input
14058        // (breaking the pass-through invariant the sibling required-
14059        // scalar accessors carry).
14060        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
14061            let c = caixa_with_versao(versao);
14062            let first = c.versao();
14063            let second = c.versao();
14064            assert_eq!(
14065                first, second,
14066                "Caixa::versao must be idempotent — two successive \
14067                 calls on the same &self must return the same &str",
14068            );
14069            assert_eq!(
14070                first, versao,
14071                "Caixa::versao must return :versao verbatim by borrow \
14072                 — got {first}, expected {versao}",
14073            );
14074        }
14075    }
14076
14077    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
14078        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14079        c.kind = kind;
14080        c
14081    }
14082
14083    #[test]
14084    fn kind_returns_kind_variant_verbatim_across_permutations() {
14085        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
14086        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
14087        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
14088        // the raw `.kind` field access across every variant in the
14089        // closed accept-set (`Biblioteca` — the library kind that
14090        // exports lisp forms; `Binario` — the nix-built executable kind
14091        // under `exe/`; `Servico` — the wasm-component daemon kind
14092        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
14093        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
14094        // composition kind).
14095        //
14096        // Pins against a future silent detour that re-derived the kind
14097        // from a peer axis (an accidental fallback to
14098        // `if !servicos.is_empty() { Servico } else if
14099        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
14100        // collapse that read the code-surface / mesh-slot columns into
14101        // the kind discriminator), a variant remap the operator
14102        // authors on one consumer without the other, or a stale-derive
14103        // detour that substituted [`CaixaKind::Biblioteca`] as the
14104        // default when the field held any other variant (which would
14105        // silently collapse the distinction between "author explicitly
14106        // declared `:kind Servico`" and "author declared any other
14107        // kind" every downstream renderer-dispatch site depends on).
14108        //
14109        // First outer top-level [`Caixa`] `Copy`-return required-enum-
14110        // discriminant accessor pin — opens the "outer [`Caixa`]
14111        // `Copy`-return required-discriminant" projection pattern.
14112        // Sibling in shape to the peer per-`:supervisor`
14113        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
14114        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
14115        // (921fe1b), and per-`:children`
14116        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
14117        // `Copy`-return closed-set-enum discriminant accessor pins on
14118        // the sibling nested-spec typed-slot discriminator axes,
14119        // extended here to the outer top-level [`Caixa`] universal-
14120        // axis surface.
14121        for kind in [
14122            CaixaKind::Biblioteca,
14123            CaixaKind::Binario,
14124            CaixaKind::Servico,
14125            CaixaKind::Supervisor,
14126            CaixaKind::Aplicacao,
14127        ] {
14128            let c = caixa_with_kind(kind);
14129            assert_eq!(
14130                c.kind(),
14131                kind,
14132                "Caixa::kind must return :kind verbatim (got {:?}, \
14133                 expected {kind:?})",
14134                c.kind(),
14135            );
14136            assert_eq!(
14137                c.kind(),
14138                c.kind,
14139                "Caixa::kind accessor and .kind field access must \
14140                 byte-equal — the accessor is the substrate-primitive \
14141                 typed dispatch every downstream kind-gate consumer \
14142                 must route through",
14143            );
14144        }
14145    }
14146
14147    #[test]
14148    fn require_kind_reads_through_lifted_kind_accessor() {
14149        // Two-consumer coherence pin: the [`crate::render::require_kind`]
14150        // entry-gate predicate (the canonical two-line
14151        // `require_kind(caixa, Servico)?` prelude every per-Servico /
14152        // per-Aplicacao renderer runs at its entry-point) and the
14153        // sibling [`crate::render::KindMismatch`] error carrier's
14154        // `actual:` field (which names the offending caixa's variant
14155        // in the diagnostic) must both key off the lifted accessor, so
14156        // any future rebrand on the typed slot's reader shape lands at
14157        // exactly one place. Pins the two-site coherence by exercising
14158        // every off-diagonal `(actual, expected)` pair across the
14159        // closed accept-set — the `KindMismatch { actual, expected }`
14160        // surfaced on the mismatch arm must byte-equal the pair the
14161        // accessor returns for each side.
14162        //
14163        // Peer of the sibling per-`:placement`
14164        // `validate_placement_reads_through_lifted_estrategia_accessor`
14165        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
14166        // `Copy`-return discriminant axis — same "the entry-gate
14167        // predicate and the error carrier's `actual:` field must route
14168        // through the substrate-primitive typed dispatch" discipline
14169        // extended onto the outer top-level [`Caixa`] universal-axis
14170        // discriminant surface.
14171        for expected in [
14172            CaixaKind::Biblioteca,
14173            CaixaKind::Binario,
14174            CaixaKind::Servico,
14175            CaixaKind::Supervisor,
14176            CaixaKind::Aplicacao,
14177        ] {
14178            for actual in [
14179                CaixaKind::Biblioteca,
14180                CaixaKind::Binario,
14181                CaixaKind::Servico,
14182                CaixaKind::Supervisor,
14183                CaixaKind::Aplicacao,
14184            ] {
14185                let c = caixa_with_kind(actual);
14186                let result = crate::render::require_kind(&c, expected);
14187                if expected == actual {
14188                    assert!(
14189                        result.is_ok(),
14190                        "require_kind must accept when actual == expected \
14191                         (actual={actual:?}, expected={expected:?})",
14192                    );
14193                } else {
14194                    let err = result.expect_err("require_kind must reject when actual != expected");
14195                    assert_eq!(
14196                        err.actual,
14197                        c.kind(),
14198                        "KindMismatch.actual must byte-equal Caixa::kind() \
14199                         — the error carrier's `actual:` field reads \
14200                         through the lifted accessor",
14201                    );
14202                    assert_eq!(
14203                        err.expected, expected,
14204                        "KindMismatch.expected must byte-equal the \
14205                         expected variant passed to require_kind",
14206                    );
14207                }
14208            }
14209        }
14210    }
14211
14212    #[test]
14213    fn aplicacao_view_kind_gate_routes_through_accessor() {
14214        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
14215        // must key off [`Caixa::kind`], not the raw `.kind` field
14216        // access. Structurally: a `Caixa { kind: X, .. }` for any
14217        // non-`Aplicacao` variant must fold to `None` on the
14218        // `aplicacao_view` composer (the "kind mismatch → no typed
14219        // view" contract every downstream Aplicacao consumer keys off
14220        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
14221        // `Some(_)`. The pair jointly pins the accessor + view-gate
14222        // composition: any future silent detour that had the accessor
14223        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
14224        // input would silently absorb the kind-mismatch case at the
14225        // accessor boundary and every per-Aplicacao renderer would
14226        // silently render a non-Aplicacao caixa's mesh slots — the
14227        // composition pin catches that at caixa-core build time.
14228        //
14229        // Peer of the sibling per-`Caixa`
14230        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
14231        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
14232        // composition pins on the sibling outer top-level [`Caixa`]
14233        // required-`&str` universal-axis surfaces — same "the
14234        // composer / validate gate must route through the substrate-
14235        // primitive typed dispatch" discipline extended onto the
14236        // outer top-level [`Caixa`] `Copy`-return required-
14237        // discriminant composition axis.
14238        for kind in [
14239            CaixaKind::Biblioteca,
14240            CaixaKind::Binario,
14241            CaixaKind::Servico,
14242            CaixaKind::Supervisor,
14243        ] {
14244            let c = caixa_with_kind(kind);
14245            assert!(
14246                c.aplicacao_view().is_none(),
14247                "aplicacao_view must return None on non-Aplicacao \
14248                 kind {kind:?} — the composer's kind-gate must route \
14249                 through Caixa::kind()",
14250            );
14251        }
14252        let c = caixa_with_kind(CaixaKind::Aplicacao);
14253        assert!(
14254            c.aplicacao_view().is_some(),
14255            "aplicacao_view must return Some on kind Aplicacao — \
14256             the composer's kind-gate must accept the matching arm \
14257             through Caixa::kind()",
14258        );
14259    }
14260
14261    #[test]
14262    fn supervisor_view_kind_gate_routes_through_accessor() {
14263        // Composition pin (mirror of the sibling
14264        // `aplicacao_view_kind_gate_routes_through_accessor` on the
14265        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
14266        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
14267        // field access. A `Caixa { kind: X, .. }` for any non-
14268        // `Supervisor` variant must fold to `None` on the
14269        // `supervisor_view` composer, and a `Caixa { kind:
14270        // Supervisor, .. }` must fold to `Some(_)`. Same peer
14271        // composition pin discipline on the second `_view` composer
14272        // axis.
14273        for kind in [
14274            CaixaKind::Biblioteca,
14275            CaixaKind::Binario,
14276            CaixaKind::Servico,
14277            CaixaKind::Aplicacao,
14278        ] {
14279            let c = caixa_with_kind(kind);
14280            assert!(
14281                c.supervisor_view().is_none(),
14282                "supervisor_view must return None on non-Supervisor \
14283                 kind {kind:?} — the composer's kind-gate must route \
14284                 through Caixa::kind()",
14285            );
14286        }
14287        let mut c = caixa_with_kind(CaixaKind::Supervisor);
14288        // A Supervisor caixa needs a strategy + at least one child to
14289        // fold to a Some(_) that also validates; the composer itself
14290        // requires only the kind arm, so bare kind flip is enough to
14291        // pin the `Some(_)` return, but we populate the minimum
14292        // supervisor shape so a future strengthening of the composer
14293        // to reject an empty spec doesn't false-positive this pin.
14294        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
14295        c.children = vec![crate::supervisor::ChildSpec {
14296            caixa: "child".into(),
14297            versao: "^0.1".into(),
14298            restart: crate::supervisor::RestartPolicy::Permanent,
14299        }];
14300        assert!(
14301            c.supervisor_view().is_some(),
14302            "supervisor_view must return Some on kind Supervisor — \
14303             the composer's kind-gate must accept the matching arm \
14304             through Caixa::kind()",
14305        );
14306    }
14307
14308    #[test]
14309    fn kind_projects_by_copy() {
14310        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
14311        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
14312        // `&self` (the returned value is owned, `Copy`-projected from
14313        // the underlying [`CaixaKind`] storage; two calls on the same
14314        // [`Caixa`] must yield byte-equal values). Peer of the peer
14315        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
14316        // `SupervisorSpec::estrategia` / per-`:children`
14317        // `ChildSpec::restart` `Copy`-return discriminant accessor
14318        // pins on the sibling nested-spec typed-slot discriminator
14319        // axes, extended onto the first outer top-level [`Caixa`]
14320        // required-`Copy`-return axis — pins against a future silent
14321        // detour that returned `&CaixaKind` (which would type-check
14322        // but silently constrain every consumer's callsite to a
14323        // borrow-shaped dispatch, breaking the zero-cost `Copy`
14324        // projection every peer sibling accessor carries).
14325        for kind in [
14326            CaixaKind::Biblioteca,
14327            CaixaKind::Binario,
14328            CaixaKind::Servico,
14329            CaixaKind::Supervisor,
14330            CaixaKind::Aplicacao,
14331        ] {
14332            let c = caixa_with_kind(kind);
14333            let first: CaixaKind = c.kind();
14334            let second: CaixaKind = c.kind();
14335            assert_eq!(
14336                first, second,
14337                "Caixa::kind must be idempotent — two successive \
14338                 calls on the same &self must return the same \
14339                 CaixaKind variant",
14340            );
14341            assert_eq!(
14342                first, kind,
14343                "Caixa::kind must return :kind verbatim by Copy — \
14344                 got {first:?}, expected {kind:?}",
14345            );
14346        }
14347    }
14348
14349    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
14350
14351    #[test]
14352    fn autores_returns_autores_slice_verbatim_across_permutations() {
14353        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
14354        // name-list slice pin: [`Caixa::autores`] must return the
14355        // `:autores` typed [`Vec<String>`] list verbatim as a
14356        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
14357        // access across every representative value in the accept-set —
14358        // `[]` (the "no maintainers declared" arm every existing
14359        // fixture without an `:autores` line carries), `[""]` (a past-
14360        // the-guard sentinel that pins the accessor doesn't perform a
14361        // silent `[""] → []` collapse on the empty-entry arm — validate
14362        // rejects `[""]` through `AutorEmpty` but the accessor must
14363        // ship the raw slot verbatim so a validate-time gate regression
14364        // surfaces at the caixa-helm emit boundary rather than being
14365        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
14366        // canonical single-maintainer form every `feira init` template
14367        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
14368        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
14369        // (the canonical RFC-5322 `<name> <email>` form the
14370        // `is_chart_maintainer_name_shape` predicate accepts), and
14371        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
14372        // sentinel — validate rejects through `AutorDuplicate` but the
14373        // accessor must ship the raw slot verbatim).
14374        //
14375        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
14376        // pin on the substrate primitive — opens the "outer [`Caixa`]
14377        // `&[T]` slice" projection pattern the sibling per-`Caixa`
14378        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
14379        // / `:servicos` / `:upgrade-from` / `:children` future lifts
14380        // fold on. Sibling in shape to the peer per-`:supervisor`
14381        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
14382        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
14383        // (a6e18d7), per-`:membros`
14384        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
14385        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14386        // (0dcc926), and per-`:upgrade-from :instructions`
14387        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
14388        // `&[T]`-return slice accessor pins on the sibling per-M2 /
14389        // per-M3 typed-slot list axes, extended onto the outer top-
14390        // level [`Caixa`] universal-axis surface. Pins against a future
14391        // silent detour that returned an owned `Vec<String>` (which
14392        // would type-check but silently clone on every accessor call,
14393        // breaking the zero-cost projection every peer sibling slice
14394        // accessor carries), a `[""] → []` collapse (which would
14395        // silently absorb the `AutorEmpty` refusal case at the accessor
14396        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
14397        // would silently absorb the `AutorDuplicate` refusal case at
14398        // the accessor boundary and the caixa-helm `maintainers:` fold
14399        // would silently render a dedupped list on a struct-literal
14400        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
14401        for autores in [
14402            vec![],
14403            vec![""],
14404            vec!["pleme-io"],
14405            vec!["alice", "bob"],
14406            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
14407            vec!["pleme-io", "pleme-io"],
14408        ] {
14409            let c = caixa_with_autores(autores.clone());
14410            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14411            assert_eq!(
14412                c.autores(),
14413                expected.as_slice(),
14414                "Caixa::autores must return :autores verbatim (got {:?}, \
14415                 expected {expected:?})",
14416                c.autores(),
14417            );
14418            assert_eq!(
14419                c.autores(),
14420                c.autores.as_slice(),
14421                "Caixa::autores must byte-equal the raw \
14422                 `self.autores.as_slice()` field access across every \
14423                 value in the Vec<String> accept-set",
14424            );
14425        }
14426    }
14427
14428    #[test]
14429    fn validate_autores_empty_entry_arm_routes_through_accessor() {
14430        // Composition pin: [`Caixa::validate_autores`]'s per-entry
14431        // empty-arm gate must key off [`Caixa::autores`], not the raw
14432        // `&self.autores` field-borrow walk. Structurally: a
14433        // `Caixa { autores: vec!["".into()], .. }` must surface the
14434        // `AutorEmpty` refusal exactly, and a
14435        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
14436        // canonical single-maintainer form) must pass validate. The
14437        // pair jointly pins the accessor + validate-gate composition:
14438        // any future silent detour that had the accessor return an
14439        // empty slice on the `[""]` arm (a
14440        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
14441        // would silently absorb the `AutorEmpty` refusal at the
14442        // accessor boundary and the validate gate would accept a
14443        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
14444        // the composition pin catches that at caixa-core build time.
14445        //
14446        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
14447        // accessor-composition pin
14448        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
14449        // sibling `Option<&str>`-composition axis and the
14450        // per-`:politicas :circuit-breaker`
14451        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
14452        // accessor-composition pin
14453        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
14454        // on the sibling required-`u32`-composition axis — same "the
14455        // validate / shape-gate predicate must route through the
14456        // substrate-primitive typed dispatch" discipline extended onto
14457        // the outer top-level [`Caixa`] universal-axis `&[T]`-
14458        // composition surface.
14459        let c = caixa_with_autores(vec![""]);
14460        assert!(
14461            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
14462            "validate_autores must reject autores == vec![\"\"] with \
14463             AutorEmpty — the accessor and the validate gate must \
14464             route through the same substrate-primitive typed dispatch \
14465             on the :autores per-entry empty arm",
14466        );
14467        let c = caixa_with_autores(vec!["pleme-io"]);
14468        assert!(
14469            c.validate_autores().is_ok(),
14470            "validate_autores must accept autores == vec![\"pleme-io\"] \
14471             (the canonical single-maintainer shape every `feira init` \
14472             template scaffolds)",
14473        );
14474    }
14475
14476    #[test]
14477    fn autores_projects_slice_by_borrow() {
14478        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
14479        // borrow — the returned slice borrows the underlying
14480        // `Vec<String>` storage of the `:autores` slot and the
14481        // accessor must not clone the backing `Vec` on every call.
14482        // Peer of the per-`:membros`
14483        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
14484        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14485        // (0dcc926) / per-`:placement`
14486        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
14487        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
14488        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
14489        // typed-slot `&[T]`-return axes, extended onto the outer top-
14490        // level [`Caixa`] universal-axis `&[String]` shape — the
14491        // accessor's returned slice must borrow from `&self` (the
14492        // returned reference's lifetime is tied to `&self`), and
14493        // calling the accessor twice on the same [`Caixa`] must yield
14494        // slices that are pointer-equal (the underlying byte-buffer is
14495        // the storage `Vec`'s allocation, not a fresh copy) as well as
14496        // value-equal (idempotent, no side effects on `&self`).
14497        //
14498        // Pins against a future silent detour that returned an owned
14499        // `Vec<String>` (which would type-check but silently clone on
14500        // every call, breaking the zero-cost projection every peer
14501        // sibling slice accessor carries), a `&Vec<String>` return
14502        // (which would leak the backing `Vec`'s grow/push/reserve
14503        // surface no downstream consumer reaches for), or a one-arm-
14504        // only accessor that returned a saturating value on some
14505        // sentinel input (breaking the pass-through invariant the
14506        // sibling slice accessors carry).
14507        for autores in [
14508            vec![],
14509            vec!["pleme-io"],
14510            vec!["alice", "bob"],
14511            vec!["pleme-io", "pleme-io"],
14512        ] {
14513            let c = caixa_with_autores(autores.clone());
14514            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14515            let first = c.autores();
14516            let second = c.autores();
14517            assert_eq!(
14518                first, second,
14519                "Caixa::autores must be idempotent — two successive \
14520                 calls on the same &self must return the same \
14521                 &[String]",
14522            );
14523            assert_eq!(
14524                first.as_ptr(),
14525                second.as_ptr(),
14526                "Caixa::autores must borrow the underlying Vec<String> \
14527                 storage — two successive calls must return slices \
14528                 with the same backing pointer (a fresh Vec<String> \
14529                 clone would change the pointer on every call)",
14530            );
14531            assert_eq!(
14532                first,
14533                expected.as_slice(),
14534                "Caixa::autores must return :autores verbatim by \
14535                 borrow — got {first:?}, expected {expected:?}",
14536            );
14537        }
14538    }
14539
14540    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
14541
14542    #[test]
14543    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
14544        // The canonical per-`Caixa` `:etiquetas` universal-axis
14545        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
14546        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
14547        // as a `&[String]`, byte-equal to the raw
14548        // `self.etiquetas.as_slice()` access across every representative
14549        // value in the accept-set — `[]` (the "no tags declared" arm
14550        // every existing fixture without an `:etiquetas` line carries),
14551        // `[""]` (a past-the-guard sentinel that pins the accessor
14552        // doesn't perform a silent `[""] → []` collapse on the empty-
14553        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
14554        // but the accessor must ship the raw slot verbatim so a
14555        // validate-time gate regression surfaces at the caixa-helm emit
14556        // boundary rather than being silently absorbed into a keyword-
14557        // drop), `["demo"]` (the canonical single-tag form every
14558        // `feira init` template scaffolds), `["example", "aplicacao",
14559        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
14560        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
14561        // (a past-the-guard duplicate sentinel — validate rejects
14562        // through `EtiquetaDuplicate` but the accessor must ship the
14563        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
14564        // at chart-render time isn't silently promoted into the
14565        // accessor boundary and struct-literal
14566        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
14567        // fixtures continue to expose the duplicate at the accessor).
14568        //
14569        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
14570        // pin on the substrate primitive — folds on the "outer
14571        // [`Caixa`] `&[T]` slice" projection pattern
14572        // `autores_returns_autores_slice_verbatim_across_permutations`
14573        // (b5d813f) opened, sibling in shape and idiom. Pins against a
14574        // future silent detour that returned an owned `Vec<String>`
14575        // (which would type-check but silently clone on every accessor
14576        // call, breaking the zero-cost projection every peer sibling
14577        // slice accessor carries), a `[""] → []` collapse (which would
14578        // silently absorb the `EtiquetaEmpty` refusal case at the
14579        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
14580        // (which would silently absorb the `EtiquetaDuplicate` refusal
14581        // case at the accessor boundary — the caixa-helm chart-render
14582        // `BTreeSet::collect` dedup is downstream of the accessor and
14583        // must not be silently promoted into it).
14584        for etiquetas in [
14585            vec![],
14586            vec![""],
14587            vec!["demo"],
14588            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
14589            vec!["demo", "demo"],
14590        ] {
14591            let c = caixa_with_etiquetas(etiquetas.clone());
14592            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14593            assert_eq!(
14594                c.etiquetas(),
14595                expected.as_slice(),
14596                "Caixa::etiquetas must return :etiquetas verbatim (got \
14597                 {:?}, expected {expected:?})",
14598                c.etiquetas(),
14599            );
14600            assert_eq!(
14601                c.etiquetas(),
14602                c.etiquetas.as_slice(),
14603                "Caixa::etiquetas must byte-equal the raw \
14604                 `self.etiquetas.as_slice()` field access across every \
14605                 value in the Vec<String> accept-set",
14606            );
14607        }
14608    }
14609
14610    #[test]
14611    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
14612        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
14613        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
14614        // `&self.etiquetas` field-borrow walk. Structurally: a
14615        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
14616        // `EtiquetaEmpty` refusal exactly, and a
14617        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
14618        // single-tag form) must pass validate. The pair jointly pins
14619        // the accessor + validate-gate composition: any future silent
14620        // detour that had the accessor return an empty slice on the
14621        // `[""]` arm (a
14622        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
14623        // silently absorb the `EtiquetaEmpty` refusal at the accessor
14624        // boundary and the validate gate would accept a struct-literal
14625        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
14626        // pin catches that at caixa-core build time.
14627        //
14628        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
14629        // through_accessor` (b5d813f) accessor-composition pin on the
14630        // sibling `&[T]`-composition axis — same "the validate / shape-
14631        // gate predicate must route through the substrate-primitive
14632        // typed dispatch" discipline extended onto the sibling outer
14633        // top-level [`Caixa`] `&[T]`-composition surface.
14634        let c = caixa_with_etiquetas(vec![""]);
14635        assert!(
14636            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
14637            "validate_etiquetas must reject etiquetas == vec![\"\"] \
14638             with EtiquetaEmpty — the accessor and the validate gate \
14639             must route through the same substrate-primitive typed \
14640             dispatch on the :etiquetas per-entry empty arm",
14641        );
14642        let c = caixa_with_etiquetas(vec!["demo"]);
14643        assert!(
14644            c.validate_etiquetas().is_ok(),
14645            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
14646             (the canonical single-tag shape every `feira init` \
14647             template scaffolds)",
14648        );
14649    }
14650
14651    #[test]
14652    fn etiquetas_projects_slice_by_borrow() {
14653        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
14654        // by borrow — the returned slice borrows the underlying
14655        // `Vec<String>` storage of the `:etiquetas` slot and the
14656        // accessor must not clone the backing `Vec` on every call.
14657        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14658        // (b5d813f) by-borrow pin on the sibling outer top-level
14659        // [`Caixa`] `&[String]`-return axis — the accessor's returned
14660        // slice must borrow from `&self` (the returned reference's
14661        // lifetime is tied to `&self`), and calling the accessor twice
14662        // on the same [`Caixa`] must yield slices that are pointer-
14663        // equal (the underlying byte-buffer is the storage `Vec`'s
14664        // allocation, not a fresh copy) as well as value-equal
14665        // (idempotent, no side effects on `&self`).
14666        //
14667        // Pins against a future silent detour that returned an owned
14668        // `Vec<String>` (which would type-check but silently clone on
14669        // every call, breaking the zero-cost projection every peer
14670        // sibling slice accessor carries), a `&Vec<String>` return
14671        // (which would leak the backing `Vec`'s grow/push/reserve
14672        // surface no downstream consumer reaches for), or a one-arm-
14673        // only accessor that returned a saturating value on some
14674        // sentinel input (breaking the pass-through invariant the
14675        // sibling slice accessors carry).
14676        for etiquetas in [
14677            vec![],
14678            vec!["demo"],
14679            vec!["example", "aplicacao", "mesh"],
14680            vec!["demo", "demo"],
14681        ] {
14682            let c = caixa_with_etiquetas(etiquetas.clone());
14683            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14684            let first = c.etiquetas();
14685            let second = c.etiquetas();
14686            assert_eq!(
14687                first, second,
14688                "Caixa::etiquetas must be idempotent — two successive \
14689                 calls on the same &self must return the same \
14690                 &[String]",
14691            );
14692            assert_eq!(
14693                first.as_ptr(),
14694                second.as_ptr(),
14695                "Caixa::etiquetas must borrow the underlying \
14696                 Vec<String> storage — two successive calls must \
14697                 return slices with the same backing pointer (a fresh \
14698                 Vec<String> clone would change the pointer on every \
14699                 call)",
14700            );
14701            assert_eq!(
14702                first,
14703                expected.as_slice(),
14704                "Caixa::etiquetas must return :etiquetas verbatim by \
14705                 borrow — got {first:?}, expected {expected:?}",
14706            );
14707        }
14708    }
14709
14710    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
14711
14712    #[test]
14713    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
14714        // The canonical per-`Caixa` `:bibliotecas` universal-axis
14715        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
14716        // must return the `:bibliotecas` typed [`Vec<String>`] list
14717        // verbatim as a `&[String]`, byte-equal to the raw
14718        // `self.bibliotecas.as_slice()` access across every
14719        // representative value in the accept-set — `[]` (the "no
14720        // libraries declared" arm every `:kind` other than `Biblioteca`
14721        // + every `Biblioteca` relying on the canonical
14722        // `lib/<nome>.lisp` implicit-default path carries; the
14723        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
14724        // fires exactly on this empty-slot + `Biblioteca`-kind
14725        // combination), `[""]` (a past-the-guard sentinel that pins
14726        // the accessor doesn't perform a silent `[""] → []` collapse
14727        // on the empty-entry arm — validate rejects `[""]` through
14728        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
14729        // must ship the raw slot verbatim so a validate-time gate
14730        // regression surfaces at the `feira build` phase-1 parse
14731        // boundary rather than being silently absorbed into a
14732        // library-drop), `["lib/demo.lisp"]` (the canonical single-
14733        // entry form `Caixa::template` scaffolds and every `feira init`
14734        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
14735        // (the canonical multi-library form the
14736        // `validate_code_paths_accepts_explicit_relative_paths_on_
14737        // every_slot` fixture emits), and `["lib/foo.lisp",
14738        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
14739        // validate rejects through `CodePathDuplicate { slot:
14740        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
14741        // but the accessor must ship the raw slot verbatim so the
14742        // `feira build` `for entry in caixa.bibliotecas()` parse walk
14743        // sees the duplicate at the accessor boundary and struct-
14744        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
14745        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
14746        // the duplicate at the accessor).
14747        //
14748        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
14749        // pin on the substrate primitive — folds on the "outer
14750        // [`Caixa`] `&[T]` slice" projection pattern
14751        // `autores_returns_autores_slice_verbatim_across_permutations`
14752        // (b5d813f) opened and
14753        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14754        // (78c7d3c) folded on, sibling in shape and idiom. Pins
14755        // against a future silent detour that returned an owned
14756        // `Vec<String>` (which would type-check but silently clone on
14757        // every accessor call, breaking the zero-cost projection
14758        // every peer sibling slice accessor carries), a `[""] → []`
14759        // collapse (which would silently absorb the `CodePathEmpty`
14760        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
14761        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
14762        // would silently absorb the `CodePathDuplicate` refusal case
14763        // at the accessor boundary — the per-slot set-not-multiset
14764        // gate is downstream of the accessor and must not be silently
14765        // promoted into it).
14766        for bibliotecas in [
14767            vec![],
14768            vec![""],
14769            vec!["lib/demo.lisp"],
14770            vec!["lib/demo.lisp", "lib/helpers.lisp"],
14771            vec!["lib/foo.lisp", "lib/foo.lisp"],
14772        ] {
14773            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
14774            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
14775            assert_eq!(
14776                c.bibliotecas(),
14777                expected.as_slice(),
14778                "Caixa::bibliotecas must return :bibliotecas verbatim \
14779                 (got {:?}, expected {expected:?})",
14780                c.bibliotecas(),
14781            );
14782            assert_eq!(
14783                c.bibliotecas(),
14784                c.bibliotecas.as_slice(),
14785                "Caixa::bibliotecas must byte-equal the raw \
14786                 `self.bibliotecas.as_slice()` field access across \
14787                 every value in the Vec<String> accept-set",
14788            );
14789        }
14790    }
14791
14792    #[test]
14793    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
14794        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
14795        // empty-arm gate on the `:bibliotecas` slot must key off
14796        // [`Caixa::bibliotecas`], not a divergent raw
14797        // `&self.bibliotecas` field-borrow walk. Structurally: a
14798        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
14799        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
14800        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
14801        // into()], .. }` (the canonical single-library form
14802        // `Caixa::template` scaffolds) must pass validate. The pair
14803        // jointly pins the accessor + validate-gate composition: any
14804        // future silent detour that had the accessor return an empty
14805        // slice on the `[""]` arm (a `.iter().filter(|s|
14806        // !s.is_empty()).collect()` collapse) would silently absorb
14807        // the `CodePathEmpty` refusal at the accessor boundary and
14808        // the validate gate would accept a struct-literal
14809        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
14810        // composition pin catches that at caixa-core build time.
14811        //
14812        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
14813        // through_accessor` (b5d813f) and
14814        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14815        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
14816        // composition axes — same "the validate / shape-gate
14817        // predicate must route through the substrate-primitive typed
14818        // dispatch" discipline extended onto the sibling outer top-
14819        // level [`Caixa`] `&[T]`-composition surface. Nominally the
14820        // in-tree `validate_code_paths` production body still keys
14821        // off the internal `[(":bibliotecas", &self.bibliotecas,
14822        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
14823        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
14824        // (the tuple's homogeneous slice-typed shape blocks a per-
14825        // element accessor swap in isolation — a future companion
14826        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
14827        // `&[T]` slice-accessor axis closes that tuple onto the
14828        // triple of typed dispatches as a unit); the composition pin
14829        // catches any future accessor-side silent filter drop against
14830        // that eventual tuple-closure regardless of whether the
14831        // `:bibliotecas` slot is threaded through the accessor or the
14832        // raw field access at the tuple's construction site.
14833        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
14834        assert!(
14835            matches!(
14836                c.validate_code_paths(),
14837                Err(ManifestError::CodePathEmpty {
14838                    slot: ":bibliotecas"
14839                })
14840            ),
14841            "validate_code_paths must reject bibliotecas == vec![\"\"] \
14842             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
14843             accessor and the validate gate must route through the \
14844             same substrate-primitive typed dispatch on the \
14845             :bibliotecas per-entry empty arm",
14846        );
14847        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
14848        assert!(
14849            c.validate_code_paths().is_ok(),
14850            "validate_code_paths must accept bibliotecas == \
14851             vec![\"lib/demo.lisp\"] (the canonical single-library \
14852             shape every `feira init` template scaffolds)",
14853        );
14854    }
14855
14856    #[test]
14857    fn bibliotecas_projects_slice_by_borrow() {
14858        // The by-borrow pin: [`Caixa::bibliotecas`] returns
14859        // `&[String]` by borrow — the returned slice borrows the
14860        // underlying `Vec<String>` storage of the `:bibliotecas` slot
14861        // and the accessor must not clone the backing `Vec` on every
14862        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14863        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
14864        // by-borrow pins on the sibling outer top-level [`Caixa`]
14865        // `&[String]`-return axes — the accessor's returned slice
14866        // must borrow from `&self` (the returned reference's lifetime
14867        // is tied to `&self`), and calling the accessor twice on the
14868        // same [`Caixa`] must yield slices that are pointer-equal
14869        // (the underlying byte-buffer is the storage `Vec`'s
14870        // allocation, not a fresh copy) as well as value-equal
14871        // (idempotent, no side effects on `&self`).
14872        //
14873        // Pins against a future silent detour that returned an owned
14874        // `Vec<String>` (which would type-check but silently clone on
14875        // every call, breaking the zero-cost projection every peer
14876        // sibling slice accessor carries), a `&Vec<String>` return
14877        // (which would leak the backing `Vec`'s grow/push/reserve
14878        // surface no downstream consumer reaches for), or a one-arm-
14879        // only accessor that returned a saturating value on some
14880        // sentinel input (breaking the pass-through invariant the
14881        // sibling slice accessors carry).
14882        for bibliotecas in [
14883            vec![],
14884            vec!["lib/demo.lisp"],
14885            vec!["lib/demo.lisp", "lib/helpers.lisp"],
14886            vec!["lib/foo.lisp", "lib/foo.lisp"],
14887        ] {
14888            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
14889            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
14890            let first = c.bibliotecas();
14891            let second = c.bibliotecas();
14892            assert_eq!(
14893                first, second,
14894                "Caixa::bibliotecas must be idempotent — two \
14895                 successive calls on the same &self must return the \
14896                 same &[String]",
14897            );
14898            assert_eq!(
14899                first.as_ptr(),
14900                second.as_ptr(),
14901                "Caixa::bibliotecas must borrow the underlying \
14902                 Vec<String> storage — two successive calls must \
14903                 return slices with the same backing pointer (a \
14904                 fresh Vec<String> clone would change the pointer on \
14905                 every call)",
14906            );
14907            assert_eq!(
14908                first,
14909                expected.as_slice(),
14910                "Caixa::bibliotecas must return :bibliotecas verbatim \
14911                 by borrow — got {first:?}, expected {expected:?}",
14912            );
14913        }
14914    }
14915
14916    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
14917
14918    #[test]
14919    fn exe_returns_exe_slice_verbatim_across_permutations() {
14920        // The canonical per-`Caixa` `:exe` universal-axis
14921        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
14922        // must return the `:exe` typed [`Vec<String>`] list verbatim as
14923        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
14924        // access across every representative value in the accept-set —
14925        // `[]` (the "no executable declared" arm every `:kind` other
14926        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
14927        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
14928        // + `Binario`-kind combination), `[""]` (a past-the-guard
14929        // sentinel that pins the accessor doesn't perform a silent
14930        // `[""] → []` collapse on the empty-entry arm — validate rejects
14931        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
14932        // accessor must ship the raw slot verbatim so a validate-time
14933        // gate regression surfaces at the layout / `feira nix` boundary
14934        // rather than being silently absorbed into an executable-drop),
14935        // `["exe/cli"]` (the canonical single-entry Binario form every
14936        // in-tree `caixa_with_code_paths` positive control uses),
14937        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
14938        // form the `validate_code_paths_accepts_explicit_relative_paths_
14939        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
14940        // (a past-the-guard duplicate sentinel — validate rejects
14941        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
14942        // set-not-multiset gate, but the accessor must ship the raw
14943        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
14944        // into(), "exe/cli".into()], .. }` fixtures continue to expose
14945        // the duplicate at the accessor).
14946        //
14947        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
14948        // pin on the substrate primitive — folds on the "outer
14949        // [`Caixa`] `&[T]` slice" projection pattern
14950        // `autores_returns_autores_slice_verbatim_across_permutations`
14951        // (b5d813f) opened,
14952        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14953        // (78c7d3c) folded on, and
14954        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14955        // (8a36c23) closed the universal-axis text-tag family of.
14956        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
14957        // the sibling `:servicos` future lift closes onto. Pins against
14958        // a future silent detour that returned an owned `Vec<String>`
14959        // (which would type-check but silently clone on every accessor
14960        // call, breaking the zero-cost projection every peer sibling
14961        // slice accessor carries), a `[""] → []` collapse (which would
14962        // silently absorb the `CodePathEmpty` refusal case at the
14963        // accessor boundary), or an `["exe/cli", "exe/cli"] →
14964        // ["exe/cli"]` dedup collapse (which would silently absorb the
14965        // `CodePathDuplicate` refusal case at the accessor boundary —
14966        // the per-slot set-not-multiset gate is downstream of the
14967        // accessor and must not be silently promoted into it).
14968        for exe in [
14969            vec![],
14970            vec![""],
14971            vec!["exe/cli"],
14972            vec!["exe/cli", "exe/serve"],
14973            vec!["exe/cli", "exe/cli"],
14974        ] {
14975            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
14976            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
14977            assert_eq!(
14978                c.exe(),
14979                expected.as_slice(),
14980                "Caixa::exe must return :exe verbatim (got {:?}, \
14981                 expected {expected:?})",
14982                c.exe(),
14983            );
14984            assert_eq!(
14985                c.exe(),
14986                c.exe.as_slice(),
14987                "Caixa::exe must byte-equal the raw \
14988                 `self.exe.as_slice()` field access across every value \
14989                 in the Vec<String> accept-set",
14990            );
14991        }
14992    }
14993
14994    #[test]
14995    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
14996        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
14997        // empty-arm gate on the `:exe` slot must key off
14998        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
14999        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
15000        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
15001        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
15002        // (the canonical single-executable form every in-tree
15003        // `caixa_with_code_paths` positive control uses) must pass
15004        // validate. The pair jointly pins the accessor + validate-gate
15005        // composition: any future silent detour that had the accessor
15006        // return an empty slice on the `[""]` arm (a
15007        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
15008        // silently absorb the `CodePathEmpty` refusal at the accessor
15009        // boundary and the validate gate would accept a struct-literal
15010        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
15011        // catches that at caixa-core build time.
15012        //
15013        // Peer of the per-`Caixa`
15014        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15015        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
15016        // (b5d813f), and
15017        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15018        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15019        // composition axes — same "the validate / shape-gate predicate
15020        // must route through the substrate-primitive typed dispatch"
15021        // discipline extended onto the sibling outer top-level [`Caixa`]
15022        // `&[T]`-composition surface. Nominally the in-tree
15023        // `validate_code_paths` production body still keys off the
15024        // internal `[(":bibliotecas", &self.bibliotecas,
15025        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15026        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15027        // (the tuple's homogeneous slice-typed shape blocks a per-
15028        // element accessor swap in isolation — a future companion lift
15029        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
15030        // accessor axis closes that tuple onto the triple of typed
15031        // dispatches as a unit); the composition pin catches any future
15032        // accessor-side silent filter drop against that eventual tuple-
15033        // closure regardless of whether the `:exe` slot is threaded
15034        // through the accessor or the raw field access at the tuple's
15035        // construction site.
15036        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
15037        assert!(
15038            matches!(
15039                c.validate_code_paths(),
15040                Err(ManifestError::CodePathEmpty { slot: ":exe" })
15041            ),
15042            "validate_code_paths must reject exe == vec![\"\"] \
15043             with CodePathEmpty {{ slot: \":exe\" }} — the \
15044             accessor and the validate gate must route through the \
15045             same substrate-primitive typed dispatch on the \
15046             :exe per-entry empty arm",
15047        );
15048        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
15049        assert!(
15050            c.validate_code_paths().is_ok(),
15051            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
15052             (the canonical single-executable shape every in-tree \
15053             `caixa_with_code_paths` positive control uses)",
15054        );
15055    }
15056
15057    #[test]
15058    fn exe_projects_slice_by_borrow() {
15059        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
15060        // borrow — the returned slice borrows the underlying
15061        // `Vec<String>` storage of the `:exe` slot and the accessor
15062        // must not clone the backing `Vec` on every call. Peer of the
15063        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
15064        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
15065        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
15066        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
15067        // return axes — the accessor's returned slice must borrow from
15068        // `&self` (the returned reference's lifetime is tied to
15069        // `&self`), and calling the accessor twice on the same
15070        // [`Caixa`] must yield slices that are pointer-equal (the
15071        // underlying byte-buffer is the storage `Vec`'s allocation,
15072        // not a fresh copy) as well as value-equal (idempotent, no
15073        // side effects on `&self`).
15074        //
15075        // Pins against a future silent detour that returned an owned
15076        // `Vec<String>` (which would type-check but silently clone on
15077        // every call, breaking the zero-cost projection every peer
15078        // sibling slice accessor carries), a `&Vec<String>` return
15079        // (which would leak the backing `Vec`'s grow/push/reserve
15080        // surface no downstream consumer reaches for), or a one-arm-
15081        // only accessor that returned a saturating value on some
15082        // sentinel input (breaking the pass-through invariant the
15083        // sibling slice accessors carry).
15084        for exe in [
15085            vec![],
15086            vec!["exe/cli"],
15087            vec!["exe/cli", "exe/serve"],
15088            vec!["exe/cli", "exe/cli"],
15089        ] {
15090            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15091            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15092            let first = c.exe();
15093            let second = c.exe();
15094            assert_eq!(
15095                first, second,
15096                "Caixa::exe must be idempotent — two successive calls \
15097                 on the same &self must return the same &[String]",
15098            );
15099            assert_eq!(
15100                first.as_ptr(),
15101                second.as_ptr(),
15102                "Caixa::exe must borrow the underlying Vec<String> \
15103                 storage — two successive calls must return slices \
15104                 with the same backing pointer (a fresh Vec<String> \
15105                 clone would change the pointer on every call)",
15106            );
15107            assert_eq!(
15108                first,
15109                expected.as_slice(),
15110                "Caixa::exe must return :exe verbatim by borrow — \
15111                 got {first:?}, expected {expected:?}",
15112            );
15113        }
15114    }
15115
15116    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
15117
15118    #[test]
15119    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
15120        // The canonical per-`Caixa` `:servicos` universal-axis
15121        // ComputeUnit-CR-YAML-entry-path-list slice pin:
15122        // [`Caixa::servicos`] must return the `:servicos` typed
15123        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
15124        // the raw `self.servicos.as_slice()` access across every
15125        // representative value in the accept-set — `[]` (the "no
15126        // ComputeUnit-CR declared" arm every `:kind` other than
15127        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
15128        // `ServicoWithoutServicos` arm-gate fires exactly on this
15129        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
15130        // guard sentinel that pins the accessor doesn't perform a
15131        // silent `[""] → []` collapse on the empty-entry arm — validate
15132        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
15133        // but the accessor must ship the raw slot verbatim so a
15134        // validate-time gate regression surfaces at the layout /
15135        // per-Servico renderer boundary rather than being silently
15136        // absorbed into a component-drop),
15137        // `["servicos/demo.computeunit.yaml"]` (the canonical
15138        // singleton V0-shape every in-tree `caixa_with_code_paths`
15139        // positive control uses; the same shape
15140        // [`crate::require_single_servico`] admits),
15141        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
15142        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
15143        // singularity gate rejects through `ServicoCountMismatch
15144        // { count: 2 }` but the accessor must ship the raw slot
15145        // verbatim so struct-literal `Caixa { servicos: vec![...,
15146        // ...], .. }` fixtures continue to expose the count at the
15147        // accessor), and `["servicos/a.computeunit.yaml",
15148        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
15149        // sentinel — validate rejects through
15150        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
15151        // set-not-multiset gate, but the accessor must ship the raw
15152        // slot verbatim so struct-literal fixtures continue to expose
15153        // the duplicate at the accessor).
15154        //
15155        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
15156        // slice accessor pin on the substrate primitive — folds on the
15157        // "outer [`Caixa`] `&[T]` slice" projection pattern
15158        // `autores_returns_autores_slice_verbatim_across_permutations`
15159        // (b5d813f) opened,
15160        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15161        // (78c7d3c) folded on,
15162        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15163        // (8a36c23) closed the universal-axis text-tag family of, and
15164        // `exe_returns_exe_slice_verbatim_across_permutations`
15165        // (65d9527) opened the foreign-code-slot sub-family of. Closes
15166        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
15167        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
15168        // `:servicos`) now each carries a substrate-canonical slice
15169        // accessor. Pins against a future silent detour that returned
15170        // an owned `Vec<String>` (which would type-check but silently
15171        // clone on every accessor call, breaking the zero-cost
15172        // projection every peer sibling slice accessor carries), a
15173        // `[""] → []` collapse (which would silently absorb the
15174        // `CodePathEmpty` refusal case at the accessor boundary), an
15175        // `[a, a] → [a]` dedup collapse (which would silently absorb
15176        // the `CodePathDuplicate` refusal case at the accessor
15177        // boundary — the per-slot set-not-multiset gate is downstream
15178        // of the accessor and must not be silently promoted into it),
15179        // or a `[a, b] → [a]` singleton collapse (which would silently
15180        // absorb the V0 `ServicoCountMismatch` refusal case at the
15181        // accessor boundary — the V0 singularity gate is downstream of
15182        // the accessor and must not be silently promoted into it).
15183        for servicos in [
15184            vec![],
15185            vec![""],
15186            vec!["servicos/demo.computeunit.yaml"],
15187            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15188            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15189        ] {
15190            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15191            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15192            assert_eq!(
15193                c.servicos(),
15194                expected.as_slice(),
15195                "Caixa::servicos must return :servicos verbatim (got \
15196                 {:?}, expected {expected:?})",
15197                c.servicos(),
15198            );
15199            assert_eq!(
15200                c.servicos(),
15201                c.servicos.as_slice(),
15202                "Caixa::servicos must byte-equal the raw \
15203                 `self.servicos.as_slice()` field access across every \
15204                 value in the Vec<String> accept-set",
15205            );
15206        }
15207    }
15208
15209    #[test]
15210    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
15211        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15212        // empty-arm gate on the `:servicos` slot must key off
15213        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
15214        // field-borrow walk. Structurally: a `Caixa { servicos:
15215        // vec!["".into()], .. }` must surface the `CodePathEmpty
15216        // { slot: ":servicos" }` refusal exactly, and a `Caixa
15217        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
15218        // .. }` (the canonical singleton V0-shape every in-tree
15219        // `caixa_with_code_paths` positive control uses) must pass
15220        // validate. The pair jointly pins the accessor + validate-gate
15221        // composition: any future silent detour that had the accessor
15222        // return an empty slice on the `[""]` arm (a `.iter().filter
15223        // (|s| !s.is_empty()).collect()` collapse) would silently
15224        // absorb the `CodePathEmpty` refusal at the accessor boundary
15225        // and the validate gate would accept a struct-literal
15226        // `Caixa { servicos: vec!["".into()], .. }` — the composition
15227        // pin catches that at caixa-core build time.
15228        //
15229        // Peer of the per-`Caixa`
15230        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15231        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15232        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
15233        // (b5d813f), and
15234        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15235        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15236        // composition axes — same "the validate / shape-gate predicate
15237        // must route through the substrate-primitive typed dispatch"
15238        // discipline extended onto the sibling outer top-level
15239        // [`Caixa`] `&[T]`-composition surface, closing the trio of
15240        // code-surface accessor-composition pins on the same axis.
15241        // Nominally the in-tree `validate_code_paths` production body
15242        // still keys off the internal
15243        // `[(":bibliotecas", &self.bibliotecas,
15244        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15245        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15246        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
15247        // per-element accessor swap in isolation — a future companion
15248        // lift promotes the tuple's element type to `&[String]` and
15249        // threads the triple of typed dispatches through as a unit);
15250        // the composition pin catches any future accessor-side silent
15251        // filter drop against that eventual tuple-closure regardless
15252        // of whether the `:servicos` slot is threaded through the
15253        // accessor or the raw field access at the tuple's construction
15254        // site.
15255        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
15256        assert!(
15257            matches!(
15258                c.validate_code_paths(),
15259                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
15260            ),
15261            "validate_code_paths must reject servicos == vec![\"\"] \
15262             with CodePathEmpty {{ slot: \":servicos\" }} — the \
15263             accessor and the validate gate must route through the \
15264             same substrate-primitive typed dispatch on the \
15265             :servicos per-entry empty arm",
15266        );
15267        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
15268        assert!(
15269            c.validate_code_paths().is_ok(),
15270            "validate_code_paths must accept servicos == \
15271             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
15272             singleton V0-shape every in-tree `caixa_with_code_paths` \
15273             positive control uses)",
15274        );
15275    }
15276
15277    #[test]
15278    fn servicos_projects_slice_by_borrow() {
15279        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
15280        // borrow — the returned slice borrows the underlying
15281        // `Vec<String>` storage of the `:servicos` slot and the
15282        // accessor must not clone the backing `Vec` on every call.
15283        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15284        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
15285        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
15286        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
15287        // the sibling outer top-level [`Caixa`] `&[String]`-return
15288        // axes — the accessor's returned slice must borrow from
15289        // `&self` (the returned reference's lifetime is tied to
15290        // `&self`), and calling the accessor twice on the same
15291        // [`Caixa`] must yield slices that are pointer-equal (the
15292        // underlying byte-buffer is the storage `Vec`'s allocation,
15293        // not a fresh copy) as well as value-equal (idempotent, no
15294        // side effects on `&self`).
15295        //
15296        // Pins against a future silent detour that returned an owned
15297        // `Vec<String>` (which would type-check but silently clone on
15298        // every call, breaking the zero-cost projection every peer
15299        // sibling slice accessor carries), a `&Vec<String>` return
15300        // (which would leak the backing `Vec`'s grow/push/reserve
15301        // surface no downstream consumer reaches for), or a one-arm-
15302        // only accessor that returned a saturating value on some
15303        // sentinel input (breaking the pass-through invariant the
15304        // sibling slice accessors carry).
15305        for servicos in [
15306            vec![],
15307            vec!["servicos/demo.computeunit.yaml"],
15308            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15309            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15310        ] {
15311            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15312            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15313            let first = c.servicos();
15314            let second = c.servicos();
15315            assert_eq!(
15316                first, second,
15317                "Caixa::servicos must be idempotent — two successive \
15318                 calls on the same &self must return the same &[String]",
15319            );
15320            assert_eq!(
15321                first.as_ptr(),
15322                second.as_ptr(),
15323                "Caixa::servicos must borrow the underlying \
15324                 Vec<String> storage — two successive calls must \
15325                 return slices with the same backing pointer (a fresh \
15326                 Vec<String> clone would change the pointer on every \
15327                 call)",
15328            );
15329            assert_eq!(
15330                first,
15331                expected.as_slice(),
15332                "Caixa::servicos must return :servicos verbatim by \
15333                 borrow — got {first:?}, expected {expected:?}",
15334            );
15335        }
15336    }
15337
15338    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
15339
15340    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
15341        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15342        c.deps = deps;
15343        c
15344    }
15345
15346    #[test]
15347    fn deps_returns_deps_slice_verbatim_across_permutations() {
15348        // The canonical per-`Caixa` `:deps` universal-axis runtime-
15349        // dependency-declaration-list slice pin: [`Caixa::deps`] must
15350        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
15351        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
15352        // access across every representative value in the accept-set —
15353        // `[]` (the "no runtime deps declared" arm every existing
15354        // fixture without a `:deps` line carries; the
15355        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
15356        // single-entry list (the shape most consumer caixas carry), a
15357        // canonical two-entry list (the multi-dep runtime closure), and
15358        // two past-the-guard sentinels — a `[""]`-`:nome` entry
15359        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15360        // `NomeInvalid` but the accessor must ship the raw slot
15361        // verbatim) and a `[a, a]` duplicate (validate rejects through
15362        // `DuplicateNome { list: ":deps" }` but the accessor must ship
15363        // the raw slot verbatim so struct-literal fixtures continue to
15364        // expose the duplicate at the accessor).
15365        //
15366        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
15367        // pin on the substrate primitive — opens the outer-`Caixa`
15368        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
15369        // future lift closes on. Peer of the closed outer-`Caixa`
15370        // foreign-code-slot `&[String]` sub-family
15371        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15372        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
15373        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
15374        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
15375        // (`autores_returns_autores_slice_verbatim_across_permutations`
15376        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15377        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
15378        // projection pattern onto a novel element-type axis (`Dep`
15379        // composite vs the prior sibling family's `String` scalar).
15380        // Pins against a future silent detour that returned an owned
15381        // `Vec<Dep>` (which would type-check but silently clone on every
15382        // accessor call, breaking the zero-cost projection every peer
15383        // sibling slice accessor carries), a `[""] → []` collapse (which
15384        // would silently absorb the `NomeEmpty` refusal case at the
15385        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15386        // would silently absorb the `DuplicateNome` refusal case at the
15387        // accessor boundary).
15388        for deps in [
15389            vec![],
15390            vec![Dep::simple("", "^0.1")],
15391            vec![Dep::simple("caixa-teia", "^0.1")],
15392            vec![
15393                Dep::simple("caixa-teia", "^0.1"),
15394                Dep::simple("caixa-core", "^0.1"),
15395            ],
15396            vec![
15397                Dep::simple("caixa-teia", "^0.1"),
15398                Dep::simple("caixa-teia", "^0.2"),
15399            ],
15400        ] {
15401            let c = caixa_with_deps(deps.clone());
15402            assert_eq!(
15403                c.deps(),
15404                deps.as_slice(),
15405                "Caixa::deps must return :deps verbatim (got {:?}, \
15406                 expected {deps:?})",
15407                c.deps(),
15408            );
15409            assert_eq!(
15410                c.deps(),
15411                c.deps.as_slice(),
15412                "Caixa::deps must element-equal the raw \
15413                 `self.deps.as_slice()` field access across every \
15414                 value in the Vec<Dep> accept-set",
15415            );
15416        }
15417    }
15418
15419    #[test]
15420    fn validate_deps_duplicate_arm_routes_through_accessor() {
15421        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
15422        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
15423        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
15424        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
15425        // "^0.2")], .. }` must surface the `DuplicateNome { list:
15426        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
15427        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
15428        // form) must pass validate. The pair jointly pins the accessor +
15429        // validate-gate composition: any future silent detour that had
15430        // the accessor return a dedupped slice on the `[a, a]` arm (a
15431        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15432        // would silently absorb the `DuplicateNome` refusal at the
15433        // accessor boundary and the validate gate would accept a
15434        // struct-literal `Caixa` carrying the drift — the composition
15435        // pin catches that at caixa-core build time.
15436        //
15437        // Peer of the per-`Caixa`
15438        // `validate_autores_empty_entry_arm_routes_through_accessor`
15439        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15440        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15441        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15442        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
15443        // (611f78b) accessor-composition pins on the sibling `&[T]`-
15444        // composition axes — same "the validate gate must route through
15445        // the substrate-primitive typed dispatch" discipline extended
15446        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
15447        // composition surface, opening the outer-`Caixa` dependency-slot
15448        // arm of the composition-pin family.
15449        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15450        let err = c.validate_deps().unwrap_err();
15451        assert!(
15452            matches!(
15453                err,
15454                DepError::DuplicateNome { ref nome, list } if nome == "d"
15455                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
15456            ),
15457            "validate_deps must reject deps == \
15458             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15459             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
15460             accessor and the validate gate must route through the \
15461             same substrate-primitive typed dispatch on the :deps \
15462             within-list duplicate arm (got {err:?})",
15463        );
15464        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
15465        assert!(
15466            c.validate_deps().is_ok(),
15467            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
15468             (the canonical single-entry form)",
15469        );
15470    }
15471
15472    #[test]
15473    fn deps_projects_slice_by_borrow() {
15474        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
15475        // — the returned slice borrows the underlying `Vec<Dep>` storage
15476        // of the `:deps` slot and the accessor must not clone the
15477        // backing `Vec` on every call. Peer of the per-`Caixa`
15478        // `autores_projects_slice_by_borrow` (b5d813f),
15479        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15480        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15481        // `exe_projects_slice_by_borrow` (65d9527), and
15482        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15483        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15484        // axes — the accessor's returned slice must borrow from `&self`
15485        // (the returned reference's lifetime is tied to `&self`), and
15486        // calling the accessor twice on the same [`Caixa`] must yield
15487        // slices that are pointer-equal (the underlying byte-buffer is
15488        // the storage `Vec`'s allocation, not a fresh copy) as well as
15489        // value-equal (idempotent, no side effects on `&self`).
15490        //
15491        // Pins against a future silent detour that returned an owned
15492        // `Vec<Dep>` (which would type-check but silently clone on
15493        // every call), a `&Vec<Dep>` return (which would leak the
15494        // backing `Vec`'s grow/push/reserve surface no downstream
15495        // consumer reaches for), or a one-arm-only accessor that
15496        // returned a saturating value on some sentinel input.
15497        for deps in [
15498            vec![],
15499            vec![Dep::simple("caixa-teia", "^0.1")],
15500            vec![
15501                Dep::simple("caixa-teia", "^0.1"),
15502                Dep::simple("caixa-core", "^0.1"),
15503            ],
15504        ] {
15505            let c = caixa_with_deps(deps.clone());
15506            let first = c.deps();
15507            let second = c.deps();
15508            assert_eq!(
15509                first, second,
15510                "Caixa::deps must be idempotent — two successive calls \
15511                 on the same &self must return the same &[Dep]",
15512            );
15513            assert_eq!(
15514                first.as_ptr(),
15515                second.as_ptr(),
15516                "Caixa::deps must borrow the underlying Vec<Dep> \
15517                 storage — two successive calls must return slices \
15518                 with the same backing pointer (a fresh Vec<Dep> clone \
15519                 would change the pointer on every call)",
15520            );
15521            assert_eq!(
15522                first,
15523                deps.as_slice(),
15524                "Caixa::deps must return :deps verbatim by borrow — \
15525                 got {first:?}, expected {deps:?}",
15526            );
15527        }
15528    }
15529
15530    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
15531
15532    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
15533        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15534        c.deps_dev = deps_dev;
15535        c
15536    }
15537
15538    #[test]
15539    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
15540        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
15541        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
15542        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
15543        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
15544        // access across every representative value in the accept-set —
15545        // `[]` (the "no dev deps declared" arm every existing fixture
15546        // without a `:deps-dev` line carries; the [`Caixa::template`]
15547        // scaffold emits `:deps-dev ()`), a canonical single-entry list
15548        // (the shape most consumer caixas carry — a `tatara-check` dev
15549        // pin), a canonical two-entry list (the multi-dev-dep closure),
15550        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
15551        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15552        // `NomeInvalid` but the accessor must ship the raw slot
15553        // verbatim) and a `[a, a]` duplicate (validate rejects through
15554        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
15555        // ship the raw slot verbatim so struct-literal fixtures continue
15556        // to expose the duplicate at the accessor).
15557        //
15558        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
15559        // pin on the substrate primitive — closes the outer-`Caixa`
15560        // dependency-slot `&[Dep]` sub-family the sibling
15561        // `deps_returns_deps_slice_verbatim_across_permutations`
15562        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
15563        // slice" projection pattern onto the sibling dev-dep axis —
15564        // pins against a future silent detour that returned an owned
15565        // `Vec<Dep>` (which would type-check but silently clone on every
15566        // accessor call, breaking the zero-cost projection every peer
15567        // sibling slice accessor carries), a `[""] → []` collapse (which
15568        // would silently absorb the `NomeEmpty` refusal case at the
15569        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15570        // would silently absorb the `DuplicateNome` refusal case at the
15571        // accessor boundary).
15572        for deps_dev in [
15573            vec![],
15574            vec![Dep::simple("", "^0.1")],
15575            vec![Dep::simple("tatara-check", "^0.1")],
15576            vec![
15577                Dep::simple("tatara-check", "^0.1"),
15578                Dep::simple("caixa-lint", "^0.1"),
15579            ],
15580            vec![
15581                Dep::simple("tatara-check", "^0.1"),
15582                Dep::simple("tatara-check", "^0.2"),
15583            ],
15584        ] {
15585            let c = caixa_with_deps_dev(deps_dev.clone());
15586            assert_eq!(
15587                c.deps_dev(),
15588                deps_dev.as_slice(),
15589                "Caixa::deps_dev must return :deps-dev verbatim (got \
15590                 {:?}, expected {deps_dev:?})",
15591                c.deps_dev(),
15592            );
15593            assert_eq!(
15594                c.deps_dev(),
15595                c.deps_dev.as_slice(),
15596                "Caixa::deps_dev must element-equal the raw \
15597                 `self.deps_dev.as_slice()` field access across every \
15598                 value in the Vec<Dep> accept-set",
15599            );
15600        }
15601    }
15602
15603    #[test]
15604    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
15605        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
15606        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
15607        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
15608        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
15609        // Dep::simple("d", "^0.2")], .. }` must surface the
15610        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
15611        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
15612        // canonical single-entry form) must pass validate. The pair
15613        // jointly pins the accessor + validate-gate composition: any
15614        // future silent detour that had the accessor return a dedupped
15615        // slice on the `[a, a]` arm (a
15616        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15617        // would silently absorb the `DuplicateNome` refusal at the
15618        // accessor boundary and the validate gate would accept a
15619        // struct-literal `Caixa` carrying the drift — the composition
15620        // pin catches that at caixa-core build time.
15621        //
15622        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
15623        // (ad34b4e) on the sibling `:deps` axis — same "the validate
15624        // gate must route through the substrate-primitive typed
15625        // dispatch" discipline folded onto the sibling `:deps-dev`
15626        // axis, closing the two-list dep-graph composition-pin family.
15627        // The `:deps-dev` diagnostic must carry the
15628        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
15629        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
15630        // offending list unambiguously.
15631        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15632        let err = c.validate_deps().unwrap_err();
15633        assert!(
15634            matches!(
15635                err,
15636                DepError::DuplicateNome { ref nome, list } if nome == "d"
15637                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
15638            ),
15639            "validate_deps must reject deps_dev == \
15640             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15641             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
15642             accessor and the validate gate must route through the \
15643             same substrate-primitive typed dispatch on the :deps-dev \
15644             within-list duplicate arm (got {err:?})",
15645        );
15646        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
15647        assert!(
15648            c.validate_deps().is_ok(),
15649            "validate_deps must accept deps_dev == \
15650             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
15651        );
15652    }
15653
15654    #[test]
15655    fn deps_dev_projects_slice_by_borrow() {
15656        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
15657        // borrow — the returned slice borrows the underlying `Vec<Dep>`
15658        // storage of the `:deps-dev` slot and the accessor must not
15659        // clone the backing `Vec` on every call. Peer of
15660        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
15661        // `:deps` axis, and of the per-`Caixa`
15662        // `autores_projects_slice_by_borrow` (b5d813f),
15663        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15664        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15665        // `exe_projects_slice_by_borrow` (65d9527), and
15666        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15667        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15668        // axes — the accessor's returned slice must borrow from `&self`
15669        // (the returned reference's lifetime is tied to `&self`), and
15670        // calling the accessor twice on the same [`Caixa`] must yield
15671        // slices that are pointer-equal (the underlying byte-buffer is
15672        // the storage `Vec`'s allocation, not a fresh copy) as well as
15673        // value-equal (idempotent, no side effects on `&self`).
15674        //
15675        // Pins against a future silent detour that returned an owned
15676        // `Vec<Dep>` (which would type-check but silently clone on
15677        // every call), a `&Vec<Dep>` return (which would leak the
15678        // backing `Vec`'s grow/push/reserve surface no downstream
15679        // consumer reaches for), or a one-arm-only accessor that
15680        // returned a saturating value on some sentinel input.
15681        for deps_dev in [
15682            vec![],
15683            vec![Dep::simple("tatara-check", "^0.1")],
15684            vec![
15685                Dep::simple("tatara-check", "^0.1"),
15686                Dep::simple("caixa-lint", "^0.1"),
15687            ],
15688        ] {
15689            let c = caixa_with_deps_dev(deps_dev.clone());
15690            let first = c.deps_dev();
15691            let second = c.deps_dev();
15692            assert_eq!(
15693                first, second,
15694                "Caixa::deps_dev must be idempotent — two successive \
15695                 calls on the same &self must return the same &[Dep]",
15696            );
15697            assert_eq!(
15698                first.as_ptr(),
15699                second.as_ptr(),
15700                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
15701                 storage — two successive calls must return slices \
15702                 with the same backing pointer (a fresh Vec<Dep> clone \
15703                 would change the pointer on every call)",
15704            );
15705            assert_eq!(
15706                first,
15707                deps_dev.as_slice(),
15708                "Caixa::deps_dev must return :deps-dev verbatim by \
15709                 borrow — got {first:?}, expected {deps_dev:?}",
15710            );
15711        }
15712    }
15713
15714    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
15715
15716    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
15717        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15718        c.limits = limits;
15719        c
15720    }
15721
15722    #[test]
15723    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
15724        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
15725        // composite optional-composite-reference-shape pin:
15726        // [`Caixa::limits`] must return the `:limits` typed
15727        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
15728        // reference over the same backing storage the raw
15729        // `self.limits.as_ref()` field access borrows from, byte-equal
15730        // across every representative fixture in the accept-set — the
15731        // author-omitted `None` shape (the "engine-default applies"
15732        // partition every downstream Servico M2 overlay emitter treats
15733        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
15734        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
15735        // per-axis cap is `None`, so the peer M2 overlay emitter's
15736        // `.is_empty()`-gated projection still emits nothing but the
15737        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
15738        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
15739        // fixture (only `:memory` set — the canonical shape most
15740        // memory-heavy Servicos carry), and a fully-populated composite
15741        // (every per-axis cap set — the canonical shape a
15742        // sandboxed-by-default Servico carries).
15743        //
15744        // Pins against a future silent detour that returned a fresh-
15745        // cloned [`LimitsSpec`] copy (which would type-check via the
15746        // `Clone` impl but silently break every downstream caller that
15747        // relied on the reference sharing the composite's backing
15748        // identity), a reference to an operator-resolved overlay (the
15749        // future per-cluster `:limits-overrides` slot — its resolution
15750        // must land at exactly this accessor body, not silently divert
15751        // the raw slot away from a second consumer), a
15752        // `None` → `Some(LimitsSpec::default)` cluster-default
15753        // projection (which would collapse the load-bearing
15754        // "author-omitted `:limits` ⇒ engine-default applies" partition
15755        // the peer [`crate::render::servico_m2_overlay`] emitter and
15756        // the peer [`Caixa::declared_servico_slots`] enumerator both
15757        // read), or an axis-shuffled projection (a future detour that
15758        // swapped `memory` and `fuel` through the accessor would
15759        // silently split the paired [`crate::StandardLayout::verify`]
15760        // per-`:limits` shape gate's traversal input from the peer
15761        // `servico_m2_overlay` emitter's projection input).
15762        //
15763        // First outer top-level [`Caixa`] `Option<&Composite>`-return
15764        // composite-reference accessor pin on the substrate primitive
15765        // — opens the outer-`Caixa` `Option<&Composite>` composite-
15766        // reference projection pattern the sibling `:behavior`
15767        // [`crate::BehaviorSpec`] / `:politicas`
15768        // [`crate::aplicacao::MeshPolicy`] / `:placement`
15769        // [`crate::aplicacao::Placement`] / `:entrada`
15770        // [`crate::aplicacao::Entrada`] future outer-composite lifts
15771        // fold on. Peer of the closed M3 outer-composite family the
15772        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
15773        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
15774        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
15775        // reference accessor pins already carry on the outer
15776        // [`crate::AplicacaoSpec`] altitude — extends the outer-
15777        // accessor byte-equal-projection discipline onto the outer
15778        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
15779        use crate::LimitsSpec;
15780        use std::time::Duration;
15781        let fixtures: Vec<Option<LimitsSpec>> = vec![
15782            None,
15783            Some(LimitsSpec::default()),
15784            Some(LimitsSpec {
15785                memory: Some(64 * 1024 * 1024),
15786                ..Default::default()
15787            }),
15788            Some(LimitsSpec {
15789                memory: Some(64 * 1024 * 1024),
15790                fuel: Some(1_000_000),
15791                wall_clock: Some(Duration::from_secs(30)),
15792                cpu: Some(500),
15793            }),
15794        ];
15795        for limits in fixtures {
15796            let c = caixa_with_limits(limits.clone());
15797            assert_eq!(
15798                c.limits(),
15799                limits.as_ref(),
15800                "Caixa::limits must return :limits verbatim (got {:?}, \
15801                 expected {:?})",
15802                c.limits(),
15803                limits.as_ref(),
15804            );
15805            match (c.limits(), c.limits.as_ref()) {
15806                (Some(a), Some(b)) => assert!(
15807                    std::ptr::eq(a, b),
15808                    "Caixa::limits accessor and self.limits.as_ref() \
15809                     field access must borrow the same backing storage \
15810                     — the accessor is the substrate-primitive typed \
15811                     dispatch every downstream Servico-M2-overlay \
15812                     composite consumer must route through, and a \
15813                     reference-identity split would silently break \
15814                     every consumer that relied on the borrow sharing \
15815                     the composite's storage",
15816                ),
15817                (None, None) => {}
15818                _ => panic!(
15819                    "Caixa::limits presence bit must byte-equal \
15820                     self.limits.is_some() — a presence-bit drift would \
15821                     silently split the paired StandardLayout::verify \
15822                     per-`:limits` shape gate's traversal head from \
15823                     the peer render::servico_m2_overlay M2 overlay \
15824                     emitter's traversal head from the peer \
15825                     Caixa::declared_servico_slots M2 declared-slot \
15826                     enumerator's presence probe",
15827                ),
15828            }
15829            assert_eq!(
15830                c.limits().is_some(),
15831                c.limits.is_some(),
15832                "Caixa::limits().is_some() must byte-equal \
15833                 self.limits.is_some() — a presence-bit drift would \
15834                 silently split every downstream Option<&LimitsSpec> \
15835                 consumer's partition on the engine-default arm",
15836            );
15837        }
15838    }
15839
15840    #[test]
15841    fn declared_servico_slots_limits_arm_routes_through_accessor() {
15842        // Composition pin: [`Caixa::declared_servico_slots`]'s
15843        // `:limits` presence-probe arm must key off [`Caixa::limits`],
15844        // not the raw `self.limits.is_some()` field-probe. Structurally:
15845        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
15846        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
15847        // (the presence bit is `Some`, so the M2 kind-coherence gate
15848        // must surface the slot as "declared" even when every per-axis
15849        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
15850        // push the label (the "author omitted the slot entirely"
15851        // partition). The pair jointly pins the accessor + declared-
15852        // slot enumerator composition: any future silent detour that
15853        // had the accessor collapse `Some(LimitsSpec::default())` to
15854        // `None` (a `.filter(|l| !l.is_empty())` projection) would
15855        // silently absorb the "declared but empty" arm at the
15856        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
15857        // kind-coherence gate would silently accept a
15858        // struct-literal `Caixa` carrying the drift.
15859        //
15860        // Peer of the sibling per-`Caixa`
15861        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
15862        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
15863        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
15864        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
15865        // enumerator gate must route through the substrate-primitive
15866        // typed dispatch" discipline extended onto the outer top-level
15867        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
15868        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
15869        // composition-pin family.
15870        use crate::LimitsSpec;
15871        let c = caixa_with_limits(Some(LimitsSpec::default()));
15872        let slots = c.declared_servico_slots();
15873        assert!(
15874            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
15875            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
15876             when `:limits` is Some (even for LimitsSpec::default()) \
15877             — the accessor and the enumerator gate must route through \
15878             the same substrate-primitive typed dispatch on the outer \
15879             :limits presence bit (got slots={slots:?})",
15880        );
15881        let c = caixa_with_limits(None);
15882        let slots = c.declared_servico_slots();
15883        assert!(
15884            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
15885            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
15886             when `:limits` is None — the author-omitted arm must \
15887             route through the accessor's None-return unchanged (got \
15888             slots={slots:?})",
15889        );
15890    }
15891
15892    #[test]
15893    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
15894        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15895        // per-`:limits` M2 overlay emit arm must key off
15896        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
15897        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
15898        // Some(64 MiB), .. default }), .. }` must surface the
15899        // `M2_KEY_LIMITS` key with the per-axis
15900        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
15901        // limits: Some(LimitsSpec::default()), .. }` must omit the
15902        // key entirely (the `.is_empty()`-gated inner arm elides an
15903        // empty composite even when the outer presence bit is `Some`),
15904        // and a `Caixa { limits: None, .. }` must also omit the key
15905        // (the "author omitted the slot entirely" partition). The
15906        // three-fixture family jointly pins the accessor + M2 overlay
15907        // emitter composition: any future silent detour that had the
15908        // accessor return a fresh-cloned copy on the `Some` arm (a
15909        // `LimitsSpec::clone()` projection) would silently break the
15910        // reference-identity pin the peer per-axis
15911        // `serde_yaml::to_value(limits)` projection reads from.
15912        use crate::LimitsSpec;
15913        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
15914        let c = caixa_with_limits(Some(LimitsSpec {
15915            memory: Some(64 * 1024 * 1024),
15916            ..Default::default()
15917        }));
15918        let overlay = servico_m2_overlay(&c).unwrap();
15919        assert!(
15920            overlay.contains_key(M2_KEY_LIMITS),
15921            "servico_m2_overlay must surface M2_KEY_LIMITS when \
15922             `:limits` carries a non-empty composite — the accessor \
15923             and the M2 overlay emitter must route through the same \
15924             substrate-primitive typed dispatch on the outer :limits \
15925             composite (got overlay={overlay:?})",
15926        );
15927        let c = caixa_with_limits(Some(LimitsSpec::default()));
15928        let overlay = servico_m2_overlay(&c).unwrap();
15929        assert!(
15930            !overlay.contains_key(M2_KEY_LIMITS),
15931            "servico_m2_overlay must omit M2_KEY_LIMITS when \
15932             `:limits` is Some(LimitsSpec::default()) — the empty \
15933             composite's `.is_empty()`-gated inner arm must elide \
15934             the key regardless of the outer presence bit (got \
15935             overlay={overlay:?})",
15936        );
15937        let c = caixa_with_limits(None);
15938        let overlay = servico_m2_overlay(&c).unwrap();
15939        assert!(
15940            !overlay.contains_key(M2_KEY_LIMITS),
15941            "servico_m2_overlay must omit M2_KEY_LIMITS when \
15942             `:limits` is None — the author-omitted arm must route \
15943             through the accessor's None-return unchanged (got \
15944             overlay={overlay:?})",
15945        );
15946    }
15947
15948    #[test]
15949    fn limits_projects_option_ref_by_borrow() {
15950        // The by-borrow pin: [`Caixa::limits`] returns
15951        // `Option<&LimitsSpec>` by borrow — the returned reference
15952        // borrows the underlying `Option<LimitsSpec>` storage of the
15953        // `:limits` slot and the accessor must not clone the backing
15954        // composite on every call. Peer of the sibling
15955        // `deps_projects_slice_by_borrow` (ad34b4e) /
15956        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
15957        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
15958        // extended here to the outer [`Caixa`] `Option<&Composite>`-
15959        // return axis: the accessor's returned reference must borrow
15960        // from `&self` (the returned reference's lifetime is tied to
15961        // `&self`), and calling the accessor twice on the same
15962        // [`Caixa`] must yield references that are pointer-equal (the
15963        // underlying byte-buffer is the storage `LimitsSpec`'s
15964        // allocation, not a fresh copy) as well as value-equal
15965        // (idempotent, no side effects on `&self`).
15966        //
15967        // Pins against a future silent detour that returned an owned
15968        // `LimitsSpec` (which would type-check via the `Clone` impl
15969        // but silently clone on every call), a `&LimitsSpec` panic-
15970        // return on the `None` arm (which would collapse the load-
15971        // bearing `Option` presence-bit into a runtime panic), or a
15972        // one-arm-only accessor that returned a saturating composite
15973        // on some sentinel input.
15974        use crate::LimitsSpec;
15975        use std::time::Duration;
15976        for limits in [
15977            Some(LimitsSpec::default()),
15978            Some(LimitsSpec {
15979                memory: Some(64 * 1024 * 1024),
15980                fuel: Some(1_000_000),
15981                wall_clock: Some(Duration::from_secs(30)),
15982                cpu: Some(500),
15983            }),
15984        ] {
15985            let c = caixa_with_limits(limits.clone());
15986            let first = c.limits().unwrap();
15987            let second = c.limits().unwrap();
15988            assert_eq!(
15989                first, second,
15990                "Caixa::limits must be idempotent — two successive \
15991                 calls on the same &self must return the same \
15992                 &LimitsSpec",
15993            );
15994            assert!(
15995                std::ptr::eq(first, second),
15996                "Caixa::limits must borrow the underlying \
15997                 Option<LimitsSpec> storage — two successive calls \
15998                 must return references with the same backing pointer \
15999                 (a fresh LimitsSpec clone would change the pointer \
16000                 on every call)",
16001            );
16002            assert_eq!(
16003                Some(first),
16004                limits.as_ref(),
16005                "Caixa::limits must return :limits verbatim by borrow \
16006                 — got {first:?}, expected {:?}",
16007                limits.as_ref(),
16008            );
16009        }
16010        let c = caixa_with_limits(None);
16011        assert!(
16012            c.limits().is_none(),
16013            "Caixa::limits must return None when :limits is absent — \
16014             the author-omitted arm must project through the \
16015             accessor's Option::None unchanged",
16016        );
16017    }
16018
16019    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
16020
16021    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
16022        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16023        c.behavior = behavior;
16024        c
16025    }
16026
16027    #[test]
16028    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
16029        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
16030        // composite optional-composite-reference-shape pin:
16031        // [`Caixa::behavior`] must return the `:behavior` typed
16032        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
16033        // reference over the same backing storage the raw
16034        // `self.behavior.as_ref()` field access borrows from, byte-equal
16035        // across every representative fixture in the accept-set — the
16036        // author-omitted `None` shape (the "runtime-default applies"
16037        // partition every downstream Servico M2 overlay emitter treats
16038        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
16039        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
16040        // every per-callback path is `None`, so the peer M2 overlay
16041        // emitter's `.is_empty()`-gated projection still emits nothing
16042        // but the outer presence-bit is `Some`, so
16043        // [`Caixa::declared_servico_slots`] still pushes the
16044        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
16045        // (only `:on-state-change` set — the canonical shape a caixa
16046        // that only wires the hot-upgrade migration path carries), and
16047        // a fully-populated composite (every per-callback path set —
16048        // the canonical shape a fully-instrumented gen_server-shaped
16049        // Servico carries).
16050        //
16051        // Peer of the sibling
16052        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16053        // (b2bd9d7) opening fixture-family + reference-identity +
16054        // presence-bit tetrad pin on the outer top-level [`Caixa`]
16055        // `Option<&Composite>`-return sub-family — extended here to the
16056        // second axis of that sub-family so both of the currently-lifted
16057        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
16058        // `:behavior`) carry the same "byte-equal, borrow-shared,
16059        // presence-bit-preserved" outer-accessor discipline.
16060        //
16061        // Pins against a future silent detour that returned a fresh-
16062        // cloned [`crate::BehaviorSpec`] copy (which would type-check
16063        // via the `Clone` impl but silently break every downstream
16064        // caller that relied on the reference sharing the composite's
16065        // backing identity), a reference to an operator-resolved
16066        // overlay (a future per-cluster `:behavior-overrides` slot —
16067        // its resolution must land at exactly this accessor body, not
16068        // silently divert the raw slot away from a second consumer), a
16069        // `None` → `Some(BehaviorSpec::default)` cluster-default
16070        // projection (which would collapse the load-bearing
16071        // "author-omitted `:behavior` ⇒ runtime-default applies"
16072        // partition the peer [`crate::render::servico_m2_overlay`]
16073        // emitter, the peer [`Caixa::declared_servico_slots`]
16074        // enumerator, and the cross-slot
16075        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
16076        // gate all read), or a callback-shuffled projection (a future
16077        // detour that swapped `on_init` and `on_terminate` through the
16078        // accessor would silently split the paired
16079        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
16080        // traversal input from the peer `servico_m2_overlay` emitter's
16081        // projection input from the cross-slot `:state-change`
16082        // composition gate's traversal input).
16083        use crate::BehaviorSpec;
16084        use std::path::PathBuf;
16085        let fixtures: Vec<Option<BehaviorSpec>> = vec![
16086            None,
16087            Some(BehaviorSpec::default()),
16088            Some(BehaviorSpec {
16089                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16090                ..Default::default()
16091            }),
16092            Some(BehaviorSpec {
16093                on_init: Some(PathBuf::from("lib/init.lisp")),
16094                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16095                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16096                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16097                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16098                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16099            }),
16100        ];
16101        for behavior in fixtures {
16102            let c = caixa_with_behavior(behavior.clone());
16103            assert_eq!(
16104                c.behavior(),
16105                behavior.as_ref(),
16106                "Caixa::behavior must return :behavior verbatim (got \
16107                 {:?}, expected {:?})",
16108                c.behavior(),
16109                behavior.as_ref(),
16110            );
16111            match (c.behavior(), c.behavior.as_ref()) {
16112                (Some(a), Some(b)) => assert!(
16113                    std::ptr::eq(a, b),
16114                    "Caixa::behavior accessor and self.behavior.as_ref() \
16115                     field access must borrow the same backing storage \
16116                     — the accessor is the substrate-primitive typed \
16117                     dispatch every downstream Servico-M2-overlay \
16118                     composite consumer must route through, and a \
16119                     reference-identity split would silently break \
16120                     every consumer that relied on the borrow sharing \
16121                     the composite's storage",
16122                ),
16123                (None, None) => {}
16124                _ => panic!(
16125                    "Caixa::behavior presence bit must byte-equal \
16126                     self.behavior.is_some() — a presence-bit drift \
16127                     would silently split the paired \
16128                     StandardLayout::verify per-`:behavior` shape \
16129                     gate's traversal head from the peer \
16130                     render::servico_m2_overlay M2 overlay emitter's \
16131                     traversal head from the cross-slot \
16132                     validate_upgrade_from_against_behavior \
16133                     composition gate's traversal head from the peer \
16134                     Caixa::declared_servico_slots M2 declared-slot \
16135                     enumerator's presence probe",
16136                ),
16137            }
16138            assert_eq!(
16139                c.behavior().is_some(),
16140                c.behavior.is_some(),
16141                "Caixa::behavior().is_some() must byte-equal \
16142                 self.behavior.is_some() — a presence-bit drift would \
16143                 silently split every downstream Option<&BehaviorSpec> \
16144                 consumer's partition on the runtime-default arm",
16145            );
16146        }
16147    }
16148
16149    #[test]
16150    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
16151        // Composition pin: [`Caixa::declared_servico_slots`]'s
16152        // `:behavior` presence-probe arm must key off
16153        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
16154        // field-probe. Structurally: a `Caixa { behavior:
16155        // Some(BehaviorSpec::default()), .. }` must still push
16156        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
16157        // presence bit is `Some`, so the M2 kind-coherence gate must
16158        // surface the slot as "declared" even when every per-callback
16159        // path is unset), and a `Caixa { behavior: None, .. }` must
16160        // NOT push the label (the "author omitted the slot entirely"
16161        // partition). The pair jointly pins the accessor + declared-
16162        // slot enumerator composition: any future silent detour that
16163        // had the accessor collapse `Some(BehaviorSpec::default())`
16164        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
16165        // silently absorb the "declared but empty" arm at the
16166        // accessor boundary and the
16167        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
16168        // kind-coherence gate would silently accept a struct-literal
16169        // `Caixa` carrying the drift.
16170        //
16171        // Peer of the sibling
16172        // `declared_servico_slots_limits_arm_routes_through_accessor`
16173        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16174        // `Option<&LimitsSpec>` arm of the same
16175        // [`Caixa::declared_servico_slots`] M2 declared-slot
16176        // enumerator's traversal — same "the enumerator gate must
16177        // route through the substrate-primitive typed dispatch"
16178        // discipline extended onto the outer top-level [`Caixa`]
16179        // `Option<&BehaviorSpec>`-composition surface.
16180        use crate::BehaviorSpec;
16181        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16182        let slots = c.declared_servico_slots();
16183        assert!(
16184            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16185            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
16186             when `:behavior` is Some (even for BehaviorSpec::default()) \
16187             — the accessor and the enumerator gate must route through \
16188             the same substrate-primitive typed dispatch on the outer \
16189             :behavior presence bit (got slots={slots:?})",
16190        );
16191        let c = caixa_with_behavior(None);
16192        let slots = c.declared_servico_slots();
16193        assert!(
16194            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16195            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
16196             when `:behavior` is None — the author-omitted arm must \
16197             route through the accessor's None-return unchanged (got \
16198             slots={slots:?})",
16199        );
16200    }
16201
16202    #[test]
16203    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
16204        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16205        // per-`:behavior` M2 overlay emit arm must key off
16206        // [`Caixa::behavior`], not the raw `&caixa.behavior`
16207        // field-borrow. Structurally: a `Caixa { behavior:
16208        // Some(BehaviorSpec { on_state_change: Some(...), .. default
16209        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
16210        // per-callback `onStateChange` sub-mapping in the overlay, a
16211        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
16212        // must omit the key entirely (the `.is_empty()`-gated inner
16213        // arm elides an empty composite even when the outer presence
16214        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
16215        // also omit the key (the "author omitted the slot entirely"
16216        // partition). The three-fixture family jointly pins the
16217        // accessor + M2 overlay emitter composition: any future
16218        // silent detour that had the accessor return a fresh-cloned
16219        // copy on the `Some` arm (a `BehaviorSpec::clone()`
16220        // projection) would silently break the reference-identity
16221        // pin the peer per-callback `serde_yaml::to_value(behavior)`
16222        // projection reads from.
16223        //
16224        // Peer of the sibling
16225        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16226        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16227        // `Option<&LimitsSpec>` arm of the same
16228        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
16229        // traversal — same "the emitter must route through the
16230        // substrate-primitive typed dispatch on the outer composite"
16231        // discipline extended onto the outer top-level [`Caixa`]
16232        // `Option<&BehaviorSpec>`-composition surface.
16233        use crate::BehaviorSpec;
16234        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
16235        use std::path::PathBuf;
16236        let c = caixa_with_behavior(Some(BehaviorSpec {
16237            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16238            ..Default::default()
16239        }));
16240        let overlay = servico_m2_overlay(&c).unwrap();
16241        assert!(
16242            overlay.contains_key(M2_KEY_BEHAVIOR),
16243            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
16244             `:behavior` carries a non-empty composite — the accessor \
16245             and the M2 overlay emitter must route through the same \
16246             substrate-primitive typed dispatch on the outer :behavior \
16247             composite (got overlay={overlay:?})",
16248        );
16249        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16250        let overlay = servico_m2_overlay(&c).unwrap();
16251        assert!(
16252            !overlay.contains_key(M2_KEY_BEHAVIOR),
16253            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16254             `:behavior` is Some(BehaviorSpec::default()) — the empty \
16255             composite's `.is_empty()`-gated inner arm must elide the \
16256             key regardless of the outer presence bit (got \
16257             overlay={overlay:?})",
16258        );
16259        let c = caixa_with_behavior(None);
16260        let overlay = servico_m2_overlay(&c).unwrap();
16261        assert!(
16262            !overlay.contains_key(M2_KEY_BEHAVIOR),
16263            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16264             `:behavior` is None — the author-omitted arm must route \
16265             through the accessor's None-return unchanged (got \
16266             overlay={overlay:?})",
16267        );
16268    }
16269
16270    #[test]
16271    fn behavior_projects_option_ref_by_borrow() {
16272        // The by-borrow pin: [`Caixa::behavior`] returns
16273        // `Option<&BehaviorSpec>` by borrow — the returned reference
16274        // borrows the underlying `Option<BehaviorSpec>` storage of the
16275        // `:behavior` slot and the accessor must not clone the backing
16276        // composite on every call. Peer of the sibling
16277        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
16278        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
16279        // return sub-family — extended here to the second axis of the
16280        // same sub-family: the accessor's returned reference must
16281        // borrow from `&self` (the returned reference's lifetime is
16282        // tied to `&self`), and calling the accessor twice on the same
16283        // [`Caixa`] must yield references that are pointer-equal (the
16284        // underlying byte-buffer is the storage `BehaviorSpec`'s
16285        // allocation, not a fresh copy) as well as value-equal
16286        // (idempotent, no side effects on `&self`).
16287        //
16288        // Pins against a future silent detour that returned an owned
16289        // `BehaviorSpec` (which would type-check via the `Clone` impl
16290        // but silently clone on every call), a `&BehaviorSpec` panic-
16291        // return on the `None` arm (which would collapse the load-
16292        // bearing `Option` presence-bit into a runtime panic), or a
16293        // one-arm-only accessor that returned a saturating composite
16294        // on some sentinel input.
16295        use crate::BehaviorSpec;
16296        use std::path::PathBuf;
16297        for behavior in [
16298            Some(BehaviorSpec::default()),
16299            Some(BehaviorSpec {
16300                on_init: Some(PathBuf::from("lib/init.lisp")),
16301                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16302                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16303                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16304                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16305                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16306            }),
16307        ] {
16308            let c = caixa_with_behavior(behavior.clone());
16309            let first = c.behavior().unwrap();
16310            let second = c.behavior().unwrap();
16311            assert_eq!(
16312                first, second,
16313                "Caixa::behavior must be idempotent — two successive \
16314                 calls on the same &self must return the same \
16315                 &BehaviorSpec",
16316            );
16317            assert!(
16318                std::ptr::eq(first, second),
16319                "Caixa::behavior must borrow the underlying \
16320                 Option<BehaviorSpec> storage — two successive calls \
16321                 must return references with the same backing pointer \
16322                 (a fresh BehaviorSpec clone would change the pointer \
16323                 on every call)",
16324            );
16325            assert_eq!(
16326                Some(first),
16327                behavior.as_ref(),
16328                "Caixa::behavior must return :behavior verbatim by \
16329                 borrow — got {first:?}, expected {:?}",
16330                behavior.as_ref(),
16331            );
16332        }
16333        let c = caixa_with_behavior(None);
16334        assert!(
16335            c.behavior().is_none(),
16336            "Caixa::behavior must return None when :behavior is absent \
16337             — the author-omitted arm must project through the \
16338             accessor's Option::None unchanged",
16339        );
16340    }
16341
16342    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
16343
16344    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
16345        use crate::aplicacao::{Membro, WitContract};
16346        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16347        c.kind = CaixaKind::Aplicacao;
16348        c.membros = vec![Membro {
16349            caixa: "a".into(),
16350            versao: "^0.1".into(),
16351        }];
16352        c.contratos = vec![WitContract {
16353            de: "a".into(),
16354            para: "a".into(),
16355            wit: "wasi:http/proxy".into(),
16356            endpoint: Some("/x".into()),
16357            subject: None,
16358            slot: None,
16359        }];
16360        c.politicas = politicas;
16361        c
16362    }
16363
16364    #[test]
16365    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
16366        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
16367        // composite optional-composite-reference-shape pin:
16368        // [`Caixa::politicas`] must return the `:politicas` typed
16369        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
16370        // reference over the same backing storage the raw
16371        // `self.politicas.as_ref()` field access borrows from,
16372        // byte-equal across every representative fixture in the
16373        // accept-set — the author-omitted `None` shape (the "cluster-
16374        // default applies" partition every downstream mesh-artifact
16375        // emitter treats as "emit no `:politicas` overlay"), the
16376        // empty-composite `Some(MeshPolicy { .. default })` shape
16377        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
16378        // per-axis mesh-policy scalar is `None`, so the peer inner
16379        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
16380        // caixa-mesh overlay elides every per-axis emit but the outer
16381        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
16382        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
16383        // single-axis fixture (only `:timeout` set — the canonical
16384        // shape a latency-sensitive Aplicacao carries), and a
16385        // fully-populated composite (every per-axis mesh-policy
16386        // scalar set — the canonical shape a fully-governed
16387        // Aplicacao carries).
16388        //
16389        // Pins against a future silent detour that returned a fresh-
16390        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
16391        // type-check via the `Clone` impl but silently break every
16392        // downstream caller that relied on the reference sharing the
16393        // composite's backing identity), a reference to an operator-
16394        // resolved overlay (the future per-cluster
16395        // `:politicas-overrides` slot — its resolution must land at
16396        // exactly this accessor body, not silently divert the raw
16397        // slot away from the peer [`Caixa::declared_mesh_slots`]
16398        // enumerator's presence probe), a
16399        // `None` → `Some(MeshPolicy::default)` cluster-default
16400        // projection (which would collapse the load-bearing
16401        // "author-omitted `:politicas` ⇒ cluster-default applies"
16402        // partition the peer [`Caixa::declared_mesh_slots`]
16403        // enumerator and the peer [`Caixa::aplicacao_view`]
16404        // Aplicacao-composition seed both read), or an axis-shuffled
16405        // projection (a future detour that swapped `timeout` and
16406        // `retries` through the accessor would silently split the
16407        // paired [`Caixa::aplicacao_view`] seed's fold input from the
16408        // sibling M3 mesh-artifact emitter's projection input).
16409        //
16410        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
16411        // composite-reference accessor pin on the substrate primitive
16412        // — peer of the sibling
16413        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16414        // (b2bd9d7) and
16415        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16416        // (35d8b52) opening tetrad pins on the outer top-level
16417        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16418        // here to the first of the three M3 mesh-slot axes so the
16419        // opening third of the outer `Option<&Composite>` sub-family
16420        // carries the same "byte-equal, borrow-shared, presence-bit-
16421        // preserved" outer-accessor discipline.
16422        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16423        use std::time::Duration;
16424        let fixtures: Vec<Option<MeshPolicy>> = vec![
16425            None,
16426            Some(MeshPolicy::default()),
16427            Some(MeshPolicy {
16428                timeout: Some(Duration::from_secs(30)),
16429                ..Default::default()
16430            }),
16431            Some(MeshPolicy {
16432                timeout: Some(Duration::from_secs(30)),
16433                retries: Some(3),
16434                circuit_breaker: Some(CircuitBreaker {
16435                    max_failures: 5,
16436                    window: Duration::from_secs(60),
16437                }),
16438                mtls_required: Some(true),
16439                rate_limit: Some(RateLimit {
16440                    rate: 100,
16441                    window: Duration::from_secs(1),
16442                }),
16443            }),
16444        ];
16445        for politicas in fixtures {
16446            let c = caixa_aplicacao_with_politicas(politicas.clone());
16447            assert_eq!(
16448                c.politicas(),
16449                politicas.as_ref(),
16450                "Caixa::politicas must return :politicas verbatim (got \
16451                 {:?}, expected {:?})",
16452                c.politicas(),
16453                politicas.as_ref(),
16454            );
16455            match (c.politicas(), c.politicas.as_ref()) {
16456                (Some(a), Some(b)) => assert!(
16457                    std::ptr::eq(a, b),
16458                    "Caixa::politicas accessor and self.politicas.as_ref() \
16459                     field access must borrow the same backing storage \
16460                     — the accessor is the substrate-primitive typed \
16461                     dispatch every downstream Aplicacao-mesh-overlay \
16462                     composite consumer must route through, and a \
16463                     reference-identity split would silently break \
16464                     every consumer that relied on the borrow sharing \
16465                     the composite's storage",
16466                ),
16467                (None, None) => {}
16468                _ => panic!(
16469                    "Caixa::politicas presence bit must byte-equal \
16470                     self.politicas.is_some() — a presence-bit drift \
16471                     would silently split the paired \
16472                     Caixa::aplicacao_view Aplicacao-composition seed's \
16473                     traversal head from the peer \
16474                     Caixa::declared_mesh_slots M3 declared-slot \
16475                     enumerator's presence probe",
16476                ),
16477            }
16478            assert_eq!(
16479                c.politicas().is_some(),
16480                c.politicas.is_some(),
16481                "Caixa::politicas().is_some() must byte-equal \
16482                 self.politicas.is_some() — a presence-bit drift would \
16483                 silently split every downstream Option<&MeshPolicy> \
16484                 consumer's partition on the cluster-default arm",
16485            );
16486        }
16487    }
16488
16489    #[test]
16490    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
16491        // Composition pin: [`Caixa::declared_mesh_slots`]'s
16492        // `:politicas` presence-probe arm must key off
16493        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
16494        // field-probe. Structurally: a `Caixa { politicas:
16495        // Some(MeshPolicy::default()), .. }` must still push
16496        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
16497        // presence bit is `Some`, so the M3 kind-coherence gate must
16498        // surface the slot as "declared" even when every per-axis
16499        // scalar is unset), and a `Caixa { politicas: None, .. }` must
16500        // NOT push the label (the "author omitted the slot entirely"
16501        // partition). The pair jointly pins the accessor + declared-
16502        // slot enumerator composition: any future silent detour that
16503        // had the accessor collapse `Some(MeshPolicy::default())` to
16504        // `None` (a `.filter(|p| !p.is_empty())` projection) would
16505        // silently absorb the "declared but empty" arm at the
16506        // accessor boundary and the
16507        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16508        // coherence gate would silently accept a struct-literal
16509        // `Caixa` carrying the drift.
16510        //
16511        // Peer of the sibling
16512        // `declared_servico_slots_limits_arm_routes_through_accessor`
16513        // (b2bd9d7) and
16514        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16515        // (35d8b52) composition pins on the sibling `:limits` /
16516        // `:behavior` outer-`Option<&Composite>` arms of the peer
16517        // [`Caixa::declared_servico_slots`] M2 declared-slot
16518        // enumerator's traversal — same "the enumerator gate must
16519        // route through the substrate-primitive typed dispatch"
16520        // discipline extended onto the outer top-level [`Caixa`] M3
16521        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
16522        // enumerator carries the same routing invariant as its M2
16523        // sibling.
16524        use crate::aplicacao::MeshPolicy;
16525        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16526        let slots = c.declared_mesh_slots();
16527        assert!(
16528            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16529            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
16530             when `:politicas` is Some (even for MeshPolicy::default()) \
16531             — the accessor and the enumerator gate must route through \
16532             the same substrate-primitive typed dispatch on the outer \
16533             :politicas presence bit (got slots={slots:?})",
16534        );
16535        let c = caixa_aplicacao_with_politicas(None);
16536        let slots = c.declared_mesh_slots();
16537        assert!(
16538            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16539            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
16540             when `:politicas` is None — the author-omitted arm must \
16541             route through the accessor's None-return unchanged (got \
16542             slots={slots:?})",
16543        );
16544    }
16545
16546    #[test]
16547    fn aplicacao_view_politicas_arm_folds_through_accessor() {
16548        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
16549        // Aplicacao-composition seed must fold through
16550        // [`Caixa::politicas`], not the raw
16551        // `self.politicas.clone().unwrap_or_default()` field-borrow.
16552        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
16553        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
16554        // must surface a projected [`crate::AplicacaoSpec`] whose
16555        // `politicas().timeout()` field byte-equals the outer
16556        // composite's `timeout` scalar (the fold must project the
16557        // authored composite verbatim), a `Caixa { politicas:
16558        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
16559        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
16560        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
16561        // fold's empty-composite arm collapses to the same default the
16562        // author-omitted arm does), and a `Caixa { politicas: None,
16563        // kind: Aplicacao, .. }` must surface an
16564        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
16565        // [`crate::aplicacao::MeshPolicy::default`] (the "author
16566        // omitted the slot entirely" arm folds through the
16567        // `unwrap_or_default` onto the cluster-default). The triad
16568        // jointly pins the accessor + Aplicacao-composition seed
16569        // composition: any future silent detour that had the accessor
16570        // divert the raw slot away from the seed's fold (an operator-
16571        // resolved overlay's default-fold arm silently differing from
16572        // the raw slot's default-fold arm) would silently split the
16573        // build-time mesh-artifact emission gate from the caixa-mesh
16574        // renderer's Aplicacao-view input at the composition boundary.
16575        use crate::aplicacao::MeshPolicy;
16576        use std::time::Duration;
16577        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
16578            timeout: Some(Duration::from_secs(30)),
16579            ..Default::default()
16580        }));
16581        let view = c.aplicacao_view().unwrap();
16582        assert_eq!(
16583            view.politicas().timeout(),
16584            Some(Duration::from_secs(30)),
16585            "Caixa::aplicacao_view must fold the authored :politicas \
16586             :timeout scalar through the accessor verbatim onto the \
16587             projected AplicacaoSpec — a future silent detour at the \
16588             seed's fold arm would surface here as a projected-scalar \
16589             drift (got {:?})",
16590            view.politicas().timeout(),
16591        );
16592        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16593        let view = c.aplicacao_view().unwrap();
16594        assert_eq!(
16595            view.politicas(),
16596            &MeshPolicy::default(),
16597            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
16598             through the accessor onto MeshPolicy::default — the empty- \
16599             composite arm collapses to the same default the author- \
16600             omitted arm does (got {:?})",
16601            view.politicas(),
16602        );
16603        let c = caixa_aplicacao_with_politicas(None);
16604        let view = c.aplicacao_view().unwrap();
16605        assert_eq!(
16606            view.politicas(),
16607            &MeshPolicy::default(),
16608            "Caixa::aplicacao_view must fold None through the accessor's \
16609             unwrap_or_default onto MeshPolicy::default — the author- \
16610             omitted arm must route through the accessor's None-return \
16611             unchanged (got {:?})",
16612            view.politicas(),
16613        );
16614    }
16615
16616    #[test]
16617    fn politicas_projects_option_ref_by_borrow() {
16618        // The by-borrow pin: [`Caixa::politicas`] returns
16619        // `Option<&MeshPolicy>` by borrow — the returned reference
16620        // borrows the underlying `Option<MeshPolicy>` storage of the
16621        // `:politicas` slot and the accessor must not clone the
16622        // backing composite on every call. Peer of the sibling
16623        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
16624        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
16625        // pins on the outer top-level [`Caixa`]
16626        // `Option<&Composite>`-return sub-family — extended here to
16627        // the third axis of the same sub-family: the accessor's
16628        // returned reference must borrow from `&self` (the returned
16629        // reference's lifetime is tied to `&self`), and calling the
16630        // accessor twice on the same [`Caixa`] must yield references
16631        // that are pointer-equal (the underlying byte-buffer is the
16632        // storage `MeshPolicy`'s allocation, not a fresh copy) as
16633        // well as value-equal (idempotent, no side effects on
16634        // `&self`).
16635        //
16636        // Pins against a future silent detour that returned an owned
16637        // `MeshPolicy` (which would type-check via the `Clone` impl
16638        // but silently clone on every call), a `&MeshPolicy` panic-
16639        // return on the `None` arm (which would collapse the load-
16640        // bearing `Option` presence-bit into a runtime panic), or a
16641        // one-arm-only accessor that returned a saturating composite
16642        // on some sentinel input.
16643        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16644        use std::time::Duration;
16645        for politicas in [
16646            Some(MeshPolicy::default()),
16647            Some(MeshPolicy {
16648                timeout: Some(Duration::from_secs(30)),
16649                retries: Some(3),
16650                circuit_breaker: Some(CircuitBreaker {
16651                    max_failures: 5,
16652                    window: Duration::from_secs(60),
16653                }),
16654                mtls_required: Some(true),
16655                rate_limit: Some(RateLimit {
16656                    rate: 100,
16657                    window: Duration::from_secs(1),
16658                }),
16659            }),
16660        ] {
16661            let c = caixa_aplicacao_with_politicas(politicas.clone());
16662            let first = c.politicas().unwrap();
16663            let second = c.politicas().unwrap();
16664            assert_eq!(
16665                first, second,
16666                "Caixa::politicas must be idempotent — two successive \
16667                 calls on the same &self must return the same \
16668                 &MeshPolicy",
16669            );
16670            assert!(
16671                std::ptr::eq(first, second),
16672                "Caixa::politicas must borrow the underlying \
16673                 Option<MeshPolicy> storage — two successive calls \
16674                 must return references with the same backing pointer \
16675                 (a fresh MeshPolicy clone would change the pointer on \
16676                 every call)",
16677            );
16678            assert_eq!(
16679                Some(first),
16680                politicas.as_ref(),
16681                "Caixa::politicas must return :politicas verbatim by \
16682                 borrow — got {first:?}, expected {:?}",
16683                politicas.as_ref(),
16684            );
16685        }
16686        let c = caixa_aplicacao_with_politicas(None);
16687        assert!(
16688            c.politicas().is_none(),
16689            "Caixa::politicas must return None when :politicas is \
16690             absent — the author-omitted arm must project through the \
16691             accessor's Option::None unchanged",
16692        );
16693    }
16694
16695    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
16696
16697    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
16698        use crate::aplicacao::{Membro, WitContract};
16699        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16700        c.kind = CaixaKind::Aplicacao;
16701        c.membros = vec![Membro {
16702            caixa: "a".into(),
16703            versao: "^0.1".into(),
16704        }];
16705        c.contratos = vec![WitContract {
16706            de: "a".into(),
16707            para: "a".into(),
16708            wit: "wasi:http/proxy".into(),
16709            endpoint: Some("/x".into()),
16710            subject: None,
16711            slot: None,
16712        }];
16713        c.placement = placement;
16714        c
16715    }
16716
16717    #[test]
16718    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
16719        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
16720        // composite optional-composite-reference-shape pin:
16721        // [`Caixa::placement`] must return the `:placement` typed
16722        // `Option<Placement>` verbatim as an `Option<&Placement>`
16723        // reference over the same backing storage the raw
16724        // `self.placement.as_ref()` field access borrows from,
16725        // byte-equal across every representative fixture in the
16726        // accept-set — the author-omitted `None` shape (the
16727        // "cluster-default applies" partition every downstream mesh-
16728        // artifact emitter treats as "emit no `:placement` overlay"),
16729        // the empty-composite `Some(Placement { .. default })` shape
16730        // (`estrategia: SingleNode`, empty clusters, no shard-key /
16731        // affinity — the outer presence-bit is `Some` so
16732        // [`Caixa::declared_mesh_slots`] still pushes the
16733        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
16734        // `Replicated`-on-two-clusters fixture (the canonical shape a
16735        // stateless HTTP Aplicacao carries), and a fully-populated
16736        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
16737        // shape a stateful Akka-style cluster-sharding Aplicacao
16738        // carries).
16739        //
16740        // Pins against a future silent detour that returned a fresh-
16741        // cloned [`crate::aplicacao::Placement`] copy (which would
16742        // type-check via the `Clone` impl but silently break every
16743        // downstream caller that relied on the reference sharing the
16744        // composite's backing identity), a reference to an operator-
16745        // resolved overlay (the future per-cluster
16746        // `:placement-overrides` slot — its resolution must land at
16747        // exactly this accessor body, not silently divert the raw
16748        // slot away from the peer [`Caixa::declared_mesh_slots`]
16749        // enumerator's presence probe), a `None` →
16750        // `Some(Placement::default)` cluster-default projection (which
16751        // would collapse the load-bearing "author-omitted `:placement`
16752        // ⇒ cluster-default applies" partition the peer
16753        // [`Caixa::declared_mesh_slots`] enumerator and the peer
16754        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
16755        // read), or an axis-shuffled projection (a future detour that
16756        // swapped `clusters` and `affinity` through the accessor would
16757        // silently split the paired [`Caixa::aplicacao_view`] seed's
16758        // fold input from the sibling M3 mesh-artifact emitter's
16759        // projection input).
16760        //
16761        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
16762        // composite-reference accessor pin on the substrate primitive
16763        // — peer of the sibling
16764        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16765        // (b2bd9d7),
16766        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16767        // (35d8b52), and
16768        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
16769        // (5d23d29) opening triad pins on the outer top-level
16770        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16771        // here to the second of the three M3 mesh-slot axes so the
16772        // opening four-fifths of the outer `Option<&Composite>` sub-
16773        // family carries the same "byte-equal, borrow-shared,
16774        // presence-bit-preserved" outer-accessor discipline.
16775        use crate::aplicacao::{Placement, PlacementStrategy};
16776        let fixtures: Vec<Option<Placement>> = vec![
16777            None,
16778            Some(Placement::default()),
16779            Some(Placement {
16780                estrategia: PlacementStrategy::Replicated,
16781                clusters: vec!["rio".into(), "sao-paulo".into()],
16782                affinity: None,
16783                shard_key: None,
16784            }),
16785            Some(Placement {
16786                estrategia: PlacementStrategy::Sharded,
16787                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
16788                affinity: Some("data-locality".into()),
16789                shard_key: Some("$tenantId".into()),
16790            }),
16791        ];
16792        for placement in fixtures {
16793            let c = caixa_aplicacao_with_placement(placement.clone());
16794            assert_eq!(
16795                c.placement(),
16796                placement.as_ref(),
16797                "Caixa::placement must return :placement verbatim (got \
16798                 {:?}, expected {:?})",
16799                c.placement(),
16800                placement.as_ref(),
16801            );
16802            match (c.placement(), c.placement.as_ref()) {
16803                (Some(a), Some(b)) => assert!(
16804                    std::ptr::eq(a, b),
16805                    "Caixa::placement accessor and self.placement.as_ref() \
16806                     field access must borrow the same backing storage \
16807                     — the accessor is the substrate-primitive typed \
16808                     dispatch every downstream Aplicacao-distribution- \
16809                     overlay composite consumer must route through, and \
16810                     a reference-identity split would silently break \
16811                     every consumer that relied on the borrow sharing \
16812                     the composite's storage",
16813                ),
16814                (None, None) => {}
16815                _ => panic!(
16816                    "Caixa::placement presence bit must byte-equal \
16817                     self.placement.is_some() — a presence-bit drift \
16818                     would silently split the paired \
16819                     Caixa::aplicacao_view Aplicacao-composition seed's \
16820                     traversal head from the peer \
16821                     Caixa::declared_mesh_slots M3 declared-slot \
16822                     enumerator's presence probe",
16823                ),
16824            }
16825            assert_eq!(
16826                c.placement().is_some(),
16827                c.placement.is_some(),
16828                "Caixa::placement().is_some() must byte-equal \
16829                 self.placement.is_some() — a presence-bit drift would \
16830                 silently split every downstream Option<&Placement> \
16831                 consumer's partition on the cluster-default arm",
16832            );
16833        }
16834    }
16835
16836    #[test]
16837    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
16838        // Composition pin: [`Caixa::declared_mesh_slots`]'s
16839        // `:placement` presence-probe arm must key off
16840        // [`Caixa::placement`], not the raw `self.placement.is_some()`
16841        // field-probe. Structurally: a `Caixa { placement:
16842        // Some(Placement::default()), .. }` must still push
16843        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
16844        // presence bit is `Some`, so the M3 kind-coherence gate must
16845        // surface the slot as "declared" even when every per-axis
16846        // scalar defers to the cluster-default arm), and a `Caixa {
16847        // placement: None, .. }` must NOT push the label (the "author
16848        // omitted the slot entirely" partition). The pair jointly pins
16849        // the accessor + declared-slot enumerator composition: any
16850        // future silent detour that had the accessor collapse
16851        // `Some(Placement::default())` to `None` (a `.filter(|p|
16852        // p.clusters().is_empty().not())` projection) would silently
16853        // absorb the "declared but empty" arm at the accessor boundary
16854        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
16855        // kind-coherence gate would silently accept a struct-literal
16856        // `Caixa` carrying the drift.
16857        //
16858        // Peer of the sibling
16859        // `declared_servico_slots_limits_arm_routes_through_accessor`
16860        // (b2bd9d7),
16861        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16862        // (35d8b52), and
16863        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
16864        // (5d23d29) composition pins on the sibling `:limits` /
16865        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
16866        // — same "the enumerator gate must route through the
16867        // substrate-primitive typed dispatch" discipline extended onto
16868        // the second of the three M3 mesh-slot axes so the
16869        // [`Caixa::declared_mesh_slots`] enumerator carries the same
16870        // routing invariant on the `:placement` arm as the peer
16871        // `:politicas` arm.
16872        use crate::aplicacao::Placement;
16873        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
16874        let slots = c.declared_mesh_slots();
16875        assert!(
16876            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
16877            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
16878             when `:placement` is Some (even for Placement::default()) \
16879             — the accessor and the enumerator gate must route through \
16880             the same substrate-primitive typed dispatch on the outer \
16881             :placement presence bit (got slots={slots:?})",
16882        );
16883        let c = caixa_aplicacao_with_placement(None);
16884        let slots = c.declared_mesh_slots();
16885        assert!(
16886            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
16887            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
16888             when `:placement` is None — the author-omitted arm must \
16889             route through the accessor's None-return unchanged (got \
16890             slots={slots:?})",
16891        );
16892    }
16893
16894    #[test]
16895    fn aplicacao_view_placement_arm_folds_through_accessor() {
16896        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
16897        // Aplicacao-composition seed must fold through
16898        // [`Caixa::placement`], not the raw
16899        // `self.placement.clone().unwrap_or_default()` field-borrow.
16900        // Structurally: a `Caixa { placement: Some(Placement {
16901        // estrategia: Replicated, clusters: ["rio"], .. default }),
16902        // kind: Aplicacao, .. }` must surface a projected
16903        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
16904        // `placement().clusters()` byte-equal the outer composite's
16905        // authored values (the fold must project the authored
16906        // composite verbatim), a `Caixa { placement:
16907        // Some(Placement::default()), kind: Aplicacao, .. }` must
16908        // surface an [`crate::AplicacaoSpec`] whose `placement()`
16909        // byte-equals [`crate::aplicacao::Placement::default`] (the
16910        // fold's empty-composite arm collapses to the same default
16911        // the author-omitted arm does), and a `Caixa { placement:
16912        // None, kind: Aplicacao, .. }` must surface an
16913        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
16914        // [`crate::aplicacao::Placement::default`] (the "author
16915        // omitted the slot entirely" arm folds through the
16916        // `unwrap_or_default` onto the cluster-default). The triad
16917        // jointly pins the accessor + Aplicacao-composition seed
16918        // composition: any future silent detour that had the accessor
16919        // divert the raw slot away from the seed's fold (an operator-
16920        // resolved overlay's default-fold arm silently differing from
16921        // the raw slot's default-fold arm) would silently split the
16922        // build-time distribution-artifact emission gate from the
16923        // caixa-mesh renderer's Aplicacao-view input at the
16924        // composition boundary.
16925        use crate::aplicacao::{Placement, PlacementStrategy};
16926        let c = caixa_aplicacao_with_placement(Some(Placement {
16927            estrategia: PlacementStrategy::Replicated,
16928            clusters: vec!["rio".into()],
16929            affinity: None,
16930            shard_key: None,
16931        }));
16932        let view = c.aplicacao_view().unwrap();
16933        assert_eq!(
16934            view.placement().estrategia(),
16935            PlacementStrategy::Replicated,
16936            "Caixa::aplicacao_view must fold the authored :placement \
16937             :estrategia scalar through the accessor verbatim onto the \
16938             projected AplicacaoSpec — a future silent detour at the \
16939             seed's fold arm would surface here as a projected-scalar \
16940             drift (got {:?})",
16941            view.placement().estrategia(),
16942        );
16943        assert_eq!(
16944            view.placement().clusters(),
16945            &["rio"],
16946            "Caixa::aplicacao_view must fold the authored :placement \
16947             :clusters list through the accessor verbatim onto the \
16948             projected AplicacaoSpec — a future silent detour at the \
16949             seed's fold arm would surface here as a projected-list \
16950             drift (got {:?})",
16951            view.placement().clusters(),
16952        );
16953        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
16954        let view = c.aplicacao_view().unwrap();
16955        assert_eq!(
16956            view.placement(),
16957            &Placement::default(),
16958            "Caixa::aplicacao_view must fold Some(Placement::default()) \
16959             through the accessor onto Placement::default — the empty- \
16960             composite arm collapses to the same default the author- \
16961             omitted arm does (got {:?})",
16962            view.placement(),
16963        );
16964        let c = caixa_aplicacao_with_placement(None);
16965        let view = c.aplicacao_view().unwrap();
16966        assert_eq!(
16967            view.placement(),
16968            &Placement::default(),
16969            "Caixa::aplicacao_view must fold None through the accessor's \
16970             unwrap_or_default onto Placement::default — the author- \
16971             omitted arm must route through the accessor's None-return \
16972             unchanged (got {:?})",
16973            view.placement(),
16974        );
16975    }
16976
16977    #[test]
16978    fn placement_projects_option_ref_by_borrow() {
16979        // The by-borrow pin: [`Caixa::placement`] returns
16980        // `Option<&Placement>` by borrow — the returned reference
16981        // borrows the underlying `Option<Placement>` storage of the
16982        // `:placement` slot and the accessor must not clone the
16983        // backing composite on every call. Peer of the sibling
16984        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
16985        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
16986        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
16987        // pins on the outer top-level [`Caixa`]
16988        // `Option<&Composite>`-return sub-family — extended here to
16989        // the fourth axis of the same sub-family: the accessor's
16990        // returned reference must borrow from `&self` (the returned
16991        // reference's lifetime is tied to `&self`), and calling the
16992        // accessor twice on the same [`Caixa`] must yield references
16993        // that are pointer-equal (the underlying byte-buffer is the
16994        // storage `Placement`'s allocation, not a fresh copy) as well
16995        // as value-equal (idempotent, no side effects on `&self`).
16996        //
16997        // Pins against a future silent detour that returned an owned
16998        // `Placement` (which would type-check via the `Clone` impl
16999        // but silently clone on every call), a `&Placement` panic-
17000        // return on the `None` arm (which would collapse the load-
17001        // bearing `Option` presence-bit into a runtime panic), or a
17002        // one-arm-only accessor that returned a saturating composite
17003        // on some sentinel input.
17004        use crate::aplicacao::{Placement, PlacementStrategy};
17005        for placement in [
17006            Some(Placement::default()),
17007            Some(Placement {
17008                estrategia: PlacementStrategy::Sharded,
17009                clusters: vec!["rio".into(), "sao-paulo".into()],
17010                affinity: Some("data-locality".into()),
17011                shard_key: Some("$tenantId".into()),
17012            }),
17013        ] {
17014            let c = caixa_aplicacao_with_placement(placement.clone());
17015            let first = c.placement().unwrap();
17016            let second = c.placement().unwrap();
17017            assert_eq!(
17018                first, second,
17019                "Caixa::placement must be idempotent — two successive \
17020                 calls on the same &self must return the same \
17021                 &Placement",
17022            );
17023            assert!(
17024                std::ptr::eq(first, second),
17025                "Caixa::placement must borrow the underlying \
17026                 Option<Placement> storage — two successive calls \
17027                 must return references with the same backing pointer \
17028                 (a fresh Placement clone would change the pointer on \
17029                 every call)",
17030            );
17031            assert_eq!(
17032                Some(first),
17033                placement.as_ref(),
17034                "Caixa::placement must return :placement verbatim by \
17035                 borrow — got {first:?}, expected {:?}",
17036                placement.as_ref(),
17037            );
17038        }
17039        let c = caixa_aplicacao_with_placement(None);
17040        assert!(
17041            c.placement().is_none(),
17042            "Caixa::placement must return None when :placement is \
17043             absent — the author-omitted arm must project through the \
17044             accessor's Option::None unchanged",
17045        );
17046    }
17047
17048    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
17049
17050    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
17051        use crate::aplicacao::{Membro, WitContract};
17052        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17053        c.kind = CaixaKind::Aplicacao;
17054        c.membros = vec![Membro {
17055            caixa: "a".into(),
17056            versao: "^0.1".into(),
17057        }];
17058        c.contratos = vec![WitContract {
17059            de: "a".into(),
17060            para: "a".into(),
17061            wit: "wasi:http/proxy".into(),
17062            endpoint: Some("/x".into()),
17063            subject: None,
17064            slot: None,
17065        }];
17066        c.entrada = entrada;
17067        c
17068    }
17069
17070    #[test]
17071    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
17072        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
17073        // composite optional-composite-reference-shape pin:
17074        // [`Caixa::entrada`] must return the `:entrada` typed
17075        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
17076        // reference over the same backing storage the raw
17077        // `self.entrada.as_ref()` field access borrows from,
17078        // byte-equal across every representative fixture in the
17079        // accept-set — the author-omitted `None` shape (the
17080        // "cluster-internal Aplicacao" partition every downstream
17081        // Gateway-API emitter treats as "emit no listener + no
17082        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
17083        // (empty `paths` — the resolved-paths fallback the peer
17084        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
17085        // onto the substrate catch-all), and a fully-populated
17086        // multi-path-with-non-default-port fixture (the canonical
17087        // shape a public HTTP Aplicacao carries).
17088        //
17089        // Pins against a future silent detour that returned a fresh-
17090        // cloned [`crate::aplicacao::Entrada`] copy (which would
17091        // type-check via the `Clone` impl but silently break every
17092        // downstream caller that relied on the reference sharing the
17093        // composite's backing identity), a reference to an operator-
17094        // resolved overlay (the future per-cluster
17095        // `:entrada-overrides` slot — its resolution must land at
17096        // exactly this accessor body, not silently divert the raw
17097        // slot away from the peer [`Caixa::declared_mesh_slots`]
17098        // enumerator's presence probe), or an axis-shuffled projection
17099        // (a future detour that swapped `host` and `para` through the
17100        // accessor would silently split the paired
17101        // [`Caixa::aplicacao_view`] seed's forward input from the
17102        // sibling M3 gateway-artifact emitter's projection input).
17103        //
17104        // Fifth and final outer top-level [`Caixa`]
17105        // `Option<&Composite>`-return composite-reference accessor pin
17106        // on the substrate primitive — peer of the sibling
17107        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17108        // (b2bd9d7),
17109        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17110        // (35d8b52),
17111        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17112        // (5d23d29), and
17113        // `placement_returns_placement_option_ref_verbatim_across_permutations`
17114        // (4fb8074) opening tetrad pins on the outer top-level
17115        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17116        // here to the third and final M3 mesh-slot axis so the closed
17117        // outer `Option<&Composite>` sub-family carries the same
17118        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
17119        // accessor discipline across all five arms.
17120        use crate::aplicacao::Entrada;
17121        let fixtures: Vec<Option<Entrada>> = vec![
17122            None,
17123            Some(Entrada {
17124                host: "checkout.quero.cloud".into(),
17125                para: "gateway".into(),
17126                paths: Vec::new(),
17127                port: crate::DEFAULT_SERVICO_PORT,
17128            }),
17129            Some(Entrada {
17130                host: "api.pleme.io".into(),
17131                para: "public-api".into(),
17132                paths: vec!["/v1".into(), "/v2".into()],
17133                port: 8080,
17134            }),
17135        ];
17136        for entrada in fixtures {
17137            let c = caixa_aplicacao_with_entrada(entrada.clone());
17138            assert_eq!(
17139                c.entrada(),
17140                entrada.as_ref(),
17141                "Caixa::entrada must return :entrada verbatim (got \
17142                 {:?}, expected {:?})",
17143                c.entrada(),
17144                entrada.as_ref(),
17145            );
17146            match (c.entrada(), c.entrada.as_ref()) {
17147                (Some(a), Some(b)) => assert!(
17148                    std::ptr::eq(a, b),
17149                    "Caixa::entrada accessor and self.entrada.as_ref() \
17150                     field access must borrow the same backing storage \
17151                     — the accessor is the substrate-primitive typed \
17152                     dispatch every downstream Aplicacao-external- \
17153                     gateway composite consumer must route through, and \
17154                     a reference-identity split would silently break \
17155                     every consumer that relied on the borrow sharing \
17156                     the composite's storage",
17157                ),
17158                (None, None) => {}
17159                _ => panic!(
17160                    "Caixa::entrada presence bit must byte-equal \
17161                     self.entrada.is_some() — a presence-bit drift \
17162                     would silently split the paired \
17163                     Caixa::aplicacao_view Aplicacao-composition seed's \
17164                     traversal head from the peer \
17165                     Caixa::declared_mesh_slots M3 declared-slot \
17166                     enumerator's presence probe",
17167                ),
17168            }
17169            assert_eq!(
17170                c.entrada().is_some(),
17171                c.entrada.is_some(),
17172                "Caixa::entrada().is_some() must byte-equal \
17173                 self.entrada.is_some() — a presence-bit drift would \
17174                 silently split every downstream Option<&Entrada> \
17175                 consumer's partition on the cluster-internal arm",
17176            );
17177        }
17178    }
17179
17180    #[test]
17181    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
17182        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
17183        // presence-probe arm must key off [`Caixa::entrada`], not the
17184        // raw `self.entrada.is_some()` field-probe. Structurally: a
17185        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
17186        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
17187        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
17188        // presence bit is `Some`, so the M3 kind-coherence gate must
17189        // surface the slot as "declared" even when every per-axis
17190        // scalar defers to the substrate catch-all / default port),
17191        // and a `Caixa { entrada: None, .. }` must NOT push the label
17192        // (the "author omitted the slot entirely" partition). The pair
17193        // jointly pins the accessor + declared-slot enumerator
17194        // composition: any future silent detour that had the accessor
17195        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
17196        // `.filter(|e| !e.paths.is_empty())` projection) would silently
17197        // absorb the "declared but empty-paths" arm at the accessor
17198        // boundary and the
17199        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17200        // coherence gate would silently accept a struct-literal
17201        // `Caixa` carrying the drift.
17202        //
17203        // Peer of the sibling
17204        // `declared_servico_slots_limits_arm_routes_through_accessor`
17205        // (b2bd9d7),
17206        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17207        // (35d8b52),
17208        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17209        // (5d23d29), and
17210        // `declared_mesh_slots_placement_arm_routes_through_accessor`
17211        // (4fb8074) composition pins on the sibling `:limits` /
17212        // `:behavior` / `:politicas` / `:placement` outer-
17213        // `Option<&Composite>` arms — same "the enumerator gate must
17214        // route through the substrate-primitive typed dispatch"
17215        // discipline extended onto the third and final M3 mesh-slot
17216        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
17217        // carries the routing invariant on every M3 mesh-slot arm.
17218        use crate::aplicacao::Entrada;
17219        let c = caixa_aplicacao_with_entrada(Some(Entrada {
17220            host: "checkout.quero.cloud".into(),
17221            para: "gateway".into(),
17222            paths: Vec::new(),
17223            port: crate::DEFAULT_SERVICO_PORT,
17224        }));
17225        let slots = c.declared_mesh_slots();
17226        assert!(
17227            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17228            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
17229             `:entrada` is Some (even for empty-paths / default-port) \
17230             — the accessor and the enumerator gate must route through \
17231             the same substrate-primitive typed dispatch on the outer \
17232             :entrada presence bit (got slots={slots:?})",
17233        );
17234        let c = caixa_aplicacao_with_entrada(None);
17235        let slots = c.declared_mesh_slots();
17236        assert!(
17237            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17238            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
17239             when `:entrada` is None — the author-omitted arm must \
17240             route through the accessor's None-return unchanged (got \
17241             slots={slots:?})",
17242        );
17243    }
17244
17245    #[test]
17246    fn aplicacao_view_entrada_arm_folds_through_accessor() {
17247        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
17248        // Aplicacao-composition seed must fold through
17249        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
17250        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
17251        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
17252        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
17253        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
17254        // equals the outer composite's authored value (the fold must
17255        // project the authored composite verbatim), and a `Caixa {
17256        // entrada: None, kind: Aplicacao, .. }` must surface an
17257        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
17258        // "author omitted the slot entirely" arm folds through the
17259        // accessor's `Option::cloned` onto the same `None` presence
17260        // bit — unlike the peer `:politicas` / `:placement` arms
17261        // `:entrada` has no cluster-default fold, the omitted arm
17262        // stays omitted). The pair jointly pins the accessor +
17263        // Aplicacao-composition seed composition: any future silent
17264        // detour that had the accessor divert the raw slot away from
17265        // the seed's fold (an operator-resolved overlay's forward arm
17266        // silently differing from the raw slot's forward arm) would
17267        // silently split the build-time gateway-artifact emission gate
17268        // from the caixa-mesh renderer's Aplicacao-view input at the
17269        // composition boundary.
17270        use crate::aplicacao::Entrada;
17271        let authored = Entrada {
17272            host: "api.pleme.io".into(),
17273            para: "public-api".into(),
17274            paths: vec!["/v1".into()],
17275            port: 8080,
17276        };
17277        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
17278        let view = c.aplicacao_view().unwrap();
17279        assert_eq!(
17280            view.entrada(),
17281            Some(&authored),
17282            "Caixa::aplicacao_view must fold the authored :entrada \
17283             composite through the accessor verbatim onto the \
17284             projected AplicacaoSpec — a future silent detour at the \
17285             seed's fold arm would surface here as a projected- \
17286             composite drift (got {:?})",
17287            view.entrada(),
17288        );
17289        let c = caixa_aplicacao_with_entrada(None);
17290        let view = c.aplicacao_view().unwrap();
17291        assert!(
17292            view.entrada().is_none(),
17293            "Caixa::aplicacao_view must fold None through the \
17294             accessor's Option::cloned onto None — the author- \
17295             omitted arm must route through the accessor's None-return \
17296             unchanged (got {:?})",
17297            view.entrada(),
17298        );
17299    }
17300
17301    #[test]
17302    fn entrada_projects_option_ref_by_borrow() {
17303        // The by-borrow pin: [`Caixa::entrada`] returns
17304        // `Option<&Entrada>` by borrow — the returned reference
17305        // borrows the underlying `Option<Entrada>` storage of the
17306        // `:entrada` slot and the accessor must not clone the backing
17307        // composite on every call. Peer of the sibling
17308        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17309        // `behavior_projects_option_ref_by_borrow` (35d8b52),
17310        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
17311        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
17312        // borrow pins on the outer top-level [`Caixa`]
17313        // `Option<&Composite>`-return sub-family — extended here to
17314        // the fifth and final axis of the same sub-family, closing
17315        // the discipline: the accessor's returned reference must
17316        // borrow from `&self` (the returned reference's lifetime is
17317        // tied to `&self`), and calling the accessor twice on the
17318        // same [`Caixa`] must yield references that are pointer-equal
17319        // (the underlying byte-buffer is the storage `Entrada`'s
17320        // allocation, not a fresh copy) as well as value-equal
17321        // (idempotent, no side effects on `&self`).
17322        //
17323        // Pins against a future silent detour that returned an owned
17324        // `Entrada` (which would type-check via the `Clone` impl but
17325        // silently clone on every call), a `&Entrada` panic-return on
17326        // the `None` arm (which would collapse the load-bearing
17327        // `Option` presence-bit into a runtime panic), or a one-arm-
17328        // only accessor that returned a saturating composite on some
17329        // sentinel input.
17330        use crate::aplicacao::Entrada;
17331        for entrada in [
17332            Some(Entrada {
17333                host: "checkout.quero.cloud".into(),
17334                para: "gateway".into(),
17335                paths: Vec::new(),
17336                port: crate::DEFAULT_SERVICO_PORT,
17337            }),
17338            Some(Entrada {
17339                host: "api.pleme.io".into(),
17340                para: "public-api".into(),
17341                paths: vec!["/v1".into(), "/v2".into()],
17342                port: 8080,
17343            }),
17344        ] {
17345            let c = caixa_aplicacao_with_entrada(entrada.clone());
17346            let first = c.entrada().unwrap();
17347            let second = c.entrada().unwrap();
17348            assert_eq!(
17349                first, second,
17350                "Caixa::entrada must be idempotent — two successive \
17351                 calls on the same &self must return the same &Entrada",
17352            );
17353            assert!(
17354                std::ptr::eq(first, second),
17355                "Caixa::entrada must borrow the underlying \
17356                 Option<Entrada> storage — two successive calls must \
17357                 return references with the same backing pointer (a \
17358                 fresh Entrada clone would change the pointer on every \
17359                 call)",
17360            );
17361            assert_eq!(
17362                Some(first),
17363                entrada.as_ref(),
17364                "Caixa::entrada must return :entrada verbatim by \
17365                 borrow — got {first:?}, expected {:?}",
17366                entrada.as_ref(),
17367            );
17368        }
17369        let c = caixa_aplicacao_with_entrada(None);
17370        assert!(
17371            c.entrada().is_none(),
17372            "Caixa::entrada must return None when :entrada is absent \
17373             — the author-omitted arm must project through the \
17374             accessor's Option::None unchanged",
17375        );
17376    }
17377
17378    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
17379
17380    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
17381        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17382        c.estrategia = estrategia;
17383        c
17384    }
17385
17386    #[test]
17387    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
17388        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
17389        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
17390        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
17391        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
17392        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
17393        // over the same discriminant the raw `self.estrategia` field
17394        // access carries, byte-equal across every representative fixture
17395        // in the accept-set — the author-omitted `None` shape (the
17396        // "defer to [`RestartStrategy::default`] through the
17397        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
17398        // every non-`Supervisor`-kind `defcaixa` carries by
17399        // `#[serde(default)]`), and each of the four closed-set variants
17400        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
17401        // / [`RestartStrategy::RestForOne`] /
17402        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
17403        // partitions on.
17404        //
17405        // Pins against a future silent detour that re-derived the
17406        // strategy from a peer axis (an accidental fallback to
17407        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
17408        // collapse that read the outer `:children` list-length axis into
17409        // the strategy discriminator at the accessor boundary), a
17410        // stale-derive detour that substituted [`RestartStrategy::default`]
17411        // when the outer `Option` held `None` (which would silently
17412        // collapse the load-bearing "author explicitly declared
17413        // `:estrategia OneForOne`" vs "author omitted the slot and
17414        // inherited the default" partition the [`Self::declared_supervisor_slots`]
17415        // presence-probe reads — the enumerator gate would still push
17416        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
17417        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17418        // kind-coherence gate's traversal head from the
17419        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
17420        // composition head), a reference to an operator-resolved overlay
17421        // (the future per-cluster `:estrategia-overrides` slot — its
17422        // resolution must land at exactly this accessor body, not
17423        // silently divert the raw slot away from a second consumer), or
17424        // an axis-remap projection (a future detour that mapped
17425        // `OneForAll` through the accessor onto `OneForOne` would
17426        // silently split every downstream sibling-restart-strategy
17427        // consumer's per-arm fan-out).
17428        //
17429        // First outer top-level [`Caixa`] `Option<Copy>`-return
17430        // supervisor-tree-slot flat-spread accessor pin on the substrate
17431        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
17432        // projection pattern the sibling per-`Caixa` `:max-restarts` /
17433        // `:restart-window` future outer-scalar pins fold on. Peer of
17434        // the inner-altitude
17435        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17436        // (eafb619) pin on the post-composition [`SupervisorSpec`]
17437        // altitude — same "the substrate-primitive accessor must byte-
17438        // equal the raw field access verbatim across every author-
17439        // declared value" discipline extended onto the pre-composition
17440        // outer author-surface [`Caixa`] altitude. Peer of the closed
17441        // outer-`Caixa` `Option<&Composite>` composite-reference family
17442        // the sibling `limits` / `behavior` / `politicas` / `placement` /
17443        // `entrada`
17444        // `..._returns_..._option_ref_verbatim_across_permutations` pins
17445        // already carry on the outer `Option<&Composite>` altitude.
17446        use crate::supervisor::RestartStrategy;
17447        let fixtures: Vec<Option<RestartStrategy>> = vec![
17448            None,
17449            Some(RestartStrategy::OneForOne),
17450            Some(RestartStrategy::OneForAll),
17451            Some(RestartStrategy::RestForOne),
17452            Some(RestartStrategy::SimpleOneForOne),
17453        ];
17454        for estrategia in fixtures {
17455            let c = caixa_with_estrategia(estrategia);
17456            assert_eq!(
17457                c.estrategia(),
17458                estrategia,
17459                "Caixa::estrategia must return :estrategia verbatim (got \
17460                 {:?}, expected {:?})",
17461                c.estrategia(),
17462                estrategia,
17463            );
17464            assert_eq!(
17465                c.estrategia(),
17466                c.estrategia,
17467                "Caixa::estrategia accessor and self.estrategia field \
17468                 access must byte-equal — the accessor is the substrate-\
17469                 primitive typed dispatch every downstream supervisor-\
17470                 tree flat-spread consumer must route through, and a \
17471                 discriminant split would silently break every consumer \
17472                 that relied on the accessor sharing the field's own \
17473                 Option<Copy> shape",
17474            );
17475            assert_eq!(
17476                c.estrategia().is_some(),
17477                c.estrategia.is_some(),
17478                "Caixa::estrategia().is_some() must byte-equal \
17479                 self.estrategia.is_some() — a presence-bit drift would \
17480                 silently split the paired Caixa::declared_supervisor_slots \
17481                 presence-probe arm from the Caixa::supervisor_view \
17482                 unwrap_or_default() fold's composition input",
17483            );
17484        }
17485    }
17486
17487    #[test]
17488    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
17489        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17490        // `:estrategia` presence-probe arm must key off
17491        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
17492        // field-probe. Structurally: every `Caixa { estrategia:
17493        // Some(RestartStrategy::_), .. }` variant must push
17494        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
17495        // (the presence bit is `Some` for every closed-set variant, so
17496        // the M2 supervisor-tree kind-coherence gate must surface the
17497        // slot as "declared" regardless of which variant the author
17498        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
17499        // the label (the "author omitted the slot entirely, deferring
17500        // to [`RestartStrategy::default`] through the supervisor_view
17501        // fold" partition). The pair jointly pins the accessor +
17502        // declared-slot enumerator composition: any future silent detour
17503        // that had the accessor collapse `Some(RestartStrategy::default())`
17504        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
17505        // projection) would silently absorb the "declared but default-
17506        // valued" arm at the accessor boundary and the
17507        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
17508        // coherence gate would silently accept a struct-literal `Caixa`
17509        // carrying the drift.
17510        //
17511        // Peer of the sibling per-`Caixa`
17512        // `declared_servico_slots_limits_arm_routes_through_accessor`
17513        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
17514        // `Option<&LimitsSpec>` composition axis — same "the enumerator
17515        // gate must route through the substrate-primitive typed
17516        // dispatch" discipline extended onto the flat-spread M2
17517        // supervisor-tree `Option<RestartStrategy>`-composition surface,
17518        // opening the outer-`Caixa` supervisor-tree-slot arm of the
17519        // composition-pin family.
17520        use crate::supervisor::RestartStrategy;
17521        for estrategia in [
17522            RestartStrategy::OneForOne,
17523            RestartStrategy::OneForAll,
17524            RestartStrategy::RestForOne,
17525            RestartStrategy::SimpleOneForOne,
17526        ] {
17527            let c = caixa_with_estrategia(Some(estrategia));
17528            let slots = c.declared_supervisor_slots();
17529            assert!(
17530                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17531                "declared_supervisor_slots must push \
17532                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
17533                 Some({estrategia:?}) — the accessor and the enumerator \
17534                 gate must route through the same substrate-primitive \
17535                 typed dispatch on the outer :estrategia presence bit \
17536                 (got slots={slots:?})",
17537            );
17538        }
17539        let c = caixa_with_estrategia(None);
17540        let slots = c.declared_supervisor_slots();
17541        assert!(
17542            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17543            "declared_supervisor_slots must NOT push \
17544             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
17545             — the author-omitted arm must route through the accessor's \
17546             None-return unchanged (got slots={slots:?})",
17547        );
17548    }
17549
17550    #[test]
17551    fn supervisor_view_estrategia_arm_routes_through_accessor() {
17552        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
17553        // [`SupervisorSpec`] construction arm must key off
17554        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
17555        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
17556        // for every `:kind Supervisor` `Caixa` carrying an author-
17557        // declared `Some(RestartStrategy::_)` variant, the composed
17558        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
17559        // outer accessor's declared variant unchanged; and for a
17560        // `:kind Supervisor` `Caixa` carrying `None`, the composed
17561        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
17562        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
17563        // arm the flat-spread `unwrap_or_default()` fold projects to on
17564        // the author-omitted arm — this is the *composition* between the
17565        // outer `Option<RestartStrategy>` accessor's presence-bit
17566        // surface and the inner post-composition non-`Option`
17567        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
17568        // pins the accessor + supervisor_view composition: any future
17569        // silent detour that had the accessor promote `None` to
17570        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
17571        // projection) would silently collapse the two arms into one at
17572        // the accessor boundary and the [`Self::declared_supervisor_slots`]
17573        // presence probe would silently drift from the composition site.
17574        //
17575        // Peer of the sibling M2 supervisor-slot post-composition
17576        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
17577        // pin on the [`SupervisorSpec::validate`] altitude — this pin
17578        // extends that inner-altitude accessor-routing discipline onto
17579        // the pre-composition outer author-surface [`Caixa`] altitude,
17580        // pinning the composition edge between the flat-spread outer
17581        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
17582        // `RestartStrategy` axes.
17583        use crate::CaixaKind;
17584        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17585        for estrategia in [
17586            RestartStrategy::OneForOne,
17587            RestartStrategy::OneForAll,
17588            RestartStrategy::RestForOne,
17589            RestartStrategy::SimpleOneForOne,
17590        ] {
17591            let mut c = caixa_with_estrategia(Some(estrategia));
17592            c.kind = CaixaKind::Supervisor;
17593            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
17594            // shape partition through the [`gen_platform::IsVariant`]
17595            // derive-generated
17596            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
17597            // than the raw `matches!(estrategia, RestartStrategy::
17598            // SimpleOneForOne)` open-coded pattern-match — same closed-
17599            // set-typed-enum arm-discriminator dispatch discipline the
17600            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
17601            // convergence (915a934) extended onto its two paired positive
17602            // / negated `matches!` sites and the peer
17603            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
17604            // predicate convergence (766ec63) extended onto the M3 mesh-
17605            // slot per-`:placement` distribution-strategy discriminator
17606            // axis. See the sibling `supervisor::tests::
17607            // round_trip_all_strategies` and
17608            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17609            // fixtures — the three sites (all test-only,
17610            // acknowledged in 915a934's Prior-commits footnote as the
17611            // outstanding follow-up) now consult one typed dispatch on
17612            // the substrate primitive.
17613            c.children = if estrategia.is_simple_one_for_one() {
17614                Vec::new()
17615            } else {
17616                vec![ChildSpec {
17617                    caixa: "worker".into(),
17618                    versao: "^0.1".into(),
17619                    restart: RestartPolicy::Permanent,
17620                }]
17621            };
17622            let view = c.supervisor_view().expect(
17623                "supervisor_view must materialize a SupervisorSpec for a \
17624                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
17625            );
17626            assert_eq!(
17627                view.estrategia(),
17628                c.estrategia().unwrap(),
17629                "supervisor_view must carry the outer Caixa::estrategia() \
17630                 declared variant onto the composed SupervisorSpec.estrategia \
17631                 field verbatim on the Some arm (got {:?}, expected {:?})",
17632                view.estrategia(),
17633                c.estrategia().unwrap(),
17634            );
17635        }
17636        // The author-omitted arm: outer `None` → composed
17637        // `RestartStrategy::default()` through the flat-spread
17638        // `unwrap_or_default()` fold.
17639        let mut c = caixa_with_estrategia(None);
17640        c.kind = CaixaKind::Supervisor;
17641        // Populate children so the sibling supervisor slots are coherent
17642        // for the [`Self::supervisor_view`] projection; the `:estrategia`
17643        // arm still defers to [`RestartStrategy::default`] on the
17644        // author-omitted arm even when the sibling slots carry values.
17645        c.children = vec![ChildSpec {
17646            caixa: "worker".into(),
17647            versao: "^0.1".into(),
17648            restart: RestartPolicy::Permanent,
17649        }];
17650        let view = c.supervisor_view().expect(
17651            "supervisor_view must materialize a SupervisorSpec for a \
17652             :kind Supervisor Caixa carrying a None `:estrategia` slot",
17653        );
17654        assert_eq!(
17655            view.estrategia(),
17656            RestartStrategy::default(),
17657            "supervisor_view must project the outer Caixa::estrategia() \
17658             None arm onto RestartStrategy::default() through the flat-\
17659             spread unwrap_or_default() fold (got {:?}, expected {:?})",
17660            view.estrategia(),
17661            RestartStrategy::default(),
17662        );
17663        assert!(
17664            c.estrategia().is_none(),
17665            "Caixa::estrategia() must remain None on the author-omitted \
17666             arm — the supervisor_view fold must not mutate the outer \
17667             flat-spread presence bit",
17668        );
17669    }
17670
17671    #[test]
17672    fn estrategia_projects_option_by_copy() {
17673        // The by-`Copy` pin: [`Caixa::estrategia`] returns
17674        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
17675        // the accessor does not borrow `&self` past the call (no
17676        // lifetime on the return type), and calling the accessor twice
17677        // on the same [`Caixa`] must yield discriminant-equal values
17678        // (idempotent, no side effects on `&self`). Peer of the sibling
17679        // outer-`Caixa` `Option<&Composite>` by-borrow
17680        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
17681        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
17682        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
17683        // `placement_projects_option_ref_by_borrow` (4fb8074) /
17684        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
17685        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
17686        // extended here to the outer-`Caixa` `Option<Copy>`-return
17687        // flat-spread axis. The `Copy` discipline replaces the pointer-
17688        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
17689        // `Copy` discriminant is definitionally the same discriminant, so
17690        // the axis reduces to discriminant equality).
17691        //
17692        // Pins against a future silent detour that returned a fresh
17693        // `Option<&RestartStrategy>` (which would type-check but silently
17694        // introduce a borrow of `&self` past the call, collapsing the
17695        // load-bearing "no lifetime on the return type" `Copy` projection
17696        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
17697        // read side effect that flipped the outer discriminant on
17698        // successive calls, or an axis-remap projection that returned a
17699        // different variant than the field storage.
17700        use crate::supervisor::RestartStrategy;
17701        for estrategia in [
17702            Some(RestartStrategy::OneForOne),
17703            Some(RestartStrategy::OneForAll),
17704            Some(RestartStrategy::RestForOne),
17705            Some(RestartStrategy::SimpleOneForOne),
17706        ] {
17707            let c = caixa_with_estrategia(estrategia);
17708            let first = c.estrategia();
17709            let second = c.estrategia();
17710            assert_eq!(
17711                first, second,
17712                "Caixa::estrategia must be idempotent — two successive \
17713                 calls on the same &self must return the same \
17714                 Option<RestartStrategy>",
17715            );
17716            assert_eq!(
17717                first, estrategia,
17718                "Caixa::estrategia must return :estrategia verbatim by \
17719                 Copy — got {first:?}, expected {estrategia:?}",
17720            );
17721        }
17722        let c = caixa_with_estrategia(None);
17723        assert!(
17724            c.estrategia().is_none(),
17725            "Caixa::estrategia must return None when :estrategia is \
17726             absent — the author-omitted arm must project through the \
17727             accessor's Option::None unchanged",
17728        );
17729    }
17730
17731    // ── Caixa::max_restarts / Caixa::restart_window —
17732    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
17733    //    (Option<u32> / Option<&str>) folding on the ed04d3c
17734    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
17735
17736    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
17737        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17738        c.max_restarts = max_restarts;
17739        c
17740    }
17741
17742    fn caixa_supervisor_with_max_restarts_and_window(
17743        max_restarts: Option<u32>,
17744        restart_window: Option<&str>,
17745    ) -> Caixa {
17746        use crate::CaixaKind;
17747        use crate::supervisor::{ChildSpec, RestartPolicy};
17748        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
17749        c.kind = CaixaKind::Supervisor;
17750        c.max_restarts = max_restarts;
17751        c.restart_window = restart_window.map(str::to_string);
17752        c.children = vec![ChildSpec {
17753            caixa: "worker".into(),
17754            versao: "^0.1".into(),
17755            restart: RestartPolicy::Permanent,
17756        }];
17757        c
17758    }
17759
17760    #[test]
17761    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
17762        // Value-shape pin: [`Caixa::max_restarts`] returns the
17763        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
17764        // from the typed slot's own storage, byte-equal across the
17765        // author-omitted `None` arm (the "defer to the
17766        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
17767        // `{intensity, 5, 60}` default" partition every
17768        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
17769        // and each of the representative fixtures in the accept-set —
17770        // `0` (the zero-floor arm the peer
17771        // [`crate::supervisor::SupervisorSpec::validate`]
17772        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
17773        // the post-composition altitude — the accessor must ship the
17774        // raw slot verbatim so struct-literal fixtures continue to
17775        // expose the zero at the accessor boundary), the OTP-canonical
17776        // `5` default (`{intensity, 5, 60}` worker-supervisor from
17777        // Learn You Some Erlang), `1000` (the
17778        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
17779        // upper-bound gate accepts on the boundary), `u32::MAX` (a
17780        // past-the-cap sentinel that the substrate-primitive accessor
17781        // must still ship verbatim). Second outer top-level
17782        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
17783        // pin — folds on the sibling
17784        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
17785        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
17786        // onto the sibling `Option<u32>` restart-budget-count arm.
17787        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
17788        for max_restarts in fixtures {
17789            let c = caixa_with_max_restarts(max_restarts);
17790            assert_eq!(
17791                c.max_restarts(),
17792                max_restarts,
17793                "Caixa::max_restarts must return :max-restarts verbatim \
17794                 (got {:?}, expected {max_restarts:?})",
17795                c.max_restarts(),
17796            );
17797            assert_eq!(
17798                c.max_restarts(),
17799                c.max_restarts,
17800                "Caixa::max_restarts accessor and self.max_restarts \
17801                 field access must byte-equal — a presence-bit or count \
17802                 drift would silently split the paired \
17803                 Caixa::declared_supervisor_slots presence-probe arm \
17804                 from the Caixa::supervisor_view unwrap_or(5) fold's \
17805                 composition input",
17806            );
17807        }
17808    }
17809
17810    #[test]
17811    fn max_restarts_projects_option_by_copy() {
17812        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
17813        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
17814        // borrow `&self` past the call (no lifetime on the return type),
17815        // and calling the accessor twice on the same [`Caixa`] must
17816        // yield equal values (idempotent, no side effects). Peer of the
17817        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
17818        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
17819        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
17820            let c = caixa_with_max_restarts(max_restarts);
17821            let first = c.max_restarts();
17822            let second = c.max_restarts();
17823            assert_eq!(
17824                first, second,
17825                "Caixa::max_restarts must be idempotent — two successive \
17826                 calls on the same &self must return the same Option<u32>",
17827            );
17828            assert_eq!(
17829                first, max_restarts,
17830                "Caixa::max_restarts must return :max-restarts verbatim \
17831                 by Copy — got {first:?}, expected {max_restarts:?}",
17832            );
17833        }
17834    }
17835
17836    #[test]
17837    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
17838        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17839        // `:max-restarts` presence-probe arm must key off
17840        // [`Caixa::max_restarts`], not the raw
17841        // `self.max_restarts.is_some()` field-probe. Structurally: every
17842        // `Caixa { max_restarts: Some(_), .. }` variant must push
17843        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
17844        // list (the presence bit is `Some` for every representative
17845        // count, so the M2 kind-coherence gate must surface the slot as
17846        // "declared"), and a `Caixa { max_restarts: None, .. }` must
17847        // NOT push the label. Peer of the sibling
17848        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
17849        // (ed04d3c) composition pin — same routing-through-accessor
17850        // discipline extended onto the sibling flat-spread `Option<u32>`
17851        // arm.
17852        for max_restarts in [0u32, 5, 1000, u32::MAX] {
17853            let c = caixa_with_max_restarts(Some(max_restarts));
17854            let slots = c.declared_supervisor_slots();
17855            assert!(
17856                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
17857                "declared_supervisor_slots must push \
17858                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
17859                 is Some({max_restarts}) — the accessor and the \
17860                 enumerator gate must route through the same \
17861                 substrate-primitive typed dispatch on the outer \
17862                 :max-restarts presence bit (got slots={slots:?})",
17863            );
17864        }
17865        let c = caixa_with_max_restarts(None);
17866        let slots = c.declared_supervisor_slots();
17867        assert!(
17868            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
17869            "declared_supervisor_slots must NOT push \
17870             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
17871             None — the author-omitted arm must route through the \
17872             accessor's None-return unchanged (got slots={slots:?})",
17873        );
17874    }
17875
17876    #[test]
17877    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
17878        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
17879        // [`SupervisorSpec`] construction arm must key off
17880        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
17881        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
17882        // every `:kind Supervisor` `Caixa` carrying an author-declared
17883        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
17884        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
17885        // carrying `None`, the composed [`SupervisorSpec`]'s
17886        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
17887        // of the sibling
17888        // `supervisor_view_estrategia_arm_routes_through_accessor`
17889        // (ed04d3c) composition pin.
17890        for max_restarts in [1u32, 5, 1000] {
17891            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
17892            let view = c.supervisor_view().expect(
17893                "supervisor_view must materialize a SupervisorSpec for a \
17894                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
17895            );
17896            assert_eq!(
17897                view.max_restarts(),
17898                max_restarts,
17899                "supervisor_view must carry the outer \
17900                 Caixa::max_restarts() Some arm onto the composed \
17901                 SupervisorSpec.max_restarts field verbatim (got {}, \
17902                 expected {max_restarts})",
17903                view.max_restarts(),
17904            );
17905        }
17906        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
17907        let view = c.supervisor_view().expect(
17908            "supervisor_view must materialize a SupervisorSpec for a \
17909             :kind Supervisor Caixa carrying a None :max-restarts",
17910        );
17911        assert_eq!(
17912            view.max_restarts(),
17913            5,
17914            "supervisor_view must project the outer \
17915             Caixa::max_restarts() None arm onto the OTP-canonical \
17916             {{intensity, 5, 60}} default (5) through the flat-spread \
17917             unwrap_or(5) fold (got {})",
17918            view.max_restarts(),
17919        );
17920        assert!(
17921            c.max_restarts().is_none(),
17922            "Caixa::max_restarts() must remain None on the author-\
17923             omitted arm — the supervisor_view fold must not mutate \
17924             the outer flat-spread presence bit",
17925        );
17926    }
17927
17928    #[test]
17929    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
17930        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
17931        // `:estrategia` arm must degrade onto the substrate-canonical
17932        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
17933        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
17934        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
17935        // worker-supervisor default — rather than the transitively-
17936        // derived [`crate::supervisor::RestartStrategy::default`] route
17937        // the prior `.unwrap_or_default()` fold reached for. Prior to the
17938        // lift the composition site carried `.unwrap_or_default()` with
17939        // no compile-time link back to the shared OTP-canonical strategy
17940        // default that the paired [`crate::supervisor::Default for
17941        // RestartStrategy`] impl and the [`crate::supervisor::Default for
17942        // SupervisorSpec`] impl's struct-literal `estrategia` field both
17943        // (now) route through the same lifted constant — so a future
17944        // rebrand of the OTP-canonical strategy default (an OTP
17945        // `rest_for_one` widening once the substrate discovers startup-
17946        // order-coupled child cohorts as the more common worker-
17947        // supervisor shape, a per-cluster overlay the operator pins
17948        // through the MESH-COMPOSITION §III.2 supervision-canary
17949        // `:estrategia-overrides` roadmap slot) would have had to migrate
17950        // the paired `MaxIntensity` + `Period` halves through the lifted
17951        // constants and the `one_for_one` half through a
17952        // `RestartStrategy::default()` route in lockstep or a
17953        // `:kind Supervisor` caixa carrying an author-omitted
17954        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
17955        // whose `estrategia` disagreed with the paired
17956        // `SupervisorSpec::default()` view. Byte-parity against the
17957        // lifted constant closes the split. Peer of the sibling
17958        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
17959        // composition pin on the paired `MaxIntensity` half + the
17960        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
17961        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
17962        // pins on the sibling entry points onto the shared substrate
17963        // constant.
17964        use crate::CaixaKind;
17965        use crate::supervisor::{ChildSpec, RestartPolicy};
17966        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
17967        c.kind = CaixaKind::Supervisor;
17968        c.estrategia = None;
17969        c.children = vec![ChildSpec {
17970            caixa: "worker".into(),
17971            versao: "^0.1".into(),
17972            restart: RestartPolicy::Permanent,
17973        }];
17974        let view = c.supervisor_view().expect(
17975            "supervisor_view must materialize a SupervisorSpec for a \
17976             :kind Supervisor Caixa carrying a None :estrategia",
17977        );
17978        assert_eq!(
17979            view.estrategia(),
17980            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17981            "supervisor_view must degrade the outer \
17982             Caixa::estrategia() None arm onto the lifted \
17983             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
17984             expected {:?})",
17985            view.estrategia(),
17986            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17987        );
17988    }
17989
17990    #[test]
17991    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
17992        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
17993        // `:max-restarts` arm must degrade onto the substrate-canonical
17994        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
17995        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
17996        // `MaxIntensity` default — rather than a raw `5` literal. Prior
17997        // to the lift the composition site carried an inline
17998        // `.unwrap_or(5)` with no compile-time link back to the shared
17999        // OTP-canonical default that the serde-side
18000        // `#[serde(default = "default_max_restarts")]` wire-format arm
18001        // and the [`Default for crate::supervisor::SupervisorSpec`]
18002        // struct-literal default arm both key off — so a future rebrand
18003        // of the OTP-canonical default (Elixir's `Supervisor` `3`
18004        // default, a per-cluster overlay the operator pins through the
18005        // MESH-COMPOSITION §III.2 supervision-canary
18006        // `:supervisor :max-restarts-overrides` roadmap slot) would
18007        // have had to be threaded through both the serde-side helper
18008        // and this view-construction arm in lockstep or a `:kind
18009        // Supervisor` caixa carrying `:max-restarts ()` would silently
18010        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
18011        // with the same fixture's serde-side `SupervisorSpec` view (an
18012        // author-omitted slot round-tripping through
18013        // `SupervisorSpec::default()` to the lifted constant, then
18014        // splitting to a stale literal past `supervisor_view`).
18015        // Byte-parity against the lifted constant closes the split.
18016        // Peer of the sibling
18017        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
18018        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
18019        // composition pins that close the same routing on the two
18020        // sibling entry points onto the shared substrate constant.
18021        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18022        let view = c.supervisor_view().expect(
18023            "supervisor_view must materialize a SupervisorSpec for a \
18024             :kind Supervisor Caixa carrying a None :max-restarts",
18025        );
18026        assert_eq!(
18027            view.max_restarts(),
18028            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18029            "supervisor_view must degrade the outer \
18030             Caixa::max_restarts() None arm onto the lifted \
18031             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
18032             expected {})",
18033            view.max_restarts(),
18034            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18035        );
18036    }
18037
18038    #[test]
18039    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
18040        // Value-shape pin: [`Caixa::restart_window`] returns the
18041        // `:restart-window` typed `Option<String>` verbatim as an
18042        // `Option<&str>`, borrowed from the typed slot's own storage,
18043        // byte-equal across the author-omitted `None` arm and each of
18044        // the representative fixtures in the accept-set — the canonical
18045        // `"60s"` from `{intensity, 5, 60}`, the sibling
18046        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
18047        // / `"0s"`) the shared codec's positive-set sweep pin covers,
18048        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
18049        // seconds drift the sibling [`Self::validate_restart_window`]
18050        // gate refuses; the accessor must ship the raw slot verbatim
18051        // so struct-literal fixtures continue to expose the drift at
18052        // the accessor boundary). Third outer top-level [`Caixa`]
18053        // supervisor-tree flat-spread pin — extends the sub-family onto
18054        // the sibling `Option<&str>` raw-duration-string arm.
18055        for window in [
18056            None,
18057            Some("60s"),
18058            Some("5m"),
18059            Some("1h"),
18060            Some("500ms"),
18061            Some("1.5s"),
18062            Some(""),
18063        ] {
18064            let c = caixa_with_restart_window(window);
18065            assert_eq!(
18066                c.restart_window(),
18067                window,
18068                "Caixa::restart_window must return :restart-window \
18069                 verbatim as Option<&str> (got {:?}, expected {window:?})",
18070                c.restart_window(),
18071            );
18072            assert_eq!(
18073                c.restart_window(),
18074                c.restart_window.as_deref(),
18075                "Caixa::restart_window accessor and \
18076                 self.restart_window.as_deref() field access must \
18077                 byte-equal — a byte-level drift would silently split \
18078                 the paired Caixa::declared_supervisor_slots \
18079                 presence-probe arm from the \
18080                 Caixa::validate_restart_window shared-codec gate and \
18081                 the Caixa::supervisor_view soft-swallowing fold",
18082            );
18083        }
18084    }
18085
18086    #[test]
18087    fn restart_window_projects_slice_by_borrow() {
18088        // The by-borrow pin: [`Caixa::restart_window`] returns
18089        // `Option<&str>` by borrow — the returned string slice borrows
18090        // the underlying `Option<String>` storage of the `:restart-window`
18091        // slot and the accessor must not clone on every call. Peer of
18092        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
18093        // by-borrow pins on the universal-axis scalar family
18094        // (`licenca_projects_option_ref_by_borrow` /
18095        // `descricao_projects_option_ref_by_borrow` and siblings) —
18096        // extended onto the M2 supervisor-tree flat-spread
18097        // `Option<&str>` raw-duration-string axis.
18098        for window in [None, Some("60s"), Some("5m"), Some("")] {
18099            let c = caixa_with_restart_window(window);
18100            let first = c.restart_window();
18101            let second = c.restart_window();
18102            assert_eq!(
18103                first, second,
18104                "Caixa::restart_window must be idempotent — two \
18105                 successive calls on the same &self must return the \
18106                 same Option<&str>",
18107            );
18108            if let (Some(a), Some(b)) = (first, second) {
18109                assert_eq!(
18110                    a.as_ptr(),
18111                    b.as_ptr(),
18112                    "Caixa::restart_window must borrow the underlying \
18113                     String storage — two successive Some-arm calls must \
18114                     return slices with the same backing pointer (a fresh \
18115                     String clone would change the pointer on every call)",
18116                );
18117            }
18118            assert_eq!(
18119                first, window,
18120                "Caixa::restart_window must return :restart-window \
18121                 verbatim by borrow — got {first:?}, expected {window:?}",
18122            );
18123        }
18124    }
18125
18126    #[test]
18127    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
18128        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18129        // `:restart-window` presence-probe arm must key off
18130        // [`Caixa::restart_window`], not the raw
18131        // `self.restart_window.is_some()` field-probe. Structurally:
18132        // every `Caixa { restart_window: Some(_), .. }` must push
18133        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
18134        // list, and a `Caixa { restart_window: None, .. }` must NOT
18135        // push the label. Peer of the sibling
18136        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
18137        // routing pin.
18138        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
18139            let c = caixa_with_restart_window(Some(window));
18140            let slots = c.declared_supervisor_slots();
18141            assert!(
18142                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18143                "declared_supervisor_slots must push \
18144                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
18145                 `:restart-window` is Some({window:?}) — the accessor \
18146                 and the enumerator gate must route through the same \
18147                 substrate-primitive typed dispatch on the outer \
18148                 :restart-window presence bit (got slots={slots:?})",
18149            );
18150        }
18151        let c = caixa_with_restart_window(None);
18152        let slots = c.declared_supervisor_slots();
18153        assert!(
18154            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18155            "declared_supervisor_slots must NOT push \
18156             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
18157             is None — the author-omitted arm must route through the \
18158             accessor's None-return unchanged (got slots={slots:?})",
18159        );
18160    }
18161
18162    #[test]
18163    fn validate_restart_window_arm_routes_through_accessor() {
18164        // Composition pin: [`Caixa::validate_restart_window`]'s
18165        // shared-codec fold arm must key off [`Caixa::restart_window`],
18166        // not the raw `self.restart_window.as_deref()` field-projection.
18167        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
18168        // express no reset" canonical shape); (2) a canonical `Some`
18169        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
18170        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
18171        // .. })` carrying the offending raw string verbatim. The three
18172        // arms jointly pin that the validator's raw-string binding is
18173        // the accessor's return, not a peer projection — any future
18174        // silent detour that had the accessor collapse `Some("")` to
18175        // `None` would silently absorb the empty-after-trim refusal
18176        // case at the accessor boundary.
18177        caixa_with_restart_window(None)
18178            .validate_restart_window()
18179            .expect("None :restart-window must validate through the accessor");
18180        caixa_with_restart_window(Some("60s"))
18181            .validate_restart_window()
18182            .expect("canonical :restart-window \"60s\" must validate through the accessor");
18183        let err = caixa_with_restart_window(Some("1.5s"))
18184            .validate_restart_window()
18185            .expect_err("fractional-seconds :restart-window must fail through the accessor");
18186        assert!(
18187            matches!(
18188                err,
18189                ManifestError::RestartWindowMalformed { ref restart_window, .. }
18190                    if restart_window == "1.5s"
18191            ),
18192            "validator must carry the offending raw string verbatim \
18193             from the accessor's borrowed &str (got {err:?})",
18194        );
18195    }
18196
18197    #[test]
18198    fn supervisor_view_restart_window_arm_routes_through_accessor() {
18199        // Composition pin: [`Caixa::supervisor_view`]'s
18200        // per-`:restart-window` [`SupervisorSpec`] construction arm
18201        // must key off [`Caixa::restart_window`]'s soft-swallowing
18202        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
18203        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
18204        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
18205        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
18206        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
18207        // (the shared codec's canonical parse); (3) codec-rejected
18208        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
18209        // (the soft-swallow preserving the view's best-effort shape).
18210        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18211        let view = c.supervisor_view().expect("Supervisor kind has a view");
18212        assert_eq!(
18213            view.restart_window(),
18214            None,
18215            "supervisor_view must project outer None :restart-window \
18216             onto None on the composed SupervisorSpec (never-reset \
18217             sentinel) through the accessor's None-return unchanged",
18218        );
18219
18220        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
18221        let view = c.supervisor_view().expect("Supervisor kind has a view");
18222        assert_eq!(
18223            view.restart_window(),
18224            Some(std::time::Duration::from_secs(60)),
18225            "supervisor_view must fold outer Some(\"60s\") through the \
18226             shared duration_codec into Duration::from_secs(60) on the \
18227             composed SupervisorSpec (accessor's Some(&str) → codec \
18228             parse → Some(Duration))",
18229        );
18230
18231        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
18232        let view = c.supervisor_view().expect("Supervisor kind has a view");
18233        assert_eq!(
18234            view.restart_window(),
18235            None,
18236            "supervisor_view must soft-swallow the shared-codec parse \
18237             failure to None (the view's best-effort shape the sibling \
18238             manifest-level validate_restart_window surfaces as \
18239             RestartWindowMalformed); the accessor's raw-string return \
18240             is the single input every downstream consumer keys off",
18241        );
18242    }
18243
18244    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
18245
18246    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
18247        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18248        c.upgrade_from = upgrade_from;
18249        c
18250    }
18251
18252    #[test]
18253    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
18254        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
18255        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
18256        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
18257        // typed `Vec<UpgradeFromEntry>` verbatim as a
18258        // `&[UpgradeFromEntry]` slice-view over the same backing
18259        // buffer the raw `self.upgrade_from.as_slice()` field access
18260        // borrows from, element-equal across every representative
18261        // fixture in the accept-set — `[]` (the "no hot-upgrade path
18262        // declared" arm every `defcaixa` without an `:upgrade-from`
18263        // block carries; `#[serde(default)]` folds an omitted slot
18264        // onto `Vec::new()`), a canonical single-entry `Restart`
18265        // fixture (the shape most Servicos carry — a single prior
18266        // version with the fallback strategy), a canonical multi-
18267        // entry list carrying every typed instruction variant
18268        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
18269        // `Restart`), and a past-the-guard sentinel — a duplicate-
18270        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
18271        // ([`crate::upgrade::validate_upgrade_from`] rejects through
18272        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
18273        // ship the raw slot verbatim so struct-literal fixtures
18274        // continue to expose the duplicate at the accessor boundary).
18275        //
18276        // Pins against a future silent detour that returned an owned
18277        // `Vec<UpgradeFromEntry>` (which would type-check but silently
18278        // clone on every accessor call, breaking the zero-cost
18279        // projection every peer sibling slice accessor carries), a
18280        // `[dup, dup] → [dup]` dedup collapse (which would silently
18281        // absorb the `DuplicateFrom` refusal case at the accessor
18282        // boundary and the [`crate::StandardLayout::verify`] cross-
18283        // entry gate would silently accept a struct-literal `Caixa`
18284        // carrying the drift), a reference to an operator-resolved
18285        // overlay (the future per-cluster `:upgrade-overrides` slot
18286        // — its resolution must land at exactly this accessor body,
18287        // not silently divert the raw slot away from a second
18288        // consumer), or an axis-shuffled projection (a future detour
18289        // that reordered entries through the accessor would silently
18290        // split the paired [`crate::StandardLayout::verify`] per-
18291        // `:upgrade-from` shape gate's traversal input from the peer
18292        // [`crate::render::servico_m2_overlay`] emitter's projection
18293        // input, since the operator's hot-upgrade dispatch matches
18294        // per-`:from` and axis reordering would silently split the
18295        // per-entry script-path existence probe's iteration order
18296        // from the M2 overlay emitter's serialized-entry order).
18297        //
18298        // First outer top-level [`Caixa`] `&[Composite]`-return
18299        // slice accessor pin on the substrate primitive for M2 / M3
18300        // typed-slot vec-carry axes — opens the outer-`Caixa`
18301        // `&[Composite]` composite-slice projection pattern the
18302        // sibling `:children` [`crate::supervisor::ChildSpec`] /
18303        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
18304        // [`crate::aplicacao::WitContract`] future outer-composite-
18305        // slice pins fold on. Peer of the closed outer-`Caixa`
18306        // scalar `Option<&Composite>` composite-reference family the
18307        // sibling `limits` / `behavior` / `politicas` / `placement`
18308        // / `entrada` `..._returns_..._option_ref_verbatim_across_
18309        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
18310        // the "byte-equal, borrow-shared" outer-accessor discipline
18311        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
18312        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18313        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
18314            vec![],
18315            vec![UpgradeFromEntry {
18316                from: "0.0.1".into(),
18317                instructions: vec![UpgradeInstruction::Restart],
18318            }],
18319            vec![
18320                UpgradeFromEntry {
18321                    from: "0.0.1".into(),
18322                    instructions: vec![
18323                        UpgradeInstruction::LoadModule {
18324                            module: "demo".into(),
18325                        },
18326                        UpgradeInstruction::SoftPurge {
18327                            module: "demo".into(),
18328                        },
18329                    ],
18330                },
18331                UpgradeFromEntry {
18332                    from: "0.0.2".into(),
18333                    instructions: vec![
18334                        UpgradeInstruction::StateChange {
18335                            script: "servicos/upgrade.lisp".into(),
18336                        },
18337                        UpgradeInstruction::Purge {
18338                            module: "demo".into(),
18339                        },
18340                        UpgradeInstruction::Restart,
18341                    ],
18342                },
18343            ],
18344            vec![
18345                UpgradeFromEntry {
18346                    from: "0.1.0".into(),
18347                    instructions: vec![UpgradeInstruction::Restart],
18348                },
18349                UpgradeFromEntry {
18350                    from: "0.1.0".into(),
18351                    instructions: vec![UpgradeInstruction::Restart],
18352                },
18353            ],
18354        ];
18355        for upgrade_from in fixtures {
18356            let c = caixa_with_upgrade_from(upgrade_from.clone());
18357            assert_eq!(
18358                c.upgrade_from(),
18359                upgrade_from.as_slice(),
18360                "Caixa::upgrade_from must return :upgrade-from \
18361                 verbatim (got {:?}, expected {upgrade_from:?})",
18362                c.upgrade_from(),
18363            );
18364            assert_eq!(
18365                c.upgrade_from(),
18366                c.upgrade_from.as_slice(),
18367                "Caixa::upgrade_from must element-equal the raw \
18368                 `self.upgrade_from.as_slice()` field access across \
18369                 every value in the Vec<UpgradeFromEntry> accept-set",
18370            );
18371            assert_eq!(
18372                c.upgrade_from().is_empty(),
18373                c.upgrade_from.is_empty(),
18374                "Caixa::upgrade_from().is_empty() must byte-equal \
18375                 self.upgrade_from.is_empty() — a presence-bit drift \
18376                 would silently split the paired \
18377                 Caixa::declared_servico_slots M2 declared-slot \
18378                 enumerator's presence probe from the peer \
18379                 crate::render::servico_m2_overlay M2 overlay \
18380                 emitter's presence gate",
18381            );
18382        }
18383    }
18384
18385    #[test]
18386    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
18387        // Composition pin: [`Caixa::declared_servico_slots`]'s
18388        // `:upgrade-from` presence-probe arm must key off
18389        // [`Caixa::upgrade_from`], not the raw
18390        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
18391        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
18392        // instructions: vec![Restart] }], .. }` must push
18393        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
18394        // (the presence bit is non-empty, so the M2 kind-coherence
18395        // gate must surface the slot as "declared"), and a `Caixa {
18396        // upgrade_from: vec![], .. }` must NOT push the label (the
18397        // "author omitted the slot entirely" arm — the empty-slice
18398        // partition the serde-default folds onto). The pair jointly
18399        // pins the accessor + declared-slot enumerator composition:
18400        // any future silent detour that had the accessor collapse
18401        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
18402        // is_empty())` projection) would silently absorb the
18403        // "declared but degenerate" arm at the accessor boundary and
18404        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
18405        // coherence gate would silently accept a struct-literal
18406        // `Caixa` carrying the drift.
18407        //
18408        // Peer of the sibling
18409        // `declared_servico_slots_limits_arm_routes_through_accessor`
18410        // (b2bd9d7) and
18411        // `declared_servico_slots_behavior_arm_routes_through_accessor`
18412        // (35d8b52) composition pins on the sibling `:limits` /
18413        // `:behavior` outer-`Option<&Composite>` arms — same "the
18414        // enumerator gate must route through the substrate-primitive
18415        // typed dispatch" discipline extended onto the third M2
18416        // Servico-runtime slot axis, closing the enumerator's routing
18417        // invariant on every M2 arm.
18418        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18419        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18420            from: "0.0.1".into(),
18421            instructions: vec![UpgradeInstruction::Restart],
18422        }]);
18423        let slots = c.declared_servico_slots();
18424        assert!(
18425            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18426            "declared_servico_slots must push \
18427             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18428             non-empty — the accessor and the enumerator gate must \
18429             route through the same substrate-primitive typed \
18430             dispatch on the outer :upgrade-from presence bit (got \
18431             slots={slots:?})",
18432        );
18433        let c = caixa_with_upgrade_from(vec![]);
18434        let slots = c.declared_servico_slots();
18435        assert!(
18436            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18437            "declared_servico_slots must NOT push \
18438             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18439             empty — the author-omitted arm must route through the \
18440             accessor's empty-slice return unchanged (got \
18441             slots={slots:?})",
18442        );
18443    }
18444
18445    #[test]
18446    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
18447        // Composition pin: [`crate::render::servico_m2_overlay`]'s
18448        // per-`:upgrade-from` M2 overlay emit arm must key off
18449        // [`Caixa::upgrade_from`], not the raw
18450        // `!caixa.upgrade_from.is_empty()` presence gate + the
18451        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
18452        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
18453        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
18454        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
18455        // sequence in the overlay (the emitter fans onto the serde
18456        // slice-serialization), and a `Caixa { upgrade_from: vec![],
18457        // .. }` must omit the key entirely (the empty-slice
18458        // partition — the `!.is_empty()` outer gate elides the key
18459        // when the author omitted the slot). The pair jointly pins
18460        // the accessor + M2 overlay emitter composition: any future
18461        // silent detour that had the accessor return a fresh-cloned
18462        // `Vec<UpgradeFromEntry>` copy would silently break the
18463        // reference-identity pin the peer per-entry
18464        // `serde_yaml::to_value(caixa.upgrade_from())` projection
18465        // reads from — the projection would clone once per accessor
18466        // call instead of borrowing the storage buffer verbatim.
18467        //
18468        // Peer of the sibling
18469        // `servico_m2_overlay_limits_arm_routes_through_accessor`
18470        // (b2bd9d7) and
18471        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
18472        // (35d8b52) composition pins on the sibling `:limits` /
18473        // `:behavior` outer-`Option<&Composite>` arms — same "the
18474        // M2 overlay emitter must route through the substrate-
18475        // primitive typed dispatch" discipline extended onto the
18476        // third M2 Servico-runtime slot axis, closing the overlay
18477        // emitter's routing invariant on every M2 arm.
18478        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
18479        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18480        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18481            from: "0.0.1".into(),
18482            instructions: vec![UpgradeInstruction::Restart],
18483        }]);
18484        let overlay = servico_m2_overlay(&c).unwrap();
18485        assert!(
18486            overlay.contains_key(M2_KEY_UPGRADE_FROM),
18487            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
18488             `:upgrade-from` is non-empty — the accessor and the M2 \
18489             overlay emitter must route through the same substrate- \
18490             primitive typed dispatch on the outer :upgrade-from \
18491             slice (got overlay={overlay:?})",
18492        );
18493        let c = caixa_with_upgrade_from(vec![]);
18494        let overlay = servico_m2_overlay(&c).unwrap();
18495        assert!(
18496            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
18497            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
18498             `:upgrade-from` is empty — the empty-slice partition \
18499             must route through the accessor's empty-slice return \
18500             unchanged (got overlay={overlay:?})",
18501        );
18502    }
18503
18504    #[test]
18505    fn upgrade_from_projects_slice_by_borrow() {
18506        // The by-borrow pin: [`Caixa::upgrade_from`] returns
18507        // `&[UpgradeFromEntry]` by borrow — the returned slice
18508        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
18509        // the `:upgrade-from` slot and the accessor must not clone
18510        // the backing `Vec` on every call. Peer of the sibling
18511        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
18512        // (`autores_projects_slice_by_borrow` b5d813f,
18513        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18514        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18515        // `exe_projects_slice_by_borrow` 65d9527,
18516        // `servicos_projects_slice_by_borrow` 611f78b,
18517        // `deps_projects_slice_by_borrow` ad34b4e,
18518        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
18519        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
18520        // axes — extended here to the first outer-`Caixa`
18521        // composite-element `&[Composite]` axis: the accessor's
18522        // returned slice must borrow from `&self` (the returned
18523        // reference's lifetime is tied to `&self`), and calling the
18524        // accessor twice on the same [`Caixa`] must yield slices
18525        // that are pointer-equal (the underlying byte-buffer is the
18526        // storage `Vec`'s allocation, not a fresh copy) as well as
18527        // value-equal (idempotent, no side effects on `&self`).
18528        //
18529        // Pins against a future silent detour that returned an owned
18530        // `Vec<UpgradeFromEntry>` (which would type-check but
18531        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
18532        // return (which would leak the backing `Vec`'s
18533        // grow/push/reserve surface no downstream consumer reaches
18534        // for), or a one-arm-only accessor that returned a
18535        // saturating value on some sentinel input.
18536        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18537        for upgrade_from in [
18538            vec![],
18539            vec![UpgradeFromEntry {
18540                from: "0.0.1".into(),
18541                instructions: vec![UpgradeInstruction::Restart],
18542            }],
18543            vec![
18544                UpgradeFromEntry {
18545                    from: "0.0.1".into(),
18546                    instructions: vec![UpgradeInstruction::Restart],
18547                },
18548                UpgradeFromEntry {
18549                    from: "0.0.2".into(),
18550                    instructions: vec![UpgradeInstruction::SoftPurge {
18551                        module: "demo".into(),
18552                    }],
18553                },
18554            ],
18555        ] {
18556            let c = caixa_with_upgrade_from(upgrade_from.clone());
18557            let first = c.upgrade_from();
18558            let second = c.upgrade_from();
18559            assert_eq!(
18560                first, second,
18561                "Caixa::upgrade_from must be idempotent — two \
18562                 successive calls on the same &self must return the \
18563                 same &[UpgradeFromEntry]",
18564            );
18565            assert_eq!(
18566                first.as_ptr(),
18567                second.as_ptr(),
18568                "Caixa::upgrade_from must borrow the underlying \
18569                 Vec<UpgradeFromEntry> storage — two successive calls \
18570                 must return slices with the same backing pointer (a \
18571                 fresh Vec<UpgradeFromEntry> clone would change the \
18572                 pointer on every call)",
18573            );
18574            assert_eq!(
18575                first,
18576                upgrade_from.as_slice(),
18577                "Caixa::upgrade_from must return :upgrade-from \
18578                 verbatim by borrow — got {first:?}, expected \
18579                 {upgrade_from:?}",
18580            );
18581        }
18582    }
18583
18584    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
18585
18586    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
18587        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18588        c.children = children;
18589        c
18590    }
18591
18592    #[test]
18593    fn children_returns_children_slice_verbatim_across_permutations() {
18594        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
18595        // outer-composite `&[ChildSpec]`-return slice-shape pin:
18596        // [`Caixa::children`] must return the `:children` typed
18597        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
18598        // the same backing buffer the raw `self.children.as_slice()`
18599        // field access borrows from, element-equal across every
18600        // representative fixture in the accept-set — `[]` (the "no
18601        // static children declared" arm every non-`Supervisor`-kind
18602        // `defcaixa` carries by `#[serde(default)]` and every
18603        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
18604        // a canonical single-child `Permanent` fixture (the shape
18605        // most `OneForOne` supervisors carry — a single long-running
18606        // worker child), a canonical multi-child list carrying every
18607        // typed restart-policy variant (`Permanent` / `Transient` /
18608        // `Temporary`), and a past-the-guard sentinel — a duplicate
18609        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
18610        // ([`crate::SupervisorSpec::validate`] rejects through
18611        // `DuplicateChildNome { nome: "w" }` but the accessor must
18612        // ship the raw slot verbatim so struct-literal fixtures
18613        // continue to expose the duplicate at the accessor boundary).
18614        //
18615        // Pins against a future silent detour that returned an owned
18616        // `Vec<ChildSpec>` (which would type-check but silently clone
18617        // on every accessor call, breaking the zero-cost projection
18618        // every peer sibling slice accessor carries), a `[dup, dup] →
18619        // [dup]` dedup collapse (which would silently absorb the
18620        // `DuplicateChildNome` refusal case at the accessor boundary
18621        // and the [`crate::StandardLayout::verify`] cross-child gate
18622        // would silently accept a struct-literal `Caixa` carrying the
18623        // drift), a reference to an operator-resolved overlay (the
18624        // future per-cluster `:children-overrides` slot — its
18625        // resolution must land at exactly this accessor body, not
18626        // silently divert the raw slot away from a second consumer),
18627        // or an axis-shuffled projection (a future detour that
18628        // reordered children through the accessor would silently
18629        // split the paired [`crate::StandardLayout::verify`] per-
18630        // supervisor gate's traversal input from the peer
18631        // [`Self::supervisor_view`] fold-in path's clone-order input,
18632        // since the OTP `RestForOne` restart strategy dispatches on
18633        // declared child order and axis reordering would silently
18634        // split the operator's per-cluster restart-fan-out order
18635        // from the caixa.lisp source-order).
18636        //
18637        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
18638        // accessor pin on the substrate primitive for M2 / M3 typed-
18639        // slot vec-carry axes — folds on the outer-`Caixa`
18640        // `&[Composite]` composite-slice sub-family the sibling
18641        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18642        // (2a1f907) pin opened, peer at the outer altitude of the
18643        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
18644        // (bc92bce) accessor on the same OTP-supervisor static-child-
18645        // list axis.
18646        use crate::supervisor::{ChildSpec, RestartPolicy};
18647        let fixtures: Vec<Vec<ChildSpec>> = vec![
18648            vec![],
18649            vec![ChildSpec {
18650                caixa: "worker".into(),
18651                versao: "^0.1".into(),
18652                restart: RestartPolicy::Permanent,
18653            }],
18654            vec![
18655                ChildSpec {
18656                    caixa: "worker-a".into(),
18657                    versao: "^0.1".into(),
18658                    restart: RestartPolicy::Permanent,
18659                },
18660                ChildSpec {
18661                    caixa: "worker-b".into(),
18662                    versao: "^0.1".into(),
18663                    restart: RestartPolicy::Transient,
18664                },
18665                ChildSpec {
18666                    caixa: "worker-c".into(),
18667                    versao: "^0.1".into(),
18668                    restart: RestartPolicy::Temporary,
18669                },
18670            ],
18671            vec![
18672                ChildSpec {
18673                    caixa: "w".into(),
18674                    versao: "^0.1".into(),
18675                    restart: RestartPolicy::Permanent,
18676                },
18677                ChildSpec {
18678                    caixa: "w".into(),
18679                    versao: "^0.1".into(),
18680                    restart: RestartPolicy::Permanent,
18681                },
18682            ],
18683        ];
18684        for children in fixtures {
18685            let c = caixa_with_children(children.clone());
18686            assert_eq!(
18687                c.children(),
18688                children.as_slice(),
18689                "Caixa::children must return :children verbatim \
18690                 (got {:?}, expected {children:?})",
18691                c.children(),
18692            );
18693            assert_eq!(
18694                c.children(),
18695                c.children.as_slice(),
18696                "Caixa::children must element-equal the raw \
18697                 `self.children.as_slice()` field access across \
18698                 every value in the Vec<ChildSpec> accept-set",
18699            );
18700            assert_eq!(
18701                c.children().is_empty(),
18702                c.children.is_empty(),
18703                "Caixa::children().is_empty() must byte-equal \
18704                 self.children.is_empty() — a presence-bit drift \
18705                 would silently split the paired \
18706                 Caixa::declared_supervisor_slots supervisor-tree \
18707                 declared-slot enumerator's presence probe from the \
18708                 peer Caixa::supervisor_view typed-view composer's \
18709                 fold-in path",
18710            );
18711        }
18712    }
18713
18714    #[test]
18715    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
18716        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18717        // `:children` presence-probe arm must key off
18718        // [`Caixa::children`], not the raw
18719        // `!self.children.is_empty()` field-probe. Structurally: a
18720        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
18721        // "^0.1", restart: Permanent }], .. }` must push
18722        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
18723        // (the presence bit is non-empty, so the supervisor-tree
18724        // kind-coherence gate must surface the slot as "declared"),
18725        // and a `Caixa { children: vec![], .. }` must NOT push the
18726        // label (the "author omitted the slot entirely" arm — the
18727        // empty-slice partition the serde-default folds onto). The
18728        // pair jointly pins the accessor + declared-slot enumerator
18729        // composition: any future silent detour that had the accessor
18730        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
18731        // "__reserved__")` projection) would silently absorb the
18732        // "declared but degenerate" arm at the accessor boundary and
18733        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
18734        // kind-coherence gate would silently accept a struct-literal
18735        // `Caixa` carrying the drift.
18736        //
18737        // Peer of the sibling
18738        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18739        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
18740        // same "the enumerator gate must route through the substrate-
18741        // primitive typed dispatch" discipline extended onto the
18742        // supervisor-tree `:children` composite-slice arm.
18743        use crate::supervisor::{ChildSpec, RestartPolicy};
18744        let c = caixa_with_children(vec![ChildSpec {
18745            caixa: "w".into(),
18746            versao: "^0.1".into(),
18747            restart: RestartPolicy::Permanent,
18748        }]);
18749        let slots = c.declared_supervisor_slots();
18750        assert!(
18751            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
18752            "declared_supervisor_slots must push \
18753             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
18754             non-empty — the accessor and the enumerator gate must \
18755             route through the same substrate-primitive typed \
18756             dispatch on the outer :children presence bit (got \
18757             slots={slots:?})",
18758        );
18759        let c = caixa_with_children(vec![]);
18760        let slots = c.declared_supervisor_slots();
18761        assert!(
18762            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
18763            "declared_supervisor_slots must NOT push \
18764             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
18765             empty — the author-omitted arm must route through the \
18766             accessor's empty-slice return unchanged (got \
18767             slots={slots:?})",
18768        );
18769    }
18770
18771    #[test]
18772    fn supervisor_view_children_arm_routes_through_accessor() {
18773        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
18774        // fold-in arm must key off [`Caixa::children`], not the raw
18775        // `self.children.clone()` field-clone. Structurally: a `Caixa {
18776        // kind: Supervisor, estrategia: Some(OneForOne), children:
18777        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
18778        // per-child list through the accessor into the typed
18779        // [`SupervisorSpec`] view's `children` field verbatim — every
18780        // entry the accessor surfaces must land in the view's
18781        // `children` slot in the same order. The pair jointly pins the
18782        // accessor + view-composer composition: any future silent
18783        // detour that had the accessor return a fresh-cloned
18784        // `Vec<ChildSpec>` copy would silently break the reference-
18785        // identity pin the peer `supervisor_view` fold-in path reads
18786        // from — the fold would clone once more per accessor call
18787        // instead of borrowing the storage buffer verbatim once.
18788        //
18789        // Peer of the sibling
18790        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
18791        // family) composition pin on the peer kind-gate arm — same
18792        // "the view composer must route through the substrate-
18793        // primitive typed dispatch" discipline extended onto the
18794        // per-`:children` fold-in arm, closing the supervisor-view
18795        // composer's routing invariant on the composite-slice input.
18796        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
18797        let mut c = caixa_with_children(vec![
18798            ChildSpec {
18799                caixa: "worker-a".into(),
18800                versao: "^0.1".into(),
18801                restart: RestartPolicy::Permanent,
18802            },
18803            ChildSpec {
18804                caixa: "worker-b".into(),
18805                versao: "^0.1".into(),
18806                restart: RestartPolicy::Transient,
18807            },
18808        ]);
18809        c.kind = crate::CaixaKind::Supervisor;
18810        c.estrategia = Some(RestartStrategy::OneForOne);
18811        let view = c
18812            .supervisor_view()
18813            .expect("Supervisor kind must produce a supervisor_view");
18814        assert_eq!(
18815            view.children(),
18816            c.children(),
18817            "supervisor_view must fold Caixa::children verbatim into \
18818             SupervisorSpec::children — the accessor and the view \
18819             composer must route through the same substrate-primitive \
18820             typed dispatch on the outer :children slice (got view \
18821             children={:?}, expected {:?})",
18822            view.children(),
18823            c.children(),
18824        );
18825    }
18826
18827    #[test]
18828    fn children_projects_slice_by_borrow() {
18829        // The by-borrow pin: [`Caixa::children`] returns
18830        // `&[ChildSpec]` by borrow — the returned slice borrows the
18831        // underlying `Vec<ChildSpec>` storage of the `:children` slot
18832        // and the accessor must not clone the backing `Vec` on every
18833        // call. Peer of the sibling outer top-level [`Caixa`]
18834        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
18835        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
18836        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18837        // `exe_projects_slice_by_borrow` 65d9527,
18838        // `servicos_projects_slice_by_borrow` 611f78b,
18839        // `deps_projects_slice_by_borrow` ad34b4e,
18840        // `deps_dev_projects_slice_by_borrow` f7fd81e,
18841        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
18842        // sibling outer top-level [`Caixa`] scalar-element and
18843        // composite-element `&[T]` axes — folds on the outer-`Caixa`
18844        // composite-element `&[Composite]` axis: the accessor's
18845        // returned slice must borrow from `&self` (the returned
18846        // reference's lifetime is tied to `&self`), and calling the
18847        // accessor twice on the same [`Caixa`] must yield slices
18848        // that are pointer-equal (the underlying byte-buffer is the
18849        // storage `Vec`'s allocation, not a fresh copy) as well as
18850        // value-equal (idempotent, no side effects on `&self`).
18851        //
18852        // Pins against a future silent detour that returned an owned
18853        // `Vec<ChildSpec>` (which would type-check but silently clone
18854        // on every call), a `&Vec<ChildSpec>` return (which would leak
18855        // the backing `Vec`'s grow/push/reserve surface no downstream
18856        // consumer reaches for), or a one-arm-only accessor that
18857        // returned a saturating value on some sentinel input.
18858        use crate::supervisor::{ChildSpec, RestartPolicy};
18859        for children in [
18860            vec![],
18861            vec![ChildSpec {
18862                caixa: "w".into(),
18863                versao: "^0.1".into(),
18864                restart: RestartPolicy::Permanent,
18865            }],
18866            vec![
18867                ChildSpec {
18868                    caixa: "worker-a".into(),
18869                    versao: "^0.1".into(),
18870                    restart: RestartPolicy::Permanent,
18871                },
18872                ChildSpec {
18873                    caixa: "worker-b".into(),
18874                    versao: "^0.1".into(),
18875                    restart: RestartPolicy::Transient,
18876                },
18877            ],
18878        ] {
18879            let c = caixa_with_children(children.clone());
18880            let first = c.children();
18881            let second = c.children();
18882            assert_eq!(
18883                first, second,
18884                "Caixa::children must be idempotent — two successive \
18885                 calls on the same &self must return the same \
18886                 &[ChildSpec]",
18887            );
18888            assert_eq!(
18889                first.as_ptr(),
18890                second.as_ptr(),
18891                "Caixa::children must borrow the underlying \
18892                 Vec<ChildSpec> storage — two successive calls must \
18893                 return slices with the same backing pointer (a fresh \
18894                 Vec<ChildSpec> clone would change the pointer on \
18895                 every call)",
18896            );
18897            assert_eq!(
18898                first,
18899                children.as_slice(),
18900                "Caixa::children must return :children verbatim by \
18901                 borrow — got {first:?}, expected {children:?}",
18902            );
18903        }
18904    }
18905
18906    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
18907
18908    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
18909        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18910        c.kind = CaixaKind::Aplicacao;
18911        c.membros = membros;
18912        c
18913    }
18914
18915    #[test]
18916    fn membros_returns_membros_slice_verbatim_across_permutations() {
18917        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
18918        // composite `&[Membro]`-return slice-shape pin:
18919        // [`Caixa::membros`] must return the `:membros` typed
18920        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
18921        // same backing buffer the raw `self.membros.as_slice()` field
18922        // access borrows from, element-equal across every
18923        // representative fixture in the accept-set — `[]` (the "no
18924        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
18925        // carries by `#[serde(default)]` and every partially-authored
18926        // Aplicacao carries before the
18927        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
18928        // canonical single-member fixture (the shape a minimal
18929        // Aplicacao carries — one Servico wrapping one contained
18930        // computation), a canonical multi-member list carrying three
18931        // distinct entries (the canonical checkout-shape Aplicacao —
18932        // cart / pricing / auth — every canonical example carries), and
18933        // a past-the-guard sentinel — a duplicate `:caixa`
18934        // `[("cart", ...), ("cart", ...)]` entry pair
18935        // ([`crate::AplicacaoSpec::validate`] rejects through
18936        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
18937        // the raw slot verbatim so struct-literal fixtures continue to
18938        // expose the duplicate at the accessor boundary).
18939        //
18940        // Pins against a future silent detour that returned an owned
18941        // `Vec<Membro>` (which would type-check but silently clone on
18942        // every accessor call, breaking the zero-cost projection every
18943        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
18944        // dedup collapse (which would silently absorb the
18945        // `DuplicateMembro` refusal case at the accessor boundary and
18946        // the [`crate::StandardLayout::verify`] cross-member gate would
18947        // silently accept a struct-literal `Caixa` carrying the drift),
18948        // a reference to an operator-resolved overlay (the future per-
18949        // cluster `:membros-overrides` slot — its resolution must land
18950        // at exactly this accessor body, not silently divert the raw
18951        // slot away from a second consumer), or an axis-shuffled
18952        // projection (a future detour that reordered members through
18953        // the accessor would silently split the paired
18954        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
18955        // traversal input from the peer [`Self::aplicacao_view`] fold-
18956        // in path's clone-order input, since the canonical `:contratos`
18957        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
18958        // read the member set through the same slice).
18959        //
18960        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
18961        // accessor pin on the substrate primitive for M2 / M3 typed-
18962        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
18963        // arm of the `&[Composite]` composite-slice sub-family the
18964        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18965        // (2a1f907) and
18966        // `children_returns_children_slice_verbatim_across_permutations`
18967        // (c17b51e) pins opened, peer at the outer altitude of the
18968        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
18969        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
18970        // list axis.
18971        use crate::aplicacao::Membro;
18972        let fixtures: Vec<Vec<Membro>> = vec![
18973            vec![],
18974            vec![Membro {
18975                caixa: "cart".into(),
18976                versao: "^0.1".into(),
18977            }],
18978            vec![
18979                Membro {
18980                    caixa: "cart".into(),
18981                    versao: "^0.1".into(),
18982                },
18983                Membro {
18984                    caixa: "pricing".into(),
18985                    versao: "^0.2".into(),
18986                },
18987                Membro {
18988                    caixa: "auth".into(),
18989                    versao: "^1.0".into(),
18990                },
18991            ],
18992            vec![
18993                Membro {
18994                    caixa: "cart".into(),
18995                    versao: "^0.1".into(),
18996                },
18997                Membro {
18998                    caixa: "cart".into(),
18999                    versao: "^0.1".into(),
19000                },
19001            ],
19002        ];
19003        for membros in fixtures {
19004            let c = caixa_aplicacao_with_membros(membros.clone());
19005            assert_eq!(
19006                c.membros(),
19007                membros.as_slice(),
19008                "Caixa::membros must return :membros verbatim \
19009                 (got {:?}, expected {membros:?})",
19010                c.membros(),
19011            );
19012            assert_eq!(
19013                c.membros(),
19014                c.membros.as_slice(),
19015                "Caixa::membros must element-equal the raw \
19016                 `self.membros.as_slice()` field access across every \
19017                 value in the Vec<Membro> accept-set",
19018            );
19019            assert_eq!(
19020                c.membros().is_empty(),
19021                c.membros.is_empty(),
19022                "Caixa::membros().is_empty() must byte-equal \
19023                 self.membros.is_empty() — a presence-bit drift would \
19024                 silently split the paired Caixa::declared_mesh_slots \
19025                 mesh declared-slot enumerator's presence probe from \
19026                 the peer Caixa::aplicacao_view typed-view composer's \
19027                 fold-in path",
19028            );
19029        }
19030    }
19031
19032    #[test]
19033    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
19034        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
19035        // presence-probe arm must key off [`Caixa::membros`], not the
19036        // raw `!self.membros.is_empty()` field-probe. Structurally: a
19037        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
19038        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
19039        // declared-slot list (the presence bit is non-empty, so the
19040        // mesh kind-coherence gate must surface the slot as
19041        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
19042        // push the label (the "author omitted the slot entirely" arm
19043        // — the empty-slice partition the serde-default folds onto).
19044        // The pair jointly pins the accessor + declared-slot
19045        // enumerator composition: any future silent detour that had
19046        // the accessor collapse `[Membro { .. }]` to `[]` (a
19047        // `.filter(|m| m.nome() != "__reserved__")` projection) would
19048        // silently absorb the "declared but degenerate" arm at the
19049        // accessor boundary and the
19050        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19051        // coherence gate would silently accept a struct-literal
19052        // `Caixa` carrying the drift.
19053        //
19054        // Peer of the sibling
19055        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19056        // (2a1f907) and
19057        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19058        // (c17b51e) composition pins on the M2 `:upgrade-from` /
19059        // `:children` composite-slice arms — same "the enumerator gate
19060        // must route through the substrate-primitive typed dispatch"
19061        // discipline extended onto the M3 `:membros` composite-slice
19062        // arm, opening the M3 arm of the declared-slot enumerator's
19063        // routing invariant.
19064        use crate::aplicacao::Membro;
19065        let c = caixa_aplicacao_with_membros(vec![Membro {
19066            caixa: "cart".into(),
19067            versao: "^0.1".into(),
19068        }]);
19069        let slots = c.declared_mesh_slots();
19070        assert!(
19071            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19072            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
19073             `:membros` is non-empty — the accessor and the enumerator \
19074             gate must route through the same substrate-primitive \
19075             typed dispatch on the outer :membros presence bit (got \
19076             slots={slots:?})",
19077        );
19078        let c = caixa_aplicacao_with_membros(vec![]);
19079        let slots = c.declared_mesh_slots();
19080        assert!(
19081            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19082            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
19083             when `:membros` is empty — the author-omitted arm must \
19084             route through the accessor's empty-slice return unchanged \
19085             (got slots={slots:?})",
19086        );
19087    }
19088
19089    #[test]
19090    fn aplicacao_view_membros_arm_routes_through_accessor() {
19091        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
19092        // fold-in arm must key off [`Caixa::membros`], not the raw
19093        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
19094        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
19095        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
19096        // member list through the accessor into the typed
19097        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
19098        // every entry the accessor surfaces must land in the view's
19099        // `membros` slot in the same order. The pair jointly pins the
19100        // accessor + view-composer composition: any future silent
19101        // detour that had the accessor return a fresh-cloned
19102        // `Vec<Membro>` copy would silently break the reference-
19103        // identity pin the peer `aplicacao_view` fold-in path reads
19104        // from — the fold would clone once more per accessor call
19105        // instead of borrowing the storage buffer verbatim once.
19106        //
19107        // Peer of the sibling
19108        // `aplicacao_view_politicas_arm_folds_through_accessor`
19109        // (5d23d29) /
19110        // `aplicacao_view_placement_arm_folds_through_accessor`
19111        // (4fb8074) /
19112        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
19113        // composition pins on the M3 `:politicas` / `:placement` /
19114        // `:entrada` outer-`Option<&Composite>` arms — extended here to
19115        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
19116        // closing the aplicacao-view composer's routing invariant on
19117        // the composite-slice input.
19118        use crate::aplicacao::Membro;
19119        let c = caixa_aplicacao_with_membros(vec![
19120            Membro {
19121                caixa: "cart".into(),
19122                versao: "^0.1".into(),
19123            },
19124            Membro {
19125                caixa: "pricing".into(),
19126                versao: "^0.2".into(),
19127            },
19128        ]);
19129        let view = c
19130            .aplicacao_view()
19131            .expect("Aplicacao kind must produce an aplicacao_view");
19132        assert_eq!(
19133            view.membros(),
19134            c.membros(),
19135            "aplicacao_view must fold Caixa::membros verbatim into \
19136             AplicacaoSpec::membros — the accessor and the view \
19137             composer must route through the same substrate-primitive \
19138             typed dispatch on the outer :membros slice (got view \
19139             membros={:?}, expected {:?})",
19140            view.membros(),
19141            c.membros(),
19142        );
19143    }
19144
19145    #[test]
19146    fn membros_projects_slice_by_borrow() {
19147        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
19148        // borrow — the returned slice borrows the underlying
19149        // `Vec<Membro>` storage of the `:membros` slot and the
19150        // accessor must not clone the backing `Vec` on every call.
19151        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19152        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19153        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19154        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19155        // `exe_projects_slice_by_borrow` 65d9527,
19156        // `servicos_projects_slice_by_borrow` 611f78b,
19157        // `deps_projects_slice_by_borrow` ad34b4e,
19158        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19159        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19160        // `children_projects_slice_by_borrow` c17b51e) on the sibling
19161        // outer top-level [`Caixa`] scalar-element and composite-
19162        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
19163        // slot composite-element `&[Composite]` axis: the accessor's
19164        // returned slice must borrow from `&self` (the returned
19165        // reference's lifetime is tied to `&self`), and calling the
19166        // accessor twice on the same [`Caixa`] must yield slices that
19167        // are pointer-equal (the underlying byte-buffer is the storage
19168        // `Vec`'s allocation, not a fresh copy) as well as value-equal
19169        // (idempotent, no side effects on `&self`).
19170        //
19171        // Pins against a future silent detour that returned an owned
19172        // `Vec<Membro>` (which would type-check but silently clone on
19173        // every call), a `&Vec<Membro>` return (which would leak the
19174        // backing `Vec`'s grow/push/reserve surface no downstream
19175        // consumer reaches for), or a one-arm-only accessor that
19176        // returned a saturating value on some sentinel input.
19177        use crate::aplicacao::Membro;
19178        for membros in [
19179            vec![],
19180            vec![Membro {
19181                caixa: "cart".into(),
19182                versao: "^0.1".into(),
19183            }],
19184            vec![
19185                Membro {
19186                    caixa: "cart".into(),
19187                    versao: "^0.1".into(),
19188                },
19189                Membro {
19190                    caixa: "pricing".into(),
19191                    versao: "^0.2".into(),
19192                },
19193            ],
19194        ] {
19195            let c = caixa_aplicacao_with_membros(membros.clone());
19196            let first = c.membros();
19197            let second = c.membros();
19198            assert_eq!(
19199                first, second,
19200                "Caixa::membros must be idempotent — two successive \
19201                 calls on the same &self must return the same &[Membro]",
19202            );
19203            assert_eq!(
19204                first.as_ptr(),
19205                second.as_ptr(),
19206                "Caixa::membros must borrow the underlying Vec<Membro> \
19207                 storage — two successive calls must return slices with \
19208                 the same backing pointer (a fresh Vec<Membro> clone \
19209                 would change the pointer on every call)",
19210            );
19211            assert_eq!(
19212                first,
19213                membros.as_slice(),
19214                "Caixa::membros must return :membros verbatim by borrow \
19215                 — got {first:?}, expected {membros:?}",
19216            );
19217        }
19218    }
19219
19220    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
19221
19222    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
19223        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19224        c.kind = CaixaKind::Aplicacao;
19225        c.contratos = contratos;
19226        c
19227    }
19228
19229    fn contrato_http_for_test(
19230        de: &str,
19231        para: &str,
19232        endpoint: &str,
19233    ) -> crate::aplicacao::WitContract {
19234        crate::aplicacao::WitContract {
19235            de: de.into(),
19236            para: para.into(),
19237            wit: "wasi:http/proxy".into(),
19238            endpoint: Some(endpoint.into()),
19239            subject: None,
19240            slot: None,
19241        }
19242    }
19243
19244    #[test]
19245    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
19246        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
19247        // composite `&[WitContract]`-return slice-shape pin:
19248        // [`Caixa::contratos`] must return the `:contratos` typed
19249        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
19250        // over the same backing buffer the raw
19251        // `self.contratos.as_slice()` field access borrows from,
19252        // element-equal across every representative fixture in the
19253        // accept-set — `[]` (the "no contracts declared" arm every
19254        // non-`Aplicacao`-kind `defcaixa` carries by
19255        // `#[serde(default)]` and every leaf-Aplicacao with a single
19256        // member carries), a canonical single-edge fixture (the
19257        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
19258        // edge), and a canonical multi-edge fixture with three distinct
19259        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
19260        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
19261        //
19262        // Pins against a future silent detour that returned an owned
19263        // `Vec<WitContract>` (which would type-check but silently clone
19264        // on every accessor call, breaking the zero-cost projection
19265        // every peer sibling slice accessor carries), an axis-shuffled
19266        // projection (a future detour that reordered edges through the
19267        // accessor would silently split the paired
19268        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19269        // traversal input from the peer [`Self::aplicacao_view`] fold-
19270        // in path's clone-order input, since every canonical
19271        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
19272        // seed dispatch reads the edge set through the same slice),
19273        // or a reference to an operator-resolved overlay (the future
19274        // per-cluster `:contratos-overrides` slot — its resolution
19275        // must land at exactly this accessor body, not silently divert
19276        // the raw slot away from a second consumer).
19277        //
19278        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
19279        // accessor pin on the substrate primitive for M2 / M3 typed-
19280        // slot vec-carry axes — closes the outer-`Caixa`
19281        // `&[Composite]` composite-slice sub-family the sibling M2
19282        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19283        // (2a1f907) and
19284        // `children_returns_children_slice_verbatim_across_permutations`
19285        // (c17b51e) pins opened and the M3
19286        // `membros_returns_membros_slice_verbatim_across_permutations`
19287        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
19288        // slot arm of the composite-slice sub-family. Peer at the outer
19289        // altitude of the closed inner-
19290        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
19291        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
19292        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
19293            vec![],
19294            vec![contrato_http_for_test("cart", "catalog", "/items")],
19295            vec![
19296                contrato_http_for_test("cart", "catalog", "/items"),
19297                contrato_http_for_test("cart", "pricing", "/price"),
19298                contrato_http_for_test("cart", "auth", "/whoami"),
19299            ],
19300        ];
19301        for contratos in fixtures {
19302            let c = caixa_aplicacao_with_contratos(contratos.clone());
19303            assert_eq!(
19304                c.contratos(),
19305                contratos.as_slice(),
19306                "Caixa::contratos must return :contratos verbatim \
19307                 (got {:?}, expected {contratos:?})",
19308                c.contratos(),
19309            );
19310            assert_eq!(
19311                c.contratos(),
19312                c.contratos.as_slice(),
19313                "Caixa::contratos must element-equal the raw \
19314                 `self.contratos.as_slice()` field access across every \
19315                 value in the Vec<WitContract> accept-set",
19316            );
19317            assert_eq!(
19318                c.contratos().is_empty(),
19319                c.contratos.is_empty(),
19320                "Caixa::contratos().is_empty() must byte-equal \
19321                 self.contratos.is_empty() — a presence-bit drift would \
19322                 silently split the paired Caixa::declared_mesh_slots \
19323                 mesh declared-slot enumerator's presence probe from \
19324                 the peer Caixa::aplicacao_view typed-view composer's \
19325                 fold-in path",
19326            );
19327        }
19328    }
19329
19330    #[test]
19331    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
19332        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
19333        // presence-probe arm must key off [`Caixa::contratos`], not the
19334        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
19335        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
19336        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
19337        // presence bit is non-empty, so the mesh kind-coherence gate
19338        // must surface the slot as "declared"), and a `Caixa {
19339        // contratos: vec![], .. }` must NOT push the label (the "author
19340        // omitted the slot entirely" arm — the empty-slice partition
19341        // the serde-default folds onto). The pair jointly pins the
19342        // accessor + declared-slot enumerator composition: any future
19343        // silent detour that had the accessor collapse
19344        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
19345        // "__reserved__")` projection) would silently absorb the
19346        // "declared but degenerate" arm at the accessor boundary and
19347        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19348        // coherence gate would silently accept a struct-literal
19349        // `Caixa` carrying the drift.
19350        //
19351        // Peer of the sibling
19352        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19353        // (2a1f907),
19354        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19355        // (c17b51e), and
19356        // `declared_mesh_slots_membros_arm_routes_through_accessor`
19357        // (0f26987) composition pins on the M2 `:upgrade-from` /
19358        // `:children` / M3 `:membros` composite-slice arms — same "the
19359        // enumerator gate must route through the substrate-primitive
19360        // typed dispatch" discipline extended onto the M3 `:contratos`
19361        // composite-slice arm, closing the M3 mesh-slot arm of the
19362        // declared-slot enumerator's routing invariant on the
19363        // composite-slice inputs.
19364        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
19365            "cart", "catalog", "/items",
19366        )]);
19367        let slots = c.declared_mesh_slots();
19368        assert!(
19369            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19370            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
19371             `:contratos` is non-empty — the accessor and the enumerator \
19372             gate must route through the same substrate-primitive \
19373             typed dispatch on the outer :contratos presence bit (got \
19374             slots={slots:?})",
19375        );
19376        let c = caixa_aplicacao_with_contratos(vec![]);
19377        let slots = c.declared_mesh_slots();
19378        assert!(
19379            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19380            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
19381             when `:contratos` is empty — the author-omitted arm must \
19382             route through the accessor's empty-slice return unchanged \
19383             (got slots={slots:?})",
19384        );
19385    }
19386
19387    #[test]
19388    fn aplicacao_view_contratos_arm_routes_through_accessor() {
19389        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
19390        // fold-in arm must key off [`Caixa::contratos`], not the raw
19391        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
19392        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
19393        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
19394        // per-edge list through the accessor into the typed
19395        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
19396        // every entry the accessor surfaces must land in the view's
19397        // `contratos` slot in the same order. The pair jointly pins
19398        // the accessor + view-composer composition: a future silent
19399        // detour that had the accessor shuffle or drop an edge would
19400        // silently split the paired declared-slot enumerator's
19401        // presence bit from the typed-view composer's edge-list, a
19402        // two-consumer split at the enumerator and the view composer
19403        // far from the source `caixa.lisp`.
19404        //
19405        // Peer of the sibling
19406        // `aplicacao_view_membros_arm_routes_through_accessor`
19407        // (0f26987) composition pin on the M3 `:membros` outer-
19408        // `&[Composite]` composite-slice arm, closing the aplicacao-
19409        // view composer's routing invariant on the composite-slice
19410        // inputs at the outer altitude.
19411        let c = caixa_aplicacao_with_contratos(vec![
19412            contrato_http_for_test("cart", "catalog", "/items"),
19413            contrato_http_for_test("cart", "pricing", "/price"),
19414        ]);
19415        let view = c
19416            .aplicacao_view()
19417            .expect("Aplicacao kind must produce an aplicacao_view");
19418        assert_eq!(
19419            view.contratos(),
19420            c.contratos(),
19421            "aplicacao_view must fold Caixa::contratos verbatim into \
19422             AplicacaoSpec::contratos — the accessor and the view \
19423             composer must route through the same substrate-primitive \
19424             typed dispatch on the outer :contratos slice (got view \
19425             contratos={:?}, expected {:?})",
19426            view.contratos(),
19427            c.contratos(),
19428        );
19429    }
19430
19431    #[test]
19432    fn contratos_projects_slice_by_borrow() {
19433        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
19434        // by borrow — the returned slice borrows the underlying
19435        // `Vec<WitContract>` storage of the `:contratos` slot and the
19436        // accessor must not clone the backing `Vec` on every call.
19437        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19438        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19439        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19440        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19441        // `exe_projects_slice_by_borrow` 65d9527,
19442        // `servicos_projects_slice_by_borrow` 611f78b,
19443        // `deps_projects_slice_by_borrow` ad34b4e,
19444        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19445        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19446        // `children_projects_slice_by_borrow` c17b51e,
19447        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
19448        // outer top-level [`Caixa`] scalar-element and composite-
19449        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
19450        // composite-element `&[Composite]` axis on the by-borrow pin:
19451        // the accessor's returned slice must borrow from `&self` (the
19452        // returned reference's lifetime is tied to `&self`), and
19453        // calling the accessor twice on the same [`Caixa`] must yield
19454        // slices that are pointer-equal (the underlying byte-buffer is
19455        // the storage `Vec`'s allocation, not a fresh copy) as well as
19456        // value-equal (idempotent, no side effects on `&self`).
19457        //
19458        // Pins against a future silent detour that returned an owned
19459        // `Vec<WitContract>` (which would type-check but silently clone
19460        // on every call), a `&Vec<WitContract>` return (which would
19461        // leak the backing `Vec`'s grow/push/reserve surface no
19462        // downstream consumer reaches for), or a one-arm-only accessor
19463        // that returned a saturating value on some sentinel input.
19464        for contratos in [
19465            vec![],
19466            vec![contrato_http_for_test("cart", "catalog", "/items")],
19467            vec![
19468                contrato_http_for_test("cart", "catalog", "/items"),
19469                contrato_http_for_test("cart", "pricing", "/price"),
19470            ],
19471        ] {
19472            let c = caixa_aplicacao_with_contratos(contratos.clone());
19473            let first = c.contratos();
19474            let second = c.contratos();
19475            assert_eq!(
19476                first, second,
19477                "Caixa::contratos must be idempotent — two successive \
19478                 calls on the same &self must return the same \
19479                 &[WitContract]",
19480            );
19481            assert_eq!(
19482                first.as_ptr(),
19483                second.as_ptr(),
19484                "Caixa::contratos must borrow the underlying \
19485                 Vec<WitContract> storage — two successive calls must \
19486                 return slices with the same backing pointer (a fresh \
19487                 Vec<WitContract> clone would change the pointer on \
19488                 every call)",
19489            );
19490            assert_eq!(
19491                first,
19492                contratos.as_slice(),
19493                "Caixa::contratos must return :contratos verbatim by \
19494                 borrow — got {first:?}, expected {contratos:?}",
19495            );
19496        }
19497    }
19498
19499    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
19500
19501    #[test]
19502    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
19503        // Load-bearing invariant: every multi-word top-level [`Caixa`]
19504        // serde-derived JSON key routes through a lifted `&'static str`
19505        // const. The Rust field names are `snake_case`
19506        // (`deps_dev` / `upgrade_from` / `max_restarts` /
19507        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
19508        // "camelCase")]` derive attribute maps each to the camelCase
19509        // byte-string the [`Caixa::to_lisp`] round-trip's
19510        // `serde_json::to_value(self)` step lands under before
19511        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
19512        // to the kebab-case `:deps-dev` / `:upgrade-from` /
19513        // `:max-restarts` / `:restart-window` author surface. Serialize
19514        // a fully-populated [`Caixa`] and pin that each canonical
19515        // byte-sequence appears verbatim in the JSON — a future
19516        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
19517        // verbatim-field-name flip at the derive attribute (any of
19518        // which would silently break every [`Caixa::to_lisp`]
19519        // round-trip and the future M4 operator-side manifest ingest's
19520        // `Value::get(<key>)` navigation) surfaces here as a build-time
19521        // test failure at `manifest.rs`, not as an apply-time
19522        // `.get(<stale-canonical-const>)` returning `None` far from the
19523        // derive-attr drift's commit. Same discipline the sibling
19524        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
19525        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
19526        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
19527        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
19528        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
19529        // [`UpgradeFromEntry`] per-entry axes — extended here to the
19530        // enclosing M0 [`Caixa`] top-level axis so the last of the four
19531        // multi-word top-level [`Caixa`] serde-derived JSON keys
19532        // (`depsDev`) joins the substrate's "one canonical byte-string
19533        // per typed serialized-key axis" discipline.
19534        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19535        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
19536        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19537        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
19538        c.upgrade_from = vec![UpgradeFromEntry {
19539            from: "0.0.1".into(),
19540            instructions: vec![UpgradeInstruction::Restart],
19541        }];
19542        c.estrategia = Some(RestartStrategy::OneForOne);
19543        c.max_restarts = Some(3);
19544        c.restart_window = Some("60s".into());
19545        c.children = vec![ChildSpec {
19546            caixa: "child".into(),
19547            versao: "^0.1".into(),
19548            restart: RestartPolicy::Permanent,
19549        }];
19550        let json = serde_json::to_string(&c).unwrap();
19551        for key in [
19552            crate::render::CAIXA_KEY_DEPS_DEV,
19553            crate::render::M2_KEY_UPGRADE_FROM,
19554            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19555            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19556        ] {
19557            let quoted = format!("\"{key}\"");
19558            assert!(
19559                json.contains(&quoted),
19560                "serialized Caixa must carry the lifted top-level \
19561                 multi-word byte-sequence {quoted} verbatim in the JSON \
19562                 emission (got: {json})",
19563            );
19564        }
19565    }
19566
19567    #[test]
19568    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
19569        // Cross-axis drift-detection pin: a future collapse of the four
19570        // canonical [`Caixa`] top-level multi-word byte-strings onto the
19571        // same value (e.g. an accidental copy-paste flip of
19572        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
19573        // `"upgradeFrom"`) would silently reroute every downstream
19574        // `Value::get(<key>)` probe on one axis onto the sibling axis's
19575        // top-level entry and pass every propagation-probe test that
19576        // expected only the stale axis's value. Peer of the sibling
19577        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
19578        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
19579        let all = [
19580            crate::render::CAIXA_KEY_DEPS_DEV,
19581            crate::render::M2_KEY_UPGRADE_FROM,
19582            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19583            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19584        ];
19585        for (i, a) in all.iter().enumerate() {
19586            for b in all.iter().skip(i + 1) {
19587                assert_ne!(
19588                    a, b,
19589                    "Caixa top-level multi-word key consts must be \
19590                     pairwise-distinct canonical byte-sequences — got \
19591                     `{a}` == `{b}`",
19592                );
19593            }
19594        }
19595    }
19596
19597    #[test]
19598    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
19599        // Shape-pin: every [`Caixa`] top-level multi-word key const must
19600        // be a lowerCamelCase byte-sequence (no `snake_case`
19601        // underscores, no `kebab-case` hyphens, no leading colon, no
19602        // `PascalCase` leading capital, no whitespace / dots) — the
19603        // canonical shape the `#[serde(rename_all = "camelCase")]`
19604        // derive produces on [`Caixa`]. A future flip to a
19605        // non-camelCase attribute at the derive surfaces both here
19606        // (this test fails on the stale-constant shape) and at
19607        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19608        // (that test fails on the mismatch between const and derive).
19609        // Peer with `membro_key_consts_are_lower_camel_case_shape`
19610        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
19611        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
19612        for key in [
19613            crate::render::CAIXA_KEY_DEPS_DEV,
19614            crate::render::M2_KEY_UPGRADE_FROM,
19615            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19616            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19617        ] {
19618            assert!(
19619                !key.is_empty(),
19620                "Caixa top-level multi-word key const must be non-empty \
19621                 (got {key:?})"
19622            );
19623            let first = key.chars().next().unwrap();
19624            assert!(
19625                first.is_ascii_lowercase(),
19626                "Caixa top-level multi-word key const must lead with an \
19627                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
19628            );
19629            assert!(
19630                key.chars().all(|c| c.is_ascii_alphanumeric()),
19631                "Caixa top-level multi-word key const must be \
19632                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
19633                 whitespace (got {key:?})",
19634            );
19635        }
19636    }
19637
19638    #[test]
19639    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
19640        // Scalar-value pin: the byte-string the
19641        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
19642        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
19643        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
19644        // → `depsTest` matching a hypothetical per-test-target
19645        // vocabulary flip) lands as an edit to exactly one const AND
19646        // one derive attribute — the sibling
19647        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19648        // pin already ties the const to the derive attribute, so a
19649        // rebrand that touches only one side of the pair fails at
19650        // caixa-core build time. Same "scalar-value pin per const"
19651        // discipline the sibling
19652        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
19653        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
19654        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
19655        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
19656    }
19657
19658    #[test]
19659    fn caixa_key_deps_pins_canonical_byte_string() {
19660        // Scalar-value pin: the byte-string the
19661        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
19662        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
19663        // on the two-list dep-graph serialized-key axis — the sibling
19664        // pin covers the multi-word `deps_dev → depsDev` camelCase
19665        // arm, this pin covers the single-word `deps → deps` no-op arm
19666        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
19667        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
19668        // axis and the emitted JSON key equals the source-side field
19669        // name byte-for-byte). A future [`crate::Caixa::deps`] field
19670        // rename (`deps` → `dependencies` matching Cargo's verbatim
19671        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
19672        // hypothetical per-runtime-target vocabulary flip) OR an added
19673        // `#[serde(rename = "…")]` explicit override lands as an edit
19674        // to exactly one const AND one derive-attr / field name — the
19675        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
19676        // pin ties the const to the emitted JSON key, so a rebrand
19677        // that touches only one side of the pair fails at caixa-core
19678        // build time.
19679        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
19680    }
19681
19682    #[test]
19683    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
19684        // Load-bearing invariant on the single-word `deps` top-level
19685        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
19686        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
19687        // `serde_json::to_value(self)` step emits. Serialize a
19688        // populated [`Caixa`] whose `:deps` slot carries at least one
19689        // entry (the `#[serde(default)]` attribute on the field emits
19690        // an empty `[]` even without members, but a non-empty vec
19691        // additionally covers the codec's per-`Dep`-entry emission
19692        // path) and pin that `"deps"` appears verbatim in the JSON
19693        // emission — a future accidental `rename_all = "snake_case"` /
19694        // `"kebab-case"` flip at the derive attribute (or an added
19695        // `#[serde(rename = "…")]` explicit override on the field, or
19696        // a Rust field rename) would break every [`Caixa::to_lisp`]
19697        // round-trip and the future M4 operator-side manifest ingest's
19698        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
19699        // build-time test failure at `manifest.rs`, not as an
19700        // apply-time `.get(<stale-canonical-const>)` returning `None`
19701        // far from the drift's commit. Peer of the sibling
19702        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19703        // multi-word pin on the same M0 [`Caixa`] top-level
19704        // serialized-key axis, extended here to the single-word arm
19705        // the multi-word test's `rename_all = "camelCase"` sweep can't
19706        // reach (single-word `deps → deps` is a no-op the multi-word
19707        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
19708        // `\"restartWindow\"` byte-scan can never observe).
19709        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19710        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
19711        let json = serde_json::to_string(&c).unwrap();
19712        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
19713        assert!(
19714            json.contains(&quoted),
19715            "serialized Caixa must carry the lifted top-level `deps` \
19716             byte-sequence {quoted} verbatim in the JSON emission (got: \
19717             {json})",
19718        );
19719    }
19720
19721    #[test]
19722    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
19723        // Cross-axis drift-detection pin on the two-list dep-graph
19724        // renderer-side wire-key axis: a future collapse of the
19725        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
19726        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
19727        // same value (e.g. an accidental copy-paste flip of
19728        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
19729        // reroute every downstream `Value::get(<key>)` probe on one
19730        // axis onto the sibling axis's dep-list and pass every
19731        // propagation-probe test that expected only the stale axis's
19732        // value — a dev-only dep would land in the runtime closure at
19733        // publish time, or a runtime dep would be excluded from the
19734        // published lacre. Peer of the sibling four-way distinct pin
19735        // on the top-level multi-word tetrad
19736        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
19737        // and the two-way pin on the sibling
19738        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
19739        // author-facing arm (4da6fba's test), extended here to the
19740        // renderer-side wire-key arm of the same two-list dep-graph
19741        // axis so both halves of the "one canonical byte-string per
19742        // typed axis per (author, wire)" grid carry the same
19743        // distinct-ness discipline.
19744        assert_ne!(
19745            crate::render::CAIXA_KEY_DEPS,
19746            crate::render::CAIXA_KEY_DEPS_DEV,
19747            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
19748             canonical byte-sequences on the two-list dep-graph \
19749             renderer-side wire-key axis"
19750        );
19751    }
19752
19753    // ── DepList / Caixa::push_dep pin ────────────────────────────────
19754    //
19755    // The compounding pin: the two-arm closed-set typed enum
19756    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
19757    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
19758    // consumer of the top-level manifest's dep-mutation surface reads
19759    // through, and the typed dispatch [`Caixa::push_dep`] on the
19760    // substrate primitive folds the "select list → check within-list
19761    // dup → push" cascade onto one method call. Prior to this landing
19762    // the two axes lived across two `&'static str` constants
19763    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
19764    // set type carrying the pair; the `feira add` mutation site's
19765    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
19766    // caixa.deps }` dispatch expressed no compile-time link back to
19767    // the substrate primitive, and a future third dep-list axis would
19768    // have silently split at every open-coded mutation site.
19769
19770    #[test]
19771    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
19772        // Every arm returns the same `&'static str` the substrate's
19773        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
19774        // constants carry. A future rebrand on either constant reaches
19775        // the enum through one edit; a regression to inline literals
19776        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
19777        // quotes from the wire-format constants every consumer routes
19778        // through and this pin flags it at build time.
19779        assert_eq!(
19780            crate::dep::DepList::Prod.as_str(),
19781            crate::render::DEP_AUTHOR_KEY_DEPS
19782        );
19783        assert_eq!(
19784            crate::dep::DepList::Dev.as_str(),
19785            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
19786        );
19787    }
19788
19789    #[test]
19790    fn dep_list_display_routes_through_as_str() {
19791        // Same as-str-through-Display convergence discipline the
19792        // sibling closed-set typed enums carry — a `format!("{list}")`
19793        // call must land byte-for-byte on the accessor's return so a
19794        // future consumer that formats the enum for a diagnostic line
19795        // reaches the same wire-format constant the wire-format
19796        // producers do.
19797        assert_eq!(
19798            format!("{}", crate::dep::DepList::Prod),
19799            crate::dep::DepList::Prod.as_str()
19800        );
19801        assert_eq!(
19802            format!("{}", crate::dep::DepList::Dev),
19803            crate::dep::DepList::Dev.as_str()
19804        );
19805    }
19806
19807    #[test]
19808    fn dep_list_all_enumerates_every_variant_once() {
19809        // Exhaustive-iteration pin — every arm appears exactly once in
19810        // `ALL`, matching the closed set the compiler enforces on the
19811        // sibling `match self` arms. A future variant addition that
19812        // extends only one method's match without extending `ALL`
19813        // would silently drop the new arm from every consumer that
19814        // iterates the slice.
19815        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
19816        assert!(variants.contains(&crate::dep::DepList::Prod));
19817        assert!(variants.contains(&crate::dep::DepList::Dev));
19818        assert_eq!(variants.len(), 2);
19819    }
19820
19821    #[test]
19822    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
19823        // Reverse projection on the two-list dep-graph axis: the
19824        // author-surface wire tag the sibling `as_str` emitter walks
19825        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
19826        // `Some(DepList::Prod)`. A regression that hand-rolled the
19827        // per-arm match without routing through the lifted
19828        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
19829        // future wire-tag rebrand and this pin flags it at build time.
19830        assert_eq!(
19831            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
19832            Some(crate::dep::DepList::Prod)
19833        );
19834    }
19835
19836    #[test]
19837    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
19838        // Peer of the `Prod`-arm pin on the dev-only axis: the
19839        // author-surface wire tag the sibling `as_str` emitter walks
19840        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
19841        // back to `Some(DepList::Dev)`. Same drift-detection posture
19842        // as the peer arm — the sibling method `match` arms are
19843        // compiler-checked exhaustive so a future variant addition
19844        // trips at build time.
19845        assert_eq!(
19846            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
19847            Some(crate::dep::DepList::Dev)
19848        );
19849    }
19850
19851    #[test]
19852    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
19853        // Every input outside the closed-set arm-string set the
19854        // sibling `as_str` emitter walks lands on the terminal `None`
19855        // fallback — no silent-accept surface. Sweeps a set of
19856        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
19857        // rebrand candidates, foreign wire tags, empty string) so a
19858        // future variant addition that widened one wire form without
19859        // extending the emitter's arm-set would trip the sibling
19860        // round-trip pin below rather than silently accepting the new
19861        // form here.
19862        for candidate in [
19863            "",
19864            "deps",
19865            "deps-dev",
19866            ":deps ",
19867            ":Deps",
19868            ":DEPS",
19869            ":build-dep",
19870            ":tool-dep",
19871            "prod",
19872            "dev",
19873        ] {
19874            assert_eq!(
19875                crate::dep::DepList::from_wire(candidate),
19876                None,
19877                "from_wire({candidate:?}) must return None; every input outside \
19878                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
19879                 the sibling as_str emitter walks lands on the terminal fallback",
19880            );
19881        }
19882    }
19883
19884    #[test]
19885    fn dep_list_round_trips_through_as_str_and_from_wire() {
19886        // Load-bearing round-trip pin: every arm the `ALL` iteration
19887        // exposes survives the `as_str` → `from_wire` composition
19888        // byte-for-byte. Same discipline the sibling closed-set enums
19889        // carry — `CaixaKind` /
19890        // `RestartStrategy` / `RestartPolicy` /
19891        // `PlacementStrategy` — extended onto the two-list dep-graph
19892        // axis. A future variant addition that extends `ALL` +
19893        // `as_str` without extending `from_wire` (or vice versa)
19894        // trips at build time on this iteration because the compiler
19895        // enforces exhaustiveness on the sibling `match self` arms.
19896        for &list in crate::dep::DepList::ALL {
19897            assert_eq!(
19898                crate::dep::DepList::from_wire(list.as_str()),
19899                Some(list),
19900                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
19901                 a silent split between the forward emitter and the reverse parser \
19902                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
19903            );
19904        }
19905    }
19906
19907    #[test]
19908    fn push_dep_routes_to_deps_slot_on_prod_arm() {
19909        // The `Prod` arm dispatches to the runtime-closure `:deps`
19910        // slot every downstream lacre-pipeline consumer resolves at
19911        // build time. A future arm that regressed to inline `&mut
19912        // self.deps_dev` on the `Prod` path would silently reroute
19913        // every runtime dep into the dev-only closure at publish time
19914        // — this pin refuses that regression.
19915        let src = Caixa::template("host");
19916        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19917        let before_deps = caixa.deps().len();
19918        let before_deps_dev = caixa.deps_dev().len();
19919        let dep = Dep {
19920            nome: "caixa-teia".to_string(),
19921            versao: "^0.1".to_string(),
19922            fonte: None,
19923            opcional: false,
19924            caracteristicas: Vec::new(),
19925        };
19926        caixa
19927            .push_dep(crate::dep::DepList::Prod, dep)
19928            .expect("first push into :deps succeeds");
19929        assert_eq!(caixa.deps().len(), before_deps + 1);
19930        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
19931        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
19932    }
19933
19934    #[test]
19935    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
19936        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
19937        // must dispatch to the dev-only-closure `:deps-dev` slot every
19938        // downstream test-facing artifact resolver reads. A future
19939        // regression that inverted the two arms would silently route
19940        // every dev-only dep into the runtime closure at publish time
19941        // and this pin catches it before the drift ships.
19942        let src = Caixa::template("host");
19943        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19944        let dep = Dep {
19945            nome: "tatara-check".to_string(),
19946            versao: "*".to_string(),
19947            fonte: None,
19948            opcional: false,
19949            caracteristicas: Vec::new(),
19950        };
19951        caixa
19952            .push_dep(crate::dep::DepList::Dev, dep)
19953            .expect("first push into :deps-dev succeeds");
19954        assert!(caixa.deps().is_empty());
19955        assert_eq!(caixa.deps_dev().len(), 1);
19956        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
19957    }
19958
19959    #[test]
19960    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
19961        // Within-list dup check routes through the canonical
19962        // [`DepError::DuplicateNome`] carrier — the substrate's typed
19963        // diagnostic for the same axis [`Caixa::validate_deps`]'s
19964        // parse-time [`crate::render::insert_first_seen`] walk raises
19965        // on. Prior to the lift the mutation site's inline
19966        // `bail!("dep '{}' already declared", …)` string-diagnostic
19967        // path expressed no through-line back to the typed error;
19968        // routing every dep-list refusal through one carrier means an
19969        // author reading a `feira add` refusal and a `feira build`
19970        // refusal reaches for the same corrective surface without
19971        // switching diagnostic idioms.
19972        let src = Caixa::template("host");
19973        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19974        let dep = Dep {
19975            nome: "caixa-teia".to_string(),
19976            versao: "^0.1".to_string(),
19977            fonte: None,
19978            opcional: false,
19979            caracteristicas: Vec::new(),
19980        };
19981        caixa
19982            .push_dep(crate::dep::DepList::Prod, dep.clone())
19983            .expect("first push succeeds");
19984        let dup = Dep {
19985            nome: "caixa-teia".to_string(),
19986            versao: "^0.2".to_string(),
19987            fonte: None,
19988            opcional: false,
19989            caracteristicas: Vec::new(),
19990        };
19991        let err = caixa
19992            .push_dep(crate::dep::DepList::Prod, dup)
19993            .expect_err("second push with same :nome refuses");
19994        assert_eq!(
19995            err,
19996            DepError::DuplicateNome {
19997                nome: "caixa-teia".to_string(),
19998                list: crate::render::DEP_AUTHOR_KEY_DEPS,
19999            }
20000        );
20001        // The refused mutation must not corrupt the target list —
20002        // exactly one entry lives past the refusal, matching the
20003        // canonical single-source-of-truth invariant `Caixa::deps()`
20004        // carries.
20005        assert_eq!(caixa.deps().len(), 1);
20006    }
20007
20008    #[test]
20009    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
20010        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
20011        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
20012        // `list` payload so a future author reading the refusal grep's
20013        // for the correct `:deps-dev` block in their `caixa.lisp`,
20014        // not the sibling `:deps` block the runtime closure resolves.
20015        let src = Caixa::template("host");
20016        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20017        let dep = Dep {
20018            nome: "tatara-check".to_string(),
20019            versao: "*".to_string(),
20020            fonte: None,
20021            opcional: false,
20022            caracteristicas: Vec::new(),
20023        };
20024        caixa
20025            .push_dep(crate::dep::DepList::Dev, dep.clone())
20026            .expect("first push succeeds");
20027        let err = caixa
20028            .push_dep(crate::dep::DepList::Dev, dep)
20029            .expect_err("second push with same :nome refuses");
20030        assert!(matches!(
20031            err,
20032            DepError::DuplicateNome {
20033                ref nome,
20034                list,
20035            } if nome == "tatara-check"
20036                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20037        ));
20038    }
20039
20040    #[test]
20041    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
20042        // The within-list dup check is scoped to the target arm — a
20043        // caixa may legitimately carry the same `:nome` under both
20044        // `:deps` and `:deps-dev` (though the substrate's peer
20045        // [`crate::Caixa::validate_deps`] walk still refuses the
20046        // shape at parse time; the mutation-site refusal is scoped to
20047        // the mutation-site's list to match the peer parse-time
20048        // per-list [`crate::render::insert_first_seen`] discipline).
20049        // The two arms hold independent seen-sets.
20050        let src = Caixa::template("host");
20051        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20052        let dep_prod = Dep {
20053            nome: "shared".to_string(),
20054            versao: "^0.1".to_string(),
20055            fonte: None,
20056            opcional: false,
20057            caracteristicas: Vec::new(),
20058        };
20059        let dep_dev = Dep {
20060            nome: "shared".to_string(),
20061            versao: "*".to_string(),
20062            fonte: None,
20063            opcional: false,
20064            caracteristicas: Vec::new(),
20065        };
20066        caixa
20067            .push_dep(crate::dep::DepList::Prod, dep_prod)
20068            .expect("push into :deps succeeds");
20069        caixa
20070            .push_dep(crate::dep::DepList::Dev, dep_dev)
20071            .expect("push same :nome into :deps-dev succeeds");
20072        assert_eq!(caixa.deps().len(), 1);
20073        assert_eq!(caixa.deps_dev().len(), 1);
20074    }
20075
20076    #[test]
20077    fn deps_of_prod_returns_the_deps_slot_verbatim() {
20078        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
20079        // accessor must project onto the runtime-closure `:deps` slot —
20080        // element-equal and length-equal to the sibling per-slot
20081        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
20082        // A future arm that regressed to `self.deps_dev()` on the `Prod`
20083        // path would silently reroute every downstream typed-dispatch
20084        // walker (the [`Caixa::validate_deps`] per-list
20085        // [`crate::render::insert_first_seen`] dedup walk, any future
20086        // per-axis-parametrised consumer) into the sibling dev-only
20087        // closure and this pin refuses that regression.
20088        let src = Caixa::template("host");
20089        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20090        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20091        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
20092        let dep = Dep {
20093            nome: "caixa-teia".to_string(),
20094            versao: "^0.1".to_string(),
20095            fonte: None,
20096            opcional: false,
20097            caracteristicas: Vec::new(),
20098        };
20099        caixa
20100            .push_dep(crate::dep::DepList::Prod, dep.clone())
20101            .expect("push into :deps succeeds");
20102        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20103        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
20104        assert_eq!(
20105            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
20106            "caixa-teia"
20107        );
20108    }
20109
20110    #[test]
20111    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
20112        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
20113        // [`Caixa::deps_of`] must project onto the dev-only-closure
20114        // `:deps-dev` slot, element-equal and length-equal to the
20115        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
20116        // future regression that inverted the two arms would silently
20117        // route every dev-list walker onto the runtime closure and this
20118        // pin catches it before the drift ships.
20119        let src = Caixa::template("host");
20120        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20121        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20122        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
20123        let dep = Dep {
20124            nome: "tatara-check".to_string(),
20125            versao: "*".to_string(),
20126            fonte: None,
20127            opcional: false,
20128            caracteristicas: Vec::new(),
20129        };
20130        caixa
20131            .push_dep(crate::dep::DepList::Dev, dep)
20132            .expect("push into :deps-dev succeeds");
20133        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20134        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
20135        assert_eq!(
20136            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
20137            "tatara-check"
20138        );
20139    }
20140
20141    #[test]
20142    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
20143        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
20144        // [`Caixa::deps_of`] must land on the same two-slot partition the
20145        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
20146        // expose — the canonical dispatch a future per-axis-parametrised
20147        // walker (a future `feira app graph` per-list dep summary, a
20148        // future M4 per-cluster dev-closure-audit overlay the CR
20149        // materializer resolves per-CR) reads through. Prior to the
20150        // lift the two-block iteration lived open-coded at every walker,
20151        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
20152        // §I) would have had to grow a third block at every consumer.
20153        // A regression that dropped the `Dev` arm from `ALL` would flip
20154        // the collected pairs to `[(":deps", &[])]` alone and this pin
20155        // refuses that shape.
20156        let src = Caixa::template("host");
20157        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20158        let prod_dep = Dep {
20159            nome: "caixa-teia".to_string(),
20160            versao: "^0.1".to_string(),
20161            fonte: None,
20162            opcional: false,
20163            caracteristicas: Vec::new(),
20164        };
20165        let dev_dep = Dep {
20166            nome: "tatara-check".to_string(),
20167            versao: "*".to_string(),
20168            fonte: None,
20169            opcional: false,
20170            caracteristicas: Vec::new(),
20171        };
20172        caixa
20173            .push_dep(crate::dep::DepList::Prod, prod_dep)
20174            .expect("push into :deps succeeds");
20175        caixa
20176            .push_dep(crate::dep::DepList::Dev, dev_dep)
20177            .expect("push into :deps-dev succeeds");
20178        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
20179            .iter()
20180            .map(|&list| {
20181                let slice = caixa.deps_of(list);
20182                (list.as_str(), slice.len(), slice[0].nome())
20183            })
20184            .collect();
20185        assert_eq!(
20186            collected,
20187            vec![
20188                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
20189                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
20190            ]
20191        );
20192    }
20193
20194    #[test]
20195    fn caixa_deps_of_is_const_fn() {
20196        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
20197        // `const`-eval-surface posture. The typed-dispatch read
20198        // accessor forwards through the sibling `pub const fn`
20199        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
20200        // accessors on the two [`crate::dep::DepList`] enum arms —
20201        // every operator in the body is already `const`-callable
20202        // (`DepList` is a plain `#[derive(Copy)]` closed-set
20203        // discriminator so the `match` arms are const-evaluable, and
20204        // each arm dispatches through the sibling `pub const fn`
20205        // slice accessor). Any future accidental downgrade to
20206        // non-`const` fails the `deps_of_via_const_fn` wrapper below
20207        // at caixa-core build time with E0015 (`cannot call non-const
20208        // method`), strictly stronger than a runtime `assert!` and
20209        // side-stepping the destructor-in-const restriction the
20210        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
20211        // rule out on the direct-`const _: () = assert!(...)`
20212        // residence.
20213        //
20214        // Peer of the sibling outer-`Caixa` accessor family pins
20215        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
20216        // on the `&[String]` universal-axis surface,
20217        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
20218        // on the outer `&[T]` composite-slice surface,
20219        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
20220        // on the outer `Option<&Composite>` surface) — this pin
20221        // extends the `const`-eval-surface discipline onto the outer-
20222        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
20223        // dep-list axis, closing the outer-`Caixa` accessor family's
20224        // last unlifted `pub fn` on the read side.
20225        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
20226            c.deps_of(list)
20227        }
20228        let src = Caixa::template("host");
20229        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20230        // Empty-list arm: both `Prod` and `Dev` degenerate to the
20231        // empty slice with no silent `None` collapse — the
20232        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
20233        // that omits the slot lands on.
20234        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
20235        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
20236        assert_eq!(
20237            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20238            caixa.deps()
20239        );
20240        assert_eq!(
20241            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20242            caixa.deps_dev()
20243        );
20244        // Populated arms: each list carries its own entry, and the
20245        // wrapper / direct dispatches agree byte-for-byte on the
20246        // slice-view under both non-empty arms.
20247        let prod_dep = Dep {
20248            nome: "caixa-teia".to_string(),
20249            versao: "^0.1".to_string(),
20250            fonte: None,
20251            opcional: false,
20252            caracteristicas: Vec::new(),
20253        };
20254        let dev_dep = Dep {
20255            nome: "tatara-check".to_string(),
20256            versao: "*".to_string(),
20257            fonte: None,
20258            opcional: false,
20259            caracteristicas: Vec::new(),
20260        };
20261        caixa
20262            .push_dep(crate::dep::DepList::Prod, prod_dep)
20263            .expect("push into :deps succeeds");
20264        caixa
20265            .push_dep(crate::dep::DepList::Dev, dev_dep)
20266            .expect("push into :deps-dev succeeds");
20267        assert_eq!(
20268            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20269            caixa.deps()
20270        );
20271        assert_eq!(
20272            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20273            caixa.deps_dev()
20274        );
20275        assert_eq!(
20276            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
20277            "caixa-teia"
20278        );
20279        assert_eq!(
20280            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
20281            "tatara-check"
20282        );
20283    }
20284
20285    #[test]
20286    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
20287        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
20288        // must route its per-list [`crate::render::insert_first_seen`]
20289        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
20290        // rather than the pre-lift open-coded two-block iteration over
20291        // `self.deps()` + `self.deps_dev()`. A regression that dropped
20292        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
20293        // stop refusing within-list dups on the sibling arm; a
20294        // regression that flipped the arm-to-list-key mapping
20295        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
20296        // diagnostic surface. Both drifts surface here through a paired
20297        // duplicate-name refusal per arm plus an offending-list-key
20298        // check on the emitted [`DepError::DuplicateNome`] carrier.
20299        for &list in crate::dep::DepList::ALL {
20300            let src = Caixa::template("host");
20301            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20302            let dup = Dep {
20303                nome: "twin".to_string(),
20304                versao: "^0.1".to_string(),
20305                fonte: None,
20306                opcional: false,
20307                caracteristicas: Vec::new(),
20308            };
20309            match list {
20310                crate::dep::DepList::Prod => {
20311                    caixa.deps.push(dup.clone());
20312                    caixa.deps.push(dup);
20313                }
20314                crate::dep::DepList::Dev => {
20315                    caixa.deps_dev.push(dup.clone());
20316                    caixa.deps_dev.push(dup);
20317                }
20318            }
20319            let err = caixa
20320                .validate_deps()
20321                .expect_err("within-list duplicate :nome must refuse");
20322            assert_eq!(
20323                err,
20324                DepError::DuplicateNome {
20325                    nome: "twin".to_string(),
20326                    list: list.as_str(),
20327                },
20328                "validate_deps on {list} arm must emit \
20329                 DepError::DuplicateNome carrying the arm's own \
20330                 as_str() diagnostic — the arm-to-list-key mapping \
20331                 flowed through DepList::ALL + Caixa::deps_of"
20332            );
20333        }
20334    }
20335
20336    #[test]
20337    fn caixa_licenca_default_pins_canonical_mit_byte() {
20338        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
20339        // canonical SPDX-`"MIT"` byte today, the same license expression
20340        // every peer substrate-side consumer of the author-omitted
20341        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
20342        // `caixa-helm/src/lib.rs`, the future M4
20343        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
20344        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
20345        // crate's [`Caixa::validate_licenca`] docstring roadmap already
20346        // names as the second consumer) fills into its per-consumer
20347        // README/annotation emit site. Pin the literal here (peer with the
20348        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
20349        // [`crate::version::DEFAULT_GIT_REMOTE`] /
20350        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
20351        // on the sibling lifted-constant surfaces) so a future
20352        // substrate-side license-fallback rebrand surfaces here as a
20353        // coordinated edit-point: the sibling caixa-helm
20354        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
20355        // pinning test already pins the equality at the renderer-emit
20356        // axis; this pin closes the second coordinate of the pair by
20357        // anchoring the lifted constant's current byte to the canonical
20358        // CAIXA-SDLC §I license scaffold's documented shape.
20359        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
20360    }
20361
20362    // ── Caixa::validate_upgrade_from — compound per-Caixa entry gate on ──
20363    // ── the M2 `:upgrade-from` slot: folds the three top-level        ──
20364    // ── `crate::upgrade` validators (per-entry + cross-entry           ──
20365    // ── duplicate-`:from`, cross-slot `:from < :versao` precedence,   ──
20366    // ── cross-slot `:state-change` ↔ `:on-state-change` composition)  ──
20367    // ── onto one substrate primitive. Byte-for-byte equivalent to the ──
20368    // ── pre-fold three-block cascade at                               ──
20369    // ── `crate::layout::StandardLayout::verify` under the same        ──
20370    // ── canonical dispatch order.                                     ──
20371
20372    #[test]
20373    fn validate_upgrade_from_folds_per_entry_arm_matches_gate() {
20374        // Fail-before-pass-after per-arm equivalence pin on the
20375        // per-entry + cross-entry axis: a fixture whose `:upgrade-from`
20376        // carries a per-entry-invalid `:from` (git-tag shape `"v0.1.0"`,
20377        // which `semver::Version::parse` rejects) surfaces the same
20378        // [`crate::UpgradeError`] through the compound gate
20379        // [`Caixa::validate_upgrade_from`] and the standalone per-entry
20380        // gate [`crate::upgrade::validate_upgrade_from`] on the same
20381        // [`Caixa::upgrade_from`] slice. Pins the fold — a silent
20382        // regression that de-folded the per-entry arm would surface here
20383        // as a mismatch between the two dispatches. Sibling in shape to
20384        // the peer per-slot-≡-standalone equivalence pins the
20385        // [`crate::AplicacaoSpec::validate_contratos`] /
20386        // [`crate::MeshPolicy::validate`] /
20387        // [`crate::SupervisorSpec::validate_children`] compound gates
20388        // each carry on their axes.
20389        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20390        c.upgrade_from = vec![crate::UpgradeFromEntry {
20391            from: "v0.1.0".into(),
20392            instructions: vec![crate::UpgradeInstruction::Restart],
20393        }];
20394        let via_method = c.validate_upgrade_from().unwrap_err();
20395        let via_standalone = crate::upgrade::validate_upgrade_from(c.upgrade_from()).unwrap_err();
20396        assert_eq!(
20397            via_method, via_standalone,
20398            "Caixa::validate_upgrade_from must surface the per-entry \
20399             axis's diagnostic byte-equal to the standalone \
20400             `crate::upgrade::validate_upgrade_from` on the same \
20401             upgrade_from() slice"
20402        );
20403        assert!(
20404            matches!(
20405                via_method,
20406                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.1.0"
20407            ),
20408            "expected FromInvalid on the git-tag-shape `:from`, got {via_method:?}"
20409        );
20410    }
20411
20412    #[test]
20413    fn validate_upgrade_from_folds_versao_arm_matches_gate() {
20414        // Per-arm equivalence pin on the cross-slot `:from ↔ :versao`
20415        // precedence axis: a fixture with a well-formed `:from` (so the
20416        // per-entry arm passes) whose parsed semver is >= the caixa's
20417        // `:versao` under SemVer-2 precedence surfaces the same
20418        // [`crate::UpgradeError::FromNotBeforeVersao`] through both the
20419        // compound gate and the standalone
20420        // [`crate::upgrade::validate_upgrade_from_against_versao`] gate
20421        // keyed off the same `(upgrade_from, versao)` pair. Pins the
20422        // fold's second arm — reaching this arm through the compound
20423        // gate requires the per-entry arm to pass first, which itself
20424        // pins the per-arm cross-arm ordering.
20425        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20426        c.versao = "0.1.0".into();
20427        c.upgrade_from = vec![crate::UpgradeFromEntry {
20428            from: "0.2.0".into(),
20429            instructions: vec![crate::UpgradeInstruction::Restart],
20430        }];
20431        let via_method = c.validate_upgrade_from().unwrap_err();
20432        let via_standalone =
20433            crate::upgrade::validate_upgrade_from_against_versao(c.upgrade_from(), c.versao())
20434                .unwrap_err();
20435        assert_eq!(
20436            via_method, via_standalone,
20437            "Caixa::validate_upgrade_from must surface the \
20438             `:from >= :versao` diagnostic byte-equal to the standalone \
20439             `crate::upgrade::validate_upgrade_from_against_versao` on \
20440             the same (upgrade_from, versao) pair"
20441        );
20442        assert!(
20443            matches!(
20444                via_method,
20445                crate::UpgradeError::FromNotBeforeVersao { ref from, ref versao }
20446                    if from == "0.2.0" && versao == "0.1.0"
20447            ),
20448            "expected FromNotBeforeVersao carrying the offending pair, got {via_method:?}"
20449        );
20450    }
20451
20452    #[test]
20453    fn validate_upgrade_from_folds_behavior_arm_matches_gate() {
20454        // Per-arm equivalence pin on the cross-slot `:state-change ↔
20455        // :on-state-change` composition axis: a fixture with a
20456        // well-formed `:from` strictly less than `:versao` (so the
20457        // per-entry and versao arms both pass) whose `:instructions`
20458        // list carries a `(:state-change …)` instruction with no
20459        // `:behavior :on-state-change` callback declared surfaces the
20460        // same [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
20461        // through both the compound gate and the standalone
20462        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
20463        // gate keyed off the same `(upgrade_from, behavior)` pair.
20464        // Reaching this arm through the compound gate requires both
20465        // prior arms to pass first — the ordering pin below pins the
20466        // per-arm dispatch order explicitly.
20467        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20468        c.versao = "0.2.0".into();
20469        c.behavior = None;
20470        c.upgrade_from = vec![crate::UpgradeFromEntry {
20471            from: "0.1.0".into(),
20472            instructions: vec![
20473                crate::UpgradeInstruction::LoadModule {
20474                    module: "demo".into(),
20475                },
20476                crate::UpgradeInstruction::StateChange {
20477                    script: std::path::PathBuf::from("lib/m.lisp"),
20478                },
20479                crate::UpgradeInstruction::SoftPurge {
20480                    module: "demo-old".into(),
20481                },
20482            ],
20483        }];
20484        let via_method = c.validate_upgrade_from().unwrap_err();
20485        let via_standalone =
20486            crate::upgrade::validate_upgrade_from_against_behavior(c.upgrade_from(), c.behavior())
20487                .unwrap_err();
20488        assert_eq!(
20489            via_method, via_standalone,
20490            "Caixa::validate_upgrade_from must surface the \
20491             `:state-change` ↔ `:on-state-change` composition \
20492             diagnostic byte-equal to the standalone \
20493             `crate::upgrade::validate_upgrade_from_against_behavior` \
20494             on the same (upgrade_from, behavior) pair"
20495        );
20496        assert!(
20497            matches!(
20498                via_method,
20499                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback {
20500                    ref from,
20501                    ref script,
20502                } if from == "0.1.0" && script == &std::path::PathBuf::from("lib/m.lisp")
20503            ),
20504            "expected StateChangeWithoutOnStateChangeCallback carrying \
20505             the offending (from, script) pair, got {via_method:?}"
20506        );
20507    }
20508
20509    #[test]
20510    fn validate_upgrade_from_per_entry_arm_fires_before_versao_arm() {
20511        // Cross-arm ordering pin between the first two arms of the
20512        // fold: a fixture carrying BOTH a per-entry-invalid `:from`
20513        // (`"v0.0.5"` — git-tag shape rejected by
20514        // [`crate::upgrade::validate_upgrade_from`]) AND a would-be
20515        // versao-precedence violation on a second entry (`"0.2.0" >=
20516        // :versao "0.1.0"`) surfaces the per-entry diagnostic first
20517        // through the compound gate. Sanity assertion: the second
20518        // entry alone under the same `:versao` trips the versao arm
20519        // on its own via the standalone
20520        // [`crate::upgrade::validate_upgrade_from_against_versao`], so
20521        // the per-entry-first surfacing is a real ordering property,
20522        // not a case where the versao arm silently accepts the
20523        // fixture. Pins the pre-fold layout wire-up's canonical
20524        // dispatch order (per-entry → versao → behavior) as a
20525        // property of the substrate primitive rather than a
20526        // convention of the layout call site.
20527        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20528        c.versao = "0.1.0".into();
20529        c.upgrade_from = vec![
20530            crate::UpgradeFromEntry {
20531                from: "v0.0.5".into(),
20532                instructions: vec![crate::UpgradeInstruction::Restart],
20533            },
20534            crate::UpgradeFromEntry {
20535                from: "0.2.0".into(),
20536                instructions: vec![crate::UpgradeInstruction::Restart],
20537            },
20538        ];
20539        let err = c.validate_upgrade_from().unwrap_err();
20540        assert!(
20541            matches!(
20542                err,
20543                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.0.5"
20544            ),
20545            "per-entry arm must fire before versao arm — expected \
20546             FromInvalid on `v0.0.5`, got {err:?}"
20547        );
20548        // Sanity: the versao-violating second entry alone under the
20549        // same `:versao` trips the versao arm on its own — proves the
20550        // per-entry-first surfacing above is a real ordering property.
20551        let sanity = crate::upgrade::validate_upgrade_from_against_versao(
20552            &[crate::UpgradeFromEntry {
20553                from: "0.2.0".into(),
20554                instructions: vec![crate::UpgradeInstruction::Restart],
20555            }],
20556            "0.1.0",
20557        )
20558        .unwrap_err();
20559        assert!(
20560            matches!(sanity, crate::UpgradeError::FromNotBeforeVersao { .. }),
20561            "sanity: the versao-violating fixture alone must trip the \
20562             versao arm — got {sanity:?}"
20563        );
20564    }
20565
20566    #[test]
20567    fn validate_upgrade_from_versao_arm_fires_before_behavior_arm() {
20568        // Cross-arm ordering pin between the second and third arms of
20569        // the fold: a fixture carrying BOTH a versao-precedence
20570        // violation (`:from "0.2.0" >= :versao "0.1.0"`) AND a
20571        // would-be missing-callback violation (a `(:state-change …)`
20572        // instruction with no `:behavior :on-state-change`) surfaces
20573        // the versao diagnostic first through the compound gate.
20574        // Sanity assertion: the missing-callback fixture alone (with
20575        // the versao-precedence violation removed by bumping
20576        // `:versao` past `:from`) trips the behavior arm on its own
20577        // via the standalone
20578        // [`crate::upgrade::validate_upgrade_from_against_behavior`],
20579        // so the versao-first surfacing is a real ordering property,
20580        // not a case where the behavior arm silently accepts the
20581        // fixture.
20582        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20583        c.versao = "0.1.0".into();
20584        c.behavior = None;
20585        c.upgrade_from = vec![crate::UpgradeFromEntry {
20586            from: "0.2.0".into(),
20587            instructions: vec![
20588                crate::UpgradeInstruction::LoadModule {
20589                    module: "demo".into(),
20590                },
20591                crate::UpgradeInstruction::StateChange {
20592                    script: std::path::PathBuf::from("lib/m.lisp"),
20593                },
20594            ],
20595        }];
20596        let err = c.validate_upgrade_from().unwrap_err();
20597        assert!(
20598            matches!(
20599                err,
20600                crate::UpgradeError::FromNotBeforeVersao { ref from, .. } if from == "0.2.0"
20601            ),
20602            "versao arm must fire before behavior arm — expected \
20603             FromNotBeforeVersao on `0.2.0`, got {err:?}"
20604        );
20605        // Sanity: the same instructions under a `:versao` that
20606        // accepts the `:from` (so the versao arm passes) trips the
20607        // behavior arm — proves the versao-first surfacing above is a
20608        // real ordering property.
20609        let sanity = crate::upgrade::validate_upgrade_from_against_behavior(
20610            &[crate::UpgradeFromEntry {
20611                from: "0.2.0".into(),
20612                instructions: vec![
20613                    crate::UpgradeInstruction::LoadModule {
20614                        module: "demo".into(),
20615                    },
20616                    crate::UpgradeInstruction::StateChange {
20617                        script: std::path::PathBuf::from("lib/m.lisp"),
20618                    },
20619                ],
20620            }],
20621            None,
20622        )
20623        .unwrap_err();
20624        assert!(
20625            matches!(
20626                sanity,
20627                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback { .. }
20628            ),
20629            "sanity: the missing-callback fixture alone must trip the \
20630             behavior arm — got {sanity:?}"
20631        );
20632    }
20633
20634    #[test]
20635    fn validate_upgrade_from_accepts_clean_fixture() {
20636        // Positive control: a well-formed `:upgrade-from` (single entry
20637        // with `:from` strictly less than `:versao`, no
20638        // `:state-change` instruction so the behavior arm is vacuous)
20639        // passes the compound gate cleanly. A future tightening of any
20640        // one arm's accepted set surfaces here as a test failure
20641        // first. Mirrors the peer `validate_versao_accepts_canonical_forms`
20642        // positive-control posture on the sibling per-Caixa gate.
20643        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20644        c.versao = "0.2.0".into();
20645        c.upgrade_from = vec![crate::UpgradeFromEntry {
20646            from: "0.1.0".into(),
20647            instructions: vec![crate::UpgradeInstruction::Restart],
20648        }];
20649        c.validate_upgrade_from()
20650            .expect("clean fixture must pass the compound `:upgrade-from` gate");
20651    }
20652
20653    #[test]
20654    fn validate_upgrade_from_accepts_empty_upgrade_from() {
20655        // Positive control on the empty-list arm: a caixa without any
20656        // `:upgrade-from` block (the default `Vec::new()`
20657        // `#[serde(default)]` folds an omitted slot onto) passes the
20658        // compound gate cleanly regardless of `:versao` or `:behavior`
20659        // — each of the three standalone validators is vacuous on the
20660        // empty entry list. Pins the identity element of the fold on
20661        // the empty-slot side.
20662        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20663        assert!(
20664            c.upgrade_from().is_empty(),
20665            "template caixa must carry an empty :upgrade-from — got {:?}",
20666            c.upgrade_from()
20667        );
20668        c.validate_upgrade_from()
20669            .expect("empty :upgrade-from must pass the compound gate cleanly");
20670    }
20671
20672    // ── Caixa::validate_limits — compound per-Caixa entry gate on   ──
20673    // ── the M2 `:limits` slot: folds the                            ──
20674    // ── [`crate::LimitsSpec::validate`] four-axis cascade on the    ──
20675    // ── present-slot arm and the `Option::None` identity element on ──
20676    // ── the absent-slot arm onto one substrate primitive.           ──
20677    // ── Byte-for-byte equivalent to the pre-fold                    ──
20678    // ── `if let Some(l) = caixa.limits() { l.validate() }`          ──
20679    // ── unwrap-and-dispatch pattern at                              ──
20680    // ── `crate::layout::StandardLayout::verify` (`layout.rs`).      ──
20681
20682    #[test]
20683    fn validate_limits_folds_arm_matches_gate() {
20684        // Fail-before-pass-after per-arm equivalence pin on the
20685        // present-slot arm: a fixture whose `:limits` carries a
20686        // zero-floor-violating `:fuel` (`Some(0)`, which
20687        // [`crate::LimitsSpec::validate`] rejects through
20688        // [`crate::LimitsError::FuelZero`]) surfaces the same
20689        // [`crate::LimitsError`] byte-equal through both the compound
20690        // gate [`Caixa::validate_limits`] and the standalone
20691        // [`crate::LimitsSpec::validate`] gate on the same `LimitsSpec`
20692        // value. Pins the fold — a silent regression that de-folded
20693        // the present-slot arm would surface here as a mismatch
20694        // between the two dispatches. Sibling in shape to the peer
20695        // per-arm equivalence pins the
20696        // [`crate::AplicacaoSpec::validate_contratos`] /
20697        // [`crate::MeshPolicy::validate`] /
20698        // [`crate::SupervisorSpec::validate_children`] /
20699        // [`Caixa::validate_upgrade_from`] compound gates each carry
20700        // on their axes.
20701        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20702        let l = crate::LimitsSpec {
20703            memory: None,
20704            fuel: Some(0),
20705            wall_clock: None,
20706            cpu: None,
20707        };
20708        c.limits = Some(l);
20709        let via_method = c.validate_limits().unwrap_err();
20710        let via_standalone = l.validate().unwrap_err();
20711        assert_eq!(
20712            via_method, via_standalone,
20713            "Caixa::validate_limits must surface the present-slot \
20714             arm's diagnostic byte-equal to the standalone \
20715             `LimitsSpec::validate` on the same `LimitsSpec` value"
20716        );
20717        assert!(
20718            matches!(via_method, crate::LimitsError::FuelZero),
20719            "expected FuelZero on the zero-floor-violating `:fuel`, \
20720             got {via_method:?}"
20721        );
20722    }
20723
20724    #[test]
20725    fn validate_limits_accepts_none() {
20726        // Positive control on the absent-slot arm (the fold's identity
20727        // element): a caixa without any `:limits` block (the
20728        // canonical "no bound declared — engine-default applies"
20729        // author shape [`crate::LimitsSpec::is_empty`]'s per-axis
20730        // `None` cascade reads, and the shape the [`Caixa::template`]
20731        // scaffold emits by construction) passes the compound gate
20732        // cleanly, regardless of any per-axis defect a subsequent
20733        // `Some(_)` binding would surface. Pins the identity element
20734        // of the fold on the absent-slot side, matching the peer
20735        // `validate_upgrade_from_accepts_empty_upgrade_from` positive-
20736        // control posture on the sibling M2 slot.
20737        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20738        assert!(
20739            c.limits().is_none(),
20740            "template caixa must carry an absent :limits — got {:?}",
20741            c.limits()
20742        );
20743        c.validate_limits()
20744            .expect("absent :limits must pass the compound gate cleanly");
20745    }
20746
20747    #[test]
20748    fn validate_limits_accepts_clean_fixture() {
20749        // Positive control on the present-slot arm: a caixa whose
20750        // `:limits` is `Some(LimitsSpec::default())` (all four axes
20751        // `None` — every axis absent under the outer `Some(_)`
20752        // binding, so every present-slot arm on
20753        // [`crate::LimitsSpec::validate`] is vacuous) passes the
20754        // compound gate cleanly. A future tightening of any one axis
20755        // that surfaces a diagnostic on the all-`None` `LimitsSpec`
20756        // would land here as a test failure first. Pins the
20757        // present-slot arm's accept-shape on the canonical
20758        // "declared-but-empty" author fixture the
20759        // `limits_round_trip_via_json` peer already round-trips
20760        // (`caixa-core/src/manifest.rs:6971`).
20761        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20762        c.limits = Some(crate::LimitsSpec::default());
20763        c.validate_limits()
20764            .expect("Some(LimitsSpec::default()) must pass the compound gate cleanly");
20765    }
20766
20767    // ── Caixa::validate_behavior — compound per-Caixa entry gate on ──
20768    // ── the M2 `:behavior` slot's pure value-shape surface: folds   ──
20769    // ── the [`crate::BehaviorSpec::validate`] six-slot cascade on   ──
20770    // ── the present-slot arm and the `Option::None` identity        ──
20771    // ── element on the absent-slot arm onto one substrate primitive.──
20772    // ── Byte-for-byte equivalent to the pre-fold                    ──
20773    // ── `if let Some(b) = caixa.behavior() { b.validate() }`        ──
20774    // ── unwrap-and-dispatch pattern at                              ──
20775    // ── `crate::layout::StandardLayout::verify` (`layout.rs`). The  ──
20776    // ── on-disk callback-path existence walk stays open-coded at    ──
20777    // ── the layout altitude because it needs the                    ──
20778    // ── [`crate::layout::LayoutInvariants::exists`] filesystem       ──
20779    // ── oracle the pure typed-shape surface has no reference to —   ──
20780    // ── mirror of the peer M2 `:upgrade-from` per-instruction       ──
20781    // ── script-path existence probe that stayed at the layout       ──
20782    // ── altitude after the [`Caixa::validate_upgrade_from`] lift    ──
20783    // ── (d6801df) for the same reason.                              ──
20784
20785    #[test]
20786    fn validate_behavior_folds_arm_matches_gate() {
20787        // Fail-before-pass-after per-arm equivalence pin on the
20788        // present-slot arm: a fixture whose `:behavior` carries an
20789        // absolute-path `:on-init` (`"/etc/passwd"`, which
20790        // [`crate::BehaviorSpec::validate`] rejects through
20791        // [`crate::BehaviorError::AbsolutePath`]) surfaces the same
20792        // [`crate::BehaviorError`] byte-equal through both the
20793        // compound gate [`Caixa::validate_behavior`] and the standalone
20794        // [`crate::BehaviorSpec::validate`] gate on the same
20795        // `BehaviorSpec` value. Pins the fold — a silent regression
20796        // that de-folded the present-slot arm would surface here as a
20797        // mismatch between the two dispatches. Sibling in shape to the
20798        // peer per-arm equivalence pins the
20799        // [`Caixa::validate_limits`] (baa4688),
20800        // [`Caixa::validate_upgrade_from`] (d6801df),
20801        // [`crate::MeshPolicy::validate`],
20802        // [`crate::AplicacaoSpec::validate_contratos`], and
20803        // [`crate::SupervisorSpec::validate_children`] compound gates
20804        // each carry on their axes.
20805        use crate::BehaviorSpec;
20806        use std::path::PathBuf;
20807        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20808        let b = BehaviorSpec {
20809            on_init: Some(PathBuf::from("/etc/passwd")),
20810            ..Default::default()
20811        };
20812        c.behavior = Some(b.clone());
20813        let via_method = c.validate_behavior().unwrap_err();
20814        let via_standalone = b.validate().unwrap_err();
20815        assert_eq!(
20816            via_method, via_standalone,
20817            "Caixa::validate_behavior must surface the present-slot \
20818             arm's diagnostic byte-equal to the standalone \
20819             `BehaviorSpec::validate` on the same `BehaviorSpec` value"
20820        );
20821        assert!(
20822            matches!(via_method, crate::BehaviorError::AbsolutePath { .. }),
20823            "expected AbsolutePath on the absolute `:on-init` path, \
20824             got {via_method:?}"
20825        );
20826    }
20827
20828    #[test]
20829    fn validate_behavior_accepts_none() {
20830        // Positive control on the absent-slot arm (the fold's identity
20831        // element): a caixa without any `:behavior` block (the
20832        // canonical "no callback declared — the runtime falls back to
20833        // the wasm-engine's default per arm" author shape
20834        // [`crate::BehaviorSpec::is_empty`]'s per-slot `None` cascade
20835        // reads, and the shape the [`Caixa::template`] scaffold emits
20836        // by construction) passes the compound gate cleanly,
20837        // regardless of any per-slot defect a subsequent `Some(_)`
20838        // binding would surface. Pins the identity element of the fold
20839        // on the absent-slot side, matching the peer
20840        // `validate_limits_accepts_none` (baa4688) and
20841        // `validate_upgrade_from_accepts_empty_upgrade_from` (d6801df)
20842        // positive-control postures on the sibling M2 slots.
20843        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20844        assert!(
20845            c.behavior().is_none(),
20846            "template caixa must carry an absent :behavior — got {:?}",
20847            c.behavior()
20848        );
20849        c.validate_behavior()
20850            .expect("absent :behavior must pass the compound gate cleanly");
20851    }
20852
20853    #[test]
20854    fn validate_behavior_accepts_clean_fixture() {
20855        // Positive control on the present-slot arm: a caixa whose
20856        // `:behavior` is `Some(BehaviorSpec::default())` (all six
20857        // slots `None` — every slot absent under the outer `Some(_)`
20858        // binding, so every present-slot arm on
20859        // [`crate::BehaviorSpec::validate`] is vacuous) passes the
20860        // compound gate cleanly. A future tightening of any one arm
20861        // that surfaces a diagnostic on the all-`None` `BehaviorSpec`
20862        // would land here as a test failure first. Pins the
20863        // present-slot arm's accept-shape on the canonical
20864        // "declared-but-empty" author fixture the sibling
20865        // `empty_behavior_round_trip` peer already round-trips
20866        // (`caixa-core/src/behavior.rs` tests). Mirror of the peer
20867        // `validate_limits_accepts_clean_fixture` (baa4688)
20868        // positive-control posture on the sibling M2 `:limits` slot.
20869        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20870        c.behavior = Some(crate::BehaviorSpec::default());
20871        c.validate_behavior()
20872            .expect("Some(BehaviorSpec::default()) must pass the compound gate cleanly");
20873    }
20874
20875    // ── Caixa::validate_deps — compound per-Caixa entry gate on the ──
20876    // ── dep-graph axis: folds the two standalone validators         ──
20877    // ── (per-entry + within-list duplicate walk that this method    ──
20878    // ── opened on, cross-slot self-edge via                         ──
20879    // ── `crate::dep::validate_no_self_dep`) onto one substrate      ──
20880    // ── primitive. Byte-for-byte equivalent to the pre-fold         ──
20881    // ── two-block cascade at                                        ──
20882    // ── `crate::layout::StandardLayout::verify` under the same      ──
20883    // ── canonical dispatch order (per-entry → self-edge).           ──
20884
20885    #[test]
20886    fn validate_deps_folds_per_entry_arm_matches_gate() {
20887        // Fail-before-pass-after per-arm equivalence pin on the
20888        // per-entry + within-list duplicate axis: a fixture whose
20889        // `:deps` carries a per-entry-invalid `:versao` (`"^bad"`,
20890        // which [`crate::parse_requirement`] rejects) surfaces the
20891        // same [`crate::DepError`] through the compound gate
20892        // [`Caixa::validate_deps`] and the standalone per-entry walk
20893        // ([`Dep::validate`]) on the offending entry. Pins the
20894        // fold — a silent regression that de-folded the per-entry arm
20895        // would surface here as a mismatch between the two
20896        // dispatches. Sibling in shape to the peer
20897        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
20898        // per-arm equivalence pin (d6801df) on the M2
20899        // `:upgrade-from` compound gate's per-entry arm, extended
20900        // here onto the universal-axis `:deps` compound gate's
20901        // per-entry arm.
20902        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20903        c.deps = vec![Dep::simple("d", "^bad")];
20904        let via_method = c.validate_deps().unwrap_err();
20905        let via_standalone = c.deps()[0].validate().unwrap_err();
20906        assert_eq!(
20907            via_method, via_standalone,
20908            "Caixa::validate_deps must surface the per-entry arm's \
20909             diagnostic byte-equal to the standalone \
20910             `Dep::validate` on the same offending entry",
20911        );
20912        assert!(
20913            matches!(
20914                via_method,
20915                DepError::VersaoInvalid { ref nome, .. } if nome == "d"
20916            ),
20917            "expected VersaoInvalid on the malformed :versao, got {via_method:?}",
20918        );
20919    }
20920
20921    #[test]
20922    fn validate_deps_folds_self_edge_arm_matches_gate() {
20923        // Per-arm equivalence pin on the cross-slot self-edge axis:
20924        // a fixture whose `:deps` lists the caixa's own `:nome`
20925        // (a self-dep, which
20926        // [`crate::dep::validate_no_self_dep`] rejects as a
20927        // structurally-invalid one-node cycle in the lacre closure's
20928        // dep-graph) surfaces the same [`crate::DepError::DepIsSelf`]
20929        // through both the compound gate and the standalone
20930        // [`crate::dep::validate_no_self_dep`] gate keyed off the
20931        // same `(deps, deps_dev, nome)` triple. Pins the fold's
20932        // second arm — reaching this arm through the compound gate
20933        // requires the per-entry + within-list duplicate walk to
20934        // pass first, which itself pins one cross-arm ordering step.
20935        // Sibling in shape to the peer
20936        // `validate_upgrade_from_folds_versao_arm_matches_gate` /
20937        // `_folds_behavior_arm_matches_gate` cross-slot equivalence
20938        // pins (d6801df) on the M2 `:upgrade-from` compound gate's
20939        // cross-slot arms.
20940        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20941        c.deps = vec![Dep::simple("demo", "^0.1")];
20942        let via_method = c.validate_deps().unwrap_err();
20943        let via_standalone =
20944            crate::dep::validate_no_self_dep(c.deps(), c.deps_dev(), c.nome()).unwrap_err();
20945        assert_eq!(
20946            via_method, via_standalone,
20947            "Caixa::validate_deps must surface the cross-slot \
20948             self-edge diagnostic byte-equal to the standalone \
20949             `crate::dep::validate_no_self_dep` on the same \
20950             (deps, deps_dev, nome) triple",
20951        );
20952        assert!(
20953            matches!(
20954                via_method,
20955                DepError::DepIsSelf { ref nome, list }
20956                    if nome == "demo" && list == crate::render::DEP_AUTHOR_KEY_DEPS
20957            ),
20958            "expected DepIsSelf carrying (nome=\"demo\", list=\":deps\"), got {via_method:?}",
20959        );
20960    }
20961
20962    #[test]
20963    fn validate_deps_per_entry_arm_fires_before_self_edge_arm() {
20964        // Cross-arm ordering pin between the two arms of the fold:
20965        // a fixture carrying BOTH a per-entry-invalid `:versao`
20966        // (`"^bad"` — [`crate::parse_requirement`] rejects the
20967        // requirement grammar) on a non-self-dep entry AND a
20968        // would-be self-edge violation on a second entry (the
20969        // caixa's own `:nome` "demo") surfaces the per-entry
20970        // diagnostic first through the compound gate. Sanity
20971        // assertion: the second entry alone under the same parent
20972        // `:nome` trips the self-edge arm on its own via the
20973        // standalone [`crate::dep::validate_no_self_dep`], so the
20974        // per-entry-first surfacing is a real ordering property,
20975        // not a case where the self-edge arm silently accepts the
20976        // fixture. Pins the pre-fold layout wire-up's canonical
20977        // dispatch order (per-entry + within-list duplicate →
20978        // self-edge) as a property of the substrate primitive
20979        // rather than a convention of the layout call site. Sibling
20980        // in shape to
20981        // `validate_upgrade_from_per_entry_arm_fires_before_versao_arm`
20982        // (d6801df) on the M2 `:upgrade-from` compound gate's
20983        // per-arm ordering property.
20984        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20985        c.deps = vec![
20986            Dep::simple("orquestra", "^bad"),
20987            Dep::simple("demo", "^0.1"),
20988        ];
20989        let err = c.validate_deps().unwrap_err();
20990        assert!(
20991            matches!(
20992                err,
20993                DepError::VersaoInvalid { ref nome, .. } if nome == "orquestra"
20994            ),
20995            "per-entry arm must fire before self-edge arm — expected \
20996             VersaoInvalid on \"orquestra\", got {err:?}",
20997        );
20998        // Sanity: the self-referential entry alone under the same
20999        // parent `:nome` trips the self-edge arm on its own — proves
21000        // the per-entry-first surfacing above is a real ordering
21001        // property, not a case where the self-edge arm silently
21002        // accepts the fixture.
21003        let sanity = crate::dep::validate_no_self_dep(&[Dep::simple("demo", "^0.1")], &[], "demo")
21004            .unwrap_err();
21005        assert!(
21006            matches!(sanity, DepError::DepIsSelf { ref nome, .. } if nome == "demo"),
21007            "sanity: the self-referential entry alone must trip the \
21008             self-edge arm — got {sanity:?}",
21009        );
21010    }
21011
21012    #[test]
21013    fn validate_deps_accepts_clean_fixture() {
21014        // Positive control: a well-formed dep-graph (one `:deps`
21015        // entry naming a non-self DNS-1123 nome + Cargo-shaped
21016        // requirement, one `:deps-dev` entry on a distinct non-self
21017        // nome) passes the compound gate cleanly. A future
21018        // tightening of either arm's accepted set surfaces here as
21019        // a test failure first. Mirrors the peer
21020        // `validate_upgrade_from_accepts_clean_fixture` positive-
21021        // control posture on the sibling per-Caixa compound gate.
21022        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21023        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
21024        c.deps_dev = vec![Dep::simple("caixa-lint", "^0.2")];
21025        c.validate_deps()
21026            .expect("clean fixture must pass the compound `:deps` gate");
21027    }
21028
21029    #[test]
21030    fn validate_deps_accepts_empty_deps_lists() {
21031        // Positive control on the empty-list arm: a caixa without
21032        // any `:deps` or `:deps-dev` entries (the default
21033        // `Vec::new()` `#[serde(default)]` folds an omitted slot
21034        // onto) passes the compound gate cleanly regardless of
21035        // `:nome` — both the per-entry walk and the self-edge walk
21036        // are vacuous on the empty entry list. Pins the identity
21037        // element of the fold on the empty-slot side, peer with the
21038        // `validate_upgrade_from_accepts_empty_upgrade_from` empty-
21039        // arm positive control (d6801df) on the sibling
21040        // `:upgrade-from` compound gate.
21041        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21042        assert!(
21043            c.deps().is_empty(),
21044            "template caixa must carry an empty :deps — got {:?}",
21045            c.deps(),
21046        );
21047        assert!(
21048            c.deps_dev().is_empty(),
21049            "template caixa must carry an empty :deps-dev — got {:?}",
21050            c.deps_dev(),
21051        );
21052        c.validate_deps()
21053            .expect("empty :deps / :deps-dev must pass the compound gate cleanly");
21054    }
21055
21056    // ── Caixa::validate_aplicacao_shape — compound per-Caixa gate ────────
21057
21058    /// Build a minimal well-formed Aplicacao fixture on top of the
21059    /// canonical template. Every arm of the compound gate then patches
21060    /// exactly one axis away from clean so its per-arm diagnostic
21061    /// surfaces without collateral noise from a peer slot.
21062    fn aplicacao_fixture(nome: &str) -> Caixa {
21063        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
21064        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21065        c.kind = CaixaKind::Aplicacao;
21066        c.bibliotecas = vec![];
21067        c.membros = vec![
21068            Membro {
21069                caixa: "checkout".into(),
21070                versao: "^0.1".into(),
21071            },
21072            Membro {
21073                caixa: "cart".into(),
21074                versao: "^0.1".into(),
21075            },
21076        ];
21077        // `:placement` defaults to `Replicated` with an empty
21078        // `:clusters` list which
21079        // [`crate::AplicacaoSpec::validate_placement`] refuses; every
21080        // per-strategy variant needs at least one named cluster (per
21081        // MESH-COMPOSITION §II.1). Pin a single-cluster `SingleNode`
21082        // placement so the typed-shape cascade passes cleanly and the
21083        // per-arm fixtures below can each patch exactly one axis.
21084        c.placement = Some(Placement {
21085            estrategia: PlacementStrategy::SingleNode,
21086            clusters: vec!["rio".into()],
21087            shard_key: None,
21088            affinity: None,
21089        });
21090        c
21091    }
21092
21093    #[test]
21094    fn validate_aplicacao_shape_folds_view_arm_matches_gate() {
21095        // Fail-before-pass-after per-arm equivalence pin on the
21096        // typed-shape cascade arm: a fixture whose typed
21097        // [`crate::AplicacaoSpec`] view fails
21098        // [`crate::AplicacaoSpec::validate`] (here — empty `:membros`,
21099        // which [`crate::AplicacaoSpec::validate_membros`] rejects as
21100        // [`crate::AplicacaoError::NoMembros`] at the first per-slot
21101        // gate) surfaces the same [`crate::AplicacaoError`] diagnostic
21102        // through both the compound gate
21103        // [`Caixa::validate_aplicacao_shape`] and the standalone
21104        // [`crate::AplicacaoSpec::validate`] on the same folded view.
21105        // Pins the fold — a silent regression that de-folded the
21106        // typed-shape arm would surface here as a mismatch between the
21107        // two dispatches. Sibling in shape to the peer
21108        // `validate_deps_folds_per_entry_arm_matches_gate` (b5dd55e) /
21109        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21110        // (d6801df) per-arm equivalence pins on the sibling per-slot
21111        // compound gates.
21112        let mut c = aplicacao_fixture("demo");
21113        c.membros = vec![];
21114        let via_method = c.validate_aplicacao_shape().unwrap_err();
21115        let via_standalone = c.aplicacao_view().unwrap().validate().unwrap_err();
21116        assert_eq!(
21117            via_method, via_standalone,
21118            "Caixa::validate_aplicacao_shape must surface the typed-\
21119             shape arm's diagnostic byte-equal to the standalone \
21120             `AplicacaoSpec::validate` on the same folded view",
21121        );
21122        assert!(
21123            matches!(via_method, crate::AplicacaoError::NoMembros),
21124            "expected NoMembros on the empty :membros, got {via_method:?}",
21125        );
21126    }
21127
21128    #[test]
21129    fn validate_aplicacao_shape_folds_self_membership_arm_matches_gate() {
21130        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21131        // fixture whose `:membros` names the Aplicacao's own `:nome`
21132        // (which [`crate::aplicacao::validate_no_self_membership`]
21133        // rejects as [`crate::AplicacaoError::MembroIsSelfAplicacao`],
21134        // a one-node lacre-closure recursion in the Aplicacao's
21135        // mesh-graph) surfaces the same
21136        // [`crate::AplicacaoError::MembroIsSelfAplicacao`] through both
21137        // the compound gate and the standalone
21138        // [`crate::aplicacao::validate_no_self_membership`] keyed off
21139        // the same `(membros, nome)` pair. Pins the fold's second arm
21140        // — reaching this arm through the compound gate requires the
21141        // typed-shape cascade to pass first, which itself pins one
21142        // cross-arm ordering step. Sibling in shape to the peer
21143        // `validate_deps_folds_self_edge_arm_matches_gate` (b5dd55e)
21144        // cross-slot equivalence pin on the sibling per-slot compound
21145        // gate.
21146        use crate::aplicacao::Membro;
21147        let mut c = aplicacao_fixture("demo");
21148        c.membros = vec![Membro {
21149            caixa: "demo".into(),
21150            versao: "^0.1".into(),
21151        }];
21152        let via_method = c.validate_aplicacao_shape().unwrap_err();
21153        let via_standalone =
21154            crate::aplicacao::validate_no_self_membership(c.membros(), c.nome()).unwrap_err();
21155        assert_eq!(
21156            via_method, via_standalone,
21157            "Caixa::validate_aplicacao_shape must surface the cross-\
21158             slot self-edge diagnostic byte-equal to the standalone \
21159             `aplicacao::validate_no_self_membership` on the same \
21160             (membros, nome) pair",
21161        );
21162        assert!(
21163            matches!(
21164                via_method,
21165                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa } if caixa == "demo"
21166            ),
21167            "expected MembroIsSelfAplicacao carrying (caixa=\"demo\"), \
21168             got {via_method:?}",
21169        );
21170    }
21171
21172    #[test]
21173    fn validate_aplicacao_shape_view_arm_fires_before_self_membership_arm() {
21174        // Cross-arm ordering pin between the two arms of the fold: a
21175        // fixture carrying BOTH a typed-shape violation (a `:contratos`
21176        // edge whose `:para` is not a declared member — rejected by
21177        // [`crate::AplicacaoSpec::validate_contratos`] as
21178        // [`crate::AplicacaoError::ContratoMemberMissing`]) AND a
21179        // would-be self-edge violation (a `:membros` entry naming the
21180        // caixa's own `:nome`) surfaces the typed-shape diagnostic
21181        // first through the compound gate. Sanity assertion: the
21182        // self-referential `:membros` entry alone under the same
21183        // parent `:nome` trips the self-edge arm on its own via the
21184        // standalone [`crate::aplicacao::validate_no_self_membership`],
21185        // so the typed-shape-first surfacing is a real ordering
21186        // property, not a case where the self-edge arm silently
21187        // accepts the fixture. Pins the pre-fold layout wire-up's
21188        // canonical dispatch order (typed-shape cascade → cross-slot
21189        // self-edge) as a property of the substrate primitive rather
21190        // than a convention of the layout call site. Sibling in shape
21191        // to `validate_deps_per_entry_arm_fires_before_self_edge_arm`
21192        // (b5dd55e) on the sibling per-slot compound gate's per-arm
21193        // ordering property.
21194        use crate::aplicacao::{Membro, WitContract};
21195        let mut c = aplicacao_fixture("demo");
21196        c.membros = vec![Membro {
21197            caixa: "demo".into(),
21198            versao: "^0.1".into(),
21199        }];
21200        c.contratos = vec![WitContract {
21201            de: "demo".into(),
21202            para: "orphan".into(),
21203            wit: "wasi:http/proxy".into(),
21204            endpoint: Some("/x".into()),
21205            subject: None,
21206            slot: None,
21207        }];
21208        let err = c.validate_aplicacao_shape().unwrap_err();
21209        assert!(
21210            matches!(
21211                err,
21212                crate::AplicacaoError::ContratoMemberMissing { ref caixa }
21213                    if caixa == "orphan"
21214            ),
21215            "typed-shape arm must fire before self-edge arm — expected \
21216             ContratoMemberMissing on \"orphan\", got {err:?}",
21217        );
21218        // Sanity: the self-referential `:membros` entry alone under
21219        // the same parent `:nome` trips the self-edge arm on its own
21220        // — proves the typed-shape-first surfacing above is a real
21221        // ordering property, not a case where the self-edge arm
21222        // silently accepts the fixture.
21223        let sanity = crate::aplicacao::validate_no_self_membership(
21224            &[Membro {
21225                caixa: "demo".into(),
21226                versao: "^0.1".into(),
21227            }],
21228            "demo",
21229        )
21230        .unwrap_err();
21231        assert!(
21232            matches!(
21233                sanity,
21234                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa }
21235                    if caixa == "demo"
21236            ),
21237            "sanity: the self-referential :membros entry alone must \
21238             trip the self-edge arm — got {sanity:?}",
21239        );
21240    }
21241
21242    #[test]
21243    fn validate_aplicacao_shape_accepts_non_aplicacao_kind() {
21244        // Positive control on the identity-element arm: every non-
21245        // Aplicacao kind passes the compound gate trivially — the
21246        // paired [`Caixa::aplicacao_view`] accessor returns `None`
21247        // off the Aplicacao arm (by construction, keyed on
21248        // `caixa.kind().is_aplicacao()`), so the fold short-circuits
21249        // to `Ok(())` without touching the mesh slots. Pins the
21250        // identity element on every non-Aplicacao kind — a future
21251        // refactor that made the mesh-slot cascade fire on the wrong
21252        // kind (say, on a `Servico` whose mesh slots happen to be
21253        // populated in a mis-authored manifest, which the peer
21254        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
21255        // coherence gate would refuse upstream anyway) surfaces here
21256        // as a test failure first. Peer with the
21257        // `validate_limits_accepts_none` / `validate_behavior_accepts_none`
21258        // identity-element pins on the sibling M2 `Option`-shaped
21259        // per-Caixa compound gates.
21260        for kind in [
21261            CaixaKind::Biblioteca,
21262            CaixaKind::Binario,
21263            CaixaKind::Servico,
21264            CaixaKind::Supervisor,
21265            CaixaKind::Acao,
21266        ] {
21267            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21268            c.kind = kind;
21269            assert!(
21270                c.aplicacao_view().is_none(),
21271                "aplicacao_view must return None off the Aplicacao arm \
21272                 for kind {kind:?}",
21273            );
21274            c.validate_aplicacao_shape().expect(
21275                "non-Aplicacao kinds must pass the compound gate as the fold's identity element",
21276            );
21277        }
21278    }
21279
21280    #[test]
21281    fn validate_aplicacao_shape_accepts_clean_fixture() {
21282        // Positive control: a well-formed Aplicacao (two DNS-1123
21283        // members with valid semver constraints, no `:contratos` /
21284        // `:entrada` / `:placement` / `:politicas` set — every
21285        // per-slot gate accepts the vacuous / omitted arm) passes the
21286        // compound gate cleanly. A future tightening of either arm's
21287        // accepted set surfaces here as a test failure first. Mirrors
21288        // the peer `validate_deps_accepts_clean_fixture` (b5dd55e) /
21289        // `validate_upgrade_from_accepts_clean_fixture` (d6801df)
21290        // positive-control postures on the sibling per-Caixa
21291        // compound gates.
21292        let c = aplicacao_fixture("demo");
21293        c.validate_aplicacao_shape()
21294            .expect("clean Aplicacao fixture must pass the compound gate");
21295    }
21296
21297    // ── Caixa::validate_supervisor_shape — compound per-Caixa gate ───────
21298
21299    /// Build a minimal well-formed Supervisor fixture on top of the
21300    /// canonical template. Every arm of the compound gate then patches
21301    /// exactly one axis away from clean so its per-arm diagnostic
21302    /// surfaces without collateral noise from a peer slot. Peer of
21303    /// [`aplicacao_fixture`] on the sibling per-Aplicacao compound
21304    /// gate's pin family.
21305    fn supervisor_fixture(nome: &str) -> Caixa {
21306        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
21307        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21308        c.kind = CaixaKind::Supervisor;
21309        // Supervisors don't run code — clear the biblioteca slot the
21310        // template seeds so the fold's per-arm diagnostics surface
21311        // without the peer `SupervisorOwnsCode` kind-coherence gate
21312        // firing upstream at the layout altitude.
21313        c.bibliotecas = vec![];
21314        // `:estrategia` defaults to `OneForOne` at the typed view level,
21315        // and `OneForOne` requires at least one `:children` entry — pin
21316        // a single-child `Permanent` worker so the typed-shape cascade
21317        // passes cleanly and the per-arm fixtures below can each patch
21318        // exactly one axis.
21319        c.estrategia = Some(RestartStrategy::OneForOne);
21320        c.children = vec![ChildSpec {
21321            caixa: "worker".into(),
21322            versao: "^0.1".into(),
21323            restart: RestartPolicy::Permanent,
21324        }];
21325        c
21326    }
21327
21328    #[test]
21329    fn validate_supervisor_shape_folds_view_arm_matches_gate() {
21330        // Fail-before-pass-after per-arm equivalence pin on the
21331        // typed-shape cascade arm: a fixture whose typed
21332        // [`crate::SupervisorSpec`] view fails
21333        // [`crate::SupervisorSpec::validate`] (here — a duplicate
21334        // `:children` `:caixa` entry, which
21335        // [`crate::SupervisorSpec::validate`]'s set-not-multiset gate
21336        // rejects as [`crate::SupervisorError::DuplicateChildCaixa`])
21337        // surfaces the same [`crate::SupervisorError`] diagnostic
21338        // through both the compound gate
21339        // [`Caixa::validate_supervisor_shape`] and the standalone
21340        // [`crate::SupervisorSpec::validate`] on the same folded view.
21341        // Pins the fold — a silent regression that de-folded the
21342        // typed-shape arm would surface here as a mismatch between the
21343        // two dispatches. Sibling in shape to the peer
21344        // `validate_aplicacao_shape_folds_view_arm_matches_gate`
21345        // (949a7a0) on the sibling per-Aplicacao compound gate.
21346        use crate::supervisor::{ChildSpec, RestartPolicy};
21347        let mut c = supervisor_fixture("demo");
21348        c.children = vec![
21349            ChildSpec {
21350                caixa: "worker".into(),
21351                versao: "^0.1".into(),
21352                restart: RestartPolicy::Permanent,
21353            },
21354            ChildSpec {
21355                caixa: "worker".into(),
21356                versao: "^0.1".into(),
21357                restart: RestartPolicy::Permanent,
21358            },
21359        ];
21360        let via_method = c.validate_supervisor_shape().unwrap_err();
21361        let via_standalone = c.supervisor_view().unwrap().validate().unwrap_err();
21362        assert_eq!(
21363            via_method, via_standalone,
21364            "Caixa::validate_supervisor_shape must surface the typed-\
21365             shape arm's diagnostic byte-equal to the standalone \
21366             `SupervisorSpec::validate` on the same folded view",
21367        );
21368        assert!(
21369            matches!(
21370                via_method,
21371                crate::SupervisorError::DuplicateChildCaixa { ref caixa }
21372                    if caixa == "worker"
21373            ),
21374            "expected DuplicateChildCaixa on the duplicate 'worker' \
21375             child, got {via_method:?}",
21376        );
21377    }
21378
21379    #[test]
21380    fn validate_supervisor_shape_folds_self_supervision_arm_matches_gate() {
21381        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21382        // fixture whose `:children :caixa` names the Supervisor's own
21383        // `:nome` (which
21384        // [`crate::supervisor::validate_no_self_supervision`] rejects
21385        // as [`crate::SupervisorError::ChildSupervisesSelf`], a
21386        // one-node reconciliation cycle in the supervisor's
21387        // supervision-tree) surfaces the same
21388        // [`crate::SupervisorError::ChildSupervisesSelf`] through both
21389        // the compound gate and the standalone
21390        // [`crate::supervisor::validate_no_self_supervision`] keyed
21391        // off the same `(children, nome)` pair. Pins the fold's
21392        // second arm — reaching this arm through the compound gate
21393        // requires the typed-shape cascade to pass first, which itself
21394        // pins one cross-arm ordering step. Sibling in shape to the
21395        // peer
21396        // `validate_aplicacao_shape_folds_self_membership_arm_matches_gate`
21397        // (949a7a0) cross-slot equivalence pin on the sibling
21398        // per-Aplicacao compound gate.
21399        use crate::supervisor::{ChildSpec, RestartPolicy};
21400        let mut c = supervisor_fixture("demo");
21401        c.children = vec![ChildSpec {
21402            caixa: "demo".into(),
21403            versao: "^0.1".into(),
21404            restart: RestartPolicy::Permanent,
21405        }];
21406        let via_method = c.validate_supervisor_shape().unwrap_err();
21407        let via_standalone =
21408            crate::supervisor::validate_no_self_supervision(c.children(), c.nome()).unwrap_err();
21409        assert_eq!(
21410            via_method, via_standalone,
21411            "Caixa::validate_supervisor_shape must surface the cross-\
21412             slot self-edge diagnostic byte-equal to the standalone \
21413             `supervisor::validate_no_self_supervision` on the same \
21414             (children, nome) pair",
21415        );
21416        assert!(
21417            matches!(
21418                via_method,
21419                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21420            ),
21421            "expected ChildSupervisesSelf carrying (caixa=\"demo\"), \
21422             got {via_method:?}",
21423        );
21424    }
21425
21426    #[test]
21427    fn validate_supervisor_shape_view_arm_fires_before_self_supervision_arm() {
21428        // Cross-arm ordering pin between the two arms of the fold: a
21429        // fixture carrying BOTH a typed-shape violation (a per-child
21430        // empty `:caixa` name — rejected by
21431        // [`crate::SupervisorSpec::validate`] as
21432        // [`crate::SupervisorError::EmptyChildName`]) AND a would-be
21433        // self-edge violation (a `:children` entry naming the
21434        // supervisor's own `:nome`) surfaces the typed-shape
21435        // diagnostic first through the compound gate. Sanity
21436        // assertion: the self-referential `:children` entry alone
21437        // under the same parent `:nome` trips the self-edge arm on
21438        // its own via the standalone
21439        // [`crate::supervisor::validate_no_self_supervision`], so the
21440        // typed-shape-first surfacing is a real ordering property, not
21441        // a case where the self-edge arm silently accepts the fixture.
21442        // Pins the pre-fold layout wire-up's canonical dispatch order
21443        // (typed-shape cascade → cross-slot self-edge) as a property
21444        // of the substrate primitive rather than a convention of the
21445        // layout call site. Sibling in shape to
21446        // `validate_aplicacao_shape_view_arm_fires_before_self_membership_arm`
21447        // (949a7a0) on the sibling per-Aplicacao compound gate.
21448        use crate::supervisor::{ChildSpec, RestartPolicy};
21449        let mut c = supervisor_fixture("demo");
21450        c.children = vec![
21451            ChildSpec {
21452                caixa: String::new(),
21453                versao: "^0.1".into(),
21454                restart: RestartPolicy::Permanent,
21455            },
21456            ChildSpec {
21457                caixa: "demo".into(),
21458                versao: "^0.1".into(),
21459                restart: RestartPolicy::Permanent,
21460            },
21461        ];
21462        let err = c.validate_supervisor_shape().unwrap_err();
21463        assert!(
21464            matches!(err, crate::SupervisorError::EmptyChildName),
21465            "typed-shape arm must fire before self-edge arm — expected \
21466             EmptyChildName on the empty :caixa child, got {err:?}",
21467        );
21468        // Sanity: the self-referential `:children` entry alone under
21469        // the same parent `:nome` trips the self-edge arm on its own
21470        // — proves the typed-shape-first surfacing above is a real
21471        // ordering property, not a case where the self-edge arm
21472        // silently accepts the fixture.
21473        let sanity = crate::supervisor::validate_no_self_supervision(
21474            &[ChildSpec {
21475                caixa: "demo".into(),
21476                versao: "^0.1".into(),
21477                restart: RestartPolicy::Permanent,
21478            }],
21479            "demo",
21480        )
21481        .unwrap_err();
21482        assert!(
21483            matches!(
21484                sanity,
21485                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21486            ),
21487            "sanity: the self-referential :children entry alone must \
21488             trip the self-edge arm — got {sanity:?}",
21489        );
21490    }
21491
21492    #[test]
21493    fn validate_supervisor_shape_accepts_non_supervisor_kind() {
21494        // Positive control on the identity-element arm: every non-
21495        // Supervisor kind passes the compound gate trivially — the
21496        // paired [`Caixa::supervisor_view`] accessor returns `None`
21497        // off the Supervisor arm (by construction, keyed on
21498        // `caixa.kind().is_supervisor()`), so the fold short-circuits
21499        // to `Ok(())` without touching the supervision-tree slots.
21500        // Pins the identity element on every non-Supervisor kind — a
21501        // future refactor that made the supervision-tree cascade fire
21502        // on the wrong kind (say, on a `Servico` whose supervision
21503        // slots happen to be populated in a mis-authored manifest,
21504        // which the peer
21505        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
21506        // kind-coherence gate would refuse upstream anyway) surfaces
21507        // here as a test failure first. Peer with the
21508        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
21509        // (949a7a0) / `validate_limits_accepts_none` /
21510        // `validate_behavior_accepts_none` identity-element pins on
21511        // the sibling per-Caixa compound gates.
21512        for kind in [
21513            CaixaKind::Biblioteca,
21514            CaixaKind::Binario,
21515            CaixaKind::Servico,
21516            CaixaKind::Aplicacao,
21517            CaixaKind::Acao,
21518        ] {
21519            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21520            c.kind = kind;
21521            assert!(
21522                c.supervisor_view().is_none(),
21523                "supervisor_view must return None off the Supervisor \
21524                 arm for kind {kind:?}",
21525            );
21526            c.validate_supervisor_shape().expect(
21527                "non-Supervisor kinds must pass the compound gate as the fold's identity element",
21528            );
21529        }
21530    }
21531
21532    #[test]
21533    fn validate_supervisor_shape_accepts_clean_fixture() {
21534        // Positive control: a well-formed Supervisor (single
21535        // DNS-1123-valid `Permanent` worker child under the
21536        // `OneForOne` strategy — the OTP MaxIntensity/Period defaults
21537        // accept the vacuous `:max-restarts` / `:restart-window`
21538        // arms) passes the compound gate cleanly. A future tightening
21539        // of either arm's accepted set surfaces here as a test
21540        // failure first. Mirrors the peer
21541        // `validate_aplicacao_shape_accepts_clean_fixture` (949a7a0)
21542        // positive-control posture on the sibling per-Caixa compound
21543        // gate.
21544        let c = supervisor_fixture("demo");
21545        c.validate_supervisor_shape()
21546            .expect("clean Supervisor fixture must pass the compound gate");
21547    }
21548
21549    // ── Caixa::validate_acao_shape — compound per-Caixa gate ─────────────
21550
21551    /// Build a minimal well-formed `:kind Acao` fixture with a valid
21552    /// two-node acyclic `:ci` slot. Every arm of the compound gate
21553    /// then patches exactly one axis away from clean so its per-arm
21554    /// diagnostic surfaces without collateral noise from a peer slot.
21555    /// Peer of [`supervisor_fixture`] / [`aplicacao_fixture`] on the
21556    /// sibling per-kind compound gates' pin families.
21557    fn acao_fixture(nome: &str) -> Caixa {
21558        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21559        c.kind = CaixaKind::Acao;
21560        // Acaos don't run code — clear the biblioteca slot the template
21561        // seeds so the compound gate's per-arm diagnostics surface
21562        // without the peer `AcaoOwnsCode` kind-coherence gate firing
21563        // upstream at the layout altitude.
21564        c.bibliotecas = vec![];
21565        c.ci = Some(canteiro_types::CiRun {
21566            workspace: "pleme-io".into(),
21567            repo: "caixa".into(),
21568            nodes: vec![
21569                canteiro_types::CiNode::new(
21570                    "build",
21571                    canteiro_types::EnvClass::None,
21572                    canteiro_types::ActionRef {
21573                        name: "build".into(),
21574                        command: "true".into(),
21575                        args: vec![],
21576                    },
21577                    vec![],
21578                ),
21579                canteiro_types::CiNode::new(
21580                    "test",
21581                    canteiro_types::EnvClass::None,
21582                    canteiro_types::ActionRef {
21583                        name: "test".into(),
21584                        command: "true".into(),
21585                        args: vec![],
21586                    },
21587                    vec!["build".into()],
21588                ),
21589            ],
21590        });
21591        c
21592    }
21593
21594    #[test]
21595    fn validate_acao_shape_folds_decompose_arm_matches_gate() {
21596        // Fail-before-pass-after per-arm equivalence pin on the
21597        // decompose axis: a fixture whose `:ci` slot fails
21598        // [`canteiro_types::decompose`] (here — a minimal two-node
21599        // cycle `a → b → a`, which the sibling
21600        // [`crate::render::decompose_ci`] wraps as
21601        // [`crate::CiDecomposeFailure`] carrying
21602        // [`canteiro_types::DecomposeError::Cycle`]) surfaces the same
21603        // [`crate::CiDecomposeFailure`] diagnostic through both the
21604        // compound gate [`Caixa::validate_acao_shape`] and the
21605        // standalone [`crate::render::decompose_ci`] on the same
21606        // `(caixa, ci)` fixture. Pins the fold — a silent regression
21607        // that de-folded the decompose arm would surface here as a
21608        // mismatch between the two dispatches. Sibling in shape to the
21609        // peer `validate_supervisor_shape_folds_view_arm_matches_gate`
21610        // / `validate_aplicacao_shape_folds_view_arm_matches_gate` on
21611        // the sibling per-kind compound gates.
21612        //
21613        // [`crate::CiDecomposeFailure`] does not derive `PartialEq`
21614        // (its `#[source]` carrier [`canteiro_types::DecomposeError`]
21615        // does, but the wrapper deliberately does not), so the two
21616        // dispatches are compared through their field pair
21617        // (`nome` + `source`) rather than through `assert_eq!` on the
21618        // wrapper itself — every field on the wrapper is thereby
21619        // pinned byte-equal without depending on an implementation
21620        // detail of `CiDecomposeFailure`'s derive set.
21621        let mut c = acao_fixture("demo");
21622        c.ci = Some(canteiro_types::CiRun {
21623            workspace: "pleme-io".into(),
21624            repo: "caixa".into(),
21625            nodes: vec![
21626                canteiro_types::CiNode::new(
21627                    "a",
21628                    canteiro_types::EnvClass::None,
21629                    canteiro_types::ActionRef {
21630                        name: "a".into(),
21631                        command: "true".into(),
21632                        args: vec![],
21633                    },
21634                    vec!["b".into()],
21635                ),
21636                canteiro_types::CiNode::new(
21637                    "b",
21638                    canteiro_types::EnvClass::None,
21639                    canteiro_types::ActionRef {
21640                        name: "b".into(),
21641                        command: "true".into(),
21642                        args: vec![],
21643                    },
21644                    vec!["a".into()],
21645                ),
21646            ],
21647        });
21648        let via_method = c.validate_acao_shape().unwrap_err();
21649        let via_standalone =
21650            crate::render::decompose_ci(&c, c.ci().expect("fixture has a :ci")).unwrap_err();
21651        assert_eq!(
21652            via_method.nome, via_standalone.nome,
21653            "Caixa::validate_acao_shape must surface the decompose \
21654             failure's `nome` byte-equal to the standalone \
21655             `decompose_ci` on the same (caixa, ci) fixture",
21656        );
21657        assert_eq!(
21658            via_method.source, via_standalone.source,
21659            "Caixa::validate_acao_shape must surface the decompose \
21660             failure's `source` byte-equal to the standalone \
21661             `decompose_ci` on the same (caixa, ci) fixture",
21662        );
21663        assert_eq!(
21664            via_method.source,
21665            canteiro_types::DecomposeError::Cycle,
21666            "expected the two-node cycle `a → b → a` to surface as \
21667             DecomposeError::Cycle, got {source:?}",
21668            source = via_method.source,
21669        );
21670    }
21671
21672    #[test]
21673    fn validate_acao_shape_folds_duplicate_node_arm_matches_gate() {
21674        // Per-arm equivalence pin on the `DuplicateNode` decompose
21675        // arm — the sibling of `Cycle` on the substrate's
21676        // `canteiro_types::DecomposeError` enumeration. A fixture
21677        // whose `:ci` slot carries two nodes sharing one name
21678        // surfaces the same [`crate::CiDecomposeFailure`] through
21679        // both dispatches, pinned by field pair. The three
21680        // decompose arms (`DuplicateNode` / `UnknownDep` / `Cycle`)
21681        // together enumerate every failure mode
21682        // [`canteiro_types::decompose`] refuses, so the per-arm
21683        // pins collectively cover the whole decompose axis.
21684        let mut c = acao_fixture("demo");
21685        c.ci = Some(canteiro_types::CiRun {
21686            workspace: "pleme-io".into(),
21687            repo: "caixa".into(),
21688            nodes: vec![
21689                canteiro_types::CiNode::new(
21690                    "twin",
21691                    canteiro_types::EnvClass::None,
21692                    canteiro_types::ActionRef {
21693                        name: "twin".into(),
21694                        command: "true".into(),
21695                        args: vec![],
21696                    },
21697                    vec![],
21698                ),
21699                canteiro_types::CiNode::new(
21700                    "twin",
21701                    canteiro_types::EnvClass::None,
21702                    canteiro_types::ActionRef {
21703                        name: "twin".into(),
21704                        command: "true".into(),
21705                        args: vec![],
21706                    },
21707                    vec![],
21708                ),
21709            ],
21710        });
21711        let via_method = c.validate_acao_shape().unwrap_err();
21712        assert_eq!(
21713            via_method.source,
21714            canteiro_types::DecomposeError::DuplicateNode("twin".into()),
21715            "expected DuplicateNode on the two-\"twin\"-name fixture, \
21716             got {source:?}",
21717            source = via_method.source,
21718        );
21719    }
21720
21721    #[test]
21722    fn validate_acao_shape_folds_unknown_dep_arm_matches_gate() {
21723        // Per-arm equivalence pin on the `UnknownDep` decompose arm —
21724        // the third and last arm on `canteiro_types::DecomposeError`
21725        // after `Cycle` and `DuplicateNode`. A fixture whose `:ci`
21726        // slot names a `deps` entry no declared node satisfies
21727        // surfaces the same [`crate::CiDecomposeFailure`] through
21728        // both dispatches. Pins the third decompose arm at the
21729        // compound gate.
21730        let mut c = acao_fixture("demo");
21731        c.ci = Some(canteiro_types::CiRun {
21732            workspace: "pleme-io".into(),
21733            repo: "caixa".into(),
21734            nodes: vec![canteiro_types::CiNode::new(
21735                "orphan",
21736                canteiro_types::EnvClass::None,
21737                canteiro_types::ActionRef {
21738                    name: "orphan".into(),
21739                    command: "true".into(),
21740                    args: vec![],
21741                },
21742                vec!["ghost".into()],
21743            )],
21744        });
21745        let via_method = c.validate_acao_shape().unwrap_err();
21746        assert_eq!(
21747            via_method.source,
21748            canteiro_types::DecomposeError::UnknownDep {
21749                node: "orphan".into(),
21750                dep: "ghost".into(),
21751            },
21752            "expected UnknownDep on the orphan-node-depends-on-ghost \
21753             fixture, got {source:?}",
21754            source = via_method.source,
21755        );
21756    }
21757
21758    #[test]
21759    fn validate_acao_shape_accepts_non_acao_kind() {
21760        // Positive control on the identity-element arm: every non-
21761        // Acao kind passes the compound gate trivially — the paired
21762        // `caixa.kind().is_acao()` guard short-circuits before the
21763        // decompose gate ever fires, so the fold returns `Ok(())`
21764        // without touching the `:ci` slot even when a non-Acao
21765        // fixture happens to declare one (the sibling
21766        // [`crate::LayoutError::CiOnNonAcao`] kind-coherence gate
21767        // catches that at the layout altitude anyway). Pins the
21768        // identity element on every non-Acao kind. Peer with the
21769        // `validate_supervisor_shape_accepts_non_supervisor_kind` /
21770        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
21771        // identity-element pins on the sibling per-Caixa compound
21772        // gates.
21773        for kind in [
21774            CaixaKind::Biblioteca,
21775            CaixaKind::Binario,
21776            CaixaKind::Servico,
21777            CaixaKind::Supervisor,
21778            CaixaKind::Aplicacao,
21779        ] {
21780            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21781            c.kind = kind;
21782            c.validate_acao_shape().expect(
21783                "non-Acao kinds must pass the compound gate as the fold's identity element",
21784            );
21785        }
21786    }
21787
21788    #[test]
21789    fn validate_acao_shape_accepts_absent_ci_slot() {
21790        // Positive control on the second identity-element arm: a
21791        // `:kind Acao` caixa with `ci = None` passes the compound
21792        // gate trivially — the presence gate is the sibling axis
21793        // owned by [`crate::LayoutError::MissingCi`] /
21794        // [`crate::require_ci`] / [`crate::MissingCiSlot`], not by
21795        // the decompose gate. A caixa that carries no `:ci` slot
21796        // has no run to decompose, so the fold's `let Some(ci) = …
21797        // else { return Ok(()) }` arm short-circuits before the
21798        // decompose gate fires. Pins that the two axes stay
21799        // separately diagnosable at the layout altitude — a future
21800        // regression that collapsed the presence gate onto the
21801        // shape gate here would land a
21802        // [`crate::CiDecomposeFailure`] on the wrong axis and
21803        // surface an off-target diagnostic at `feira build` time.
21804        let mut c = acao_fixture("demo");
21805        c.ci = None;
21806        c.validate_acao_shape().expect(
21807            "an :kind Acao caixa with absent :ci must pass the compound gate — \
21808             the presence gate is layout's MissingCi axis, not the decompose gate",
21809        );
21810    }
21811
21812    #[test]
21813    fn validate_acao_shape_accepts_clean_fixture() {
21814        // Positive control: a well-formed Acao (a two-node acyclic
21815        // `:ci` run with `test` depending on `build`) passes the
21816        // compound gate cleanly. A future tightening of the
21817        // decompose gate's accepted set surfaces here as a test
21818        // failure first. Mirrors the peer
21819        // `validate_supervisor_shape_accepts_clean_fixture` /
21820        // `validate_aplicacao_shape_accepts_clean_fixture`
21821        // positive-control posture on the sibling per-Caixa
21822        // compound gates.
21823        let c = acao_fixture("demo");
21824        c.validate_acao_shape()
21825            .expect("clean Acao fixture must pass the compound gate");
21826    }
21827
21828    fn bare_servico_fixture(nome: &str) -> Caixa {
21829        // A minimal Servico caixa with no code and no typed slots —
21830        // the cross-family fold's identity element on every arm.
21831        // Clears the biblioteca slot the template seeds so the
21832        // per-arm patches below can each add exactly one typed slot
21833        // without a peer `ServicoOwnsCode` / layout-side kind-gate
21834        // firing upstream.
21835        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21836        c.kind = CaixaKind::Servico;
21837        c.bibliotecas = vec![];
21838        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
21839        c
21840    }
21841
21842    #[test]
21843    fn validate_kind_slot_coherence_folds_mesh_arm_matches_gate() {
21844        // Fail-before-pass-after per-arm equivalence pin on the M3
21845        // mesh-slot arm of the cross-family kind-coherence fold: a
21846        // non-Aplicacao caixa carrying a declared M3 mesh slot (here
21847        // a `:kind Servico` fixture with a single `:membros` entry —
21848        // the smallest possible M3 slot declaration on a foreign
21849        // kind) surfaces the same
21850        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] variant
21851        // through both the compound gate
21852        // [`Caixa::validate_kind_slot_coherence`] and the standalone
21853        // constructor [`crate::LayoutError::mesh_slots_on_non_aplicacao`]
21854        // dispatched on the same `declared_mesh_slots` list. Pins
21855        // the fold — a silent regression that de-folded the mesh
21856        // arm would surface here as a mismatch between the two
21857        // dispatches. Sibling in shape to the peer
21858        // `validate_aplicacao_shape_folds_view_arm_matches_gate` /
21859        // `validate_supervisor_shape_folds_view_arm_matches_gate` /
21860        // `validate_acao_shape_folds_decompose_arm_matches_gate`
21861        // per-arm equivalence pins on the sibling per-kind compound
21862        // gates.
21863        use crate::aplicacao::Membro;
21864        let mut c = bare_servico_fixture("demo");
21865        c.membros = vec![Membro {
21866            caixa: "cart".into(),
21867            versao: "^0.1".into(),
21868        }];
21869        let via_method = c.validate_kind_slot_coherence().unwrap_err();
21870        let via_standalone =
21871            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
21872        assert_eq!(
21873            via_method, via_standalone,
21874            "Caixa::validate_kind_slot_coherence must surface the M3 \
21875             mesh-slot arm's diagnostic byte-equal to the standalone \
21876             LayoutError::mesh_slots_on_non_aplicacao ctor on the same \
21877             declared_mesh_slots list",
21878        );
21879    }
21880
21881    #[test]
21882    fn validate_kind_slot_coherence_folds_supervisor_arm_matches_gate() {
21883        // Per-arm equivalence pin on the supervisor-tree arm — the
21884        // sibling of the mesh arm on the cross-family fold. A
21885        // non-Supervisor caixa carrying a declared supervisor slot
21886        // (a `:kind Servico` fixture with `:estrategia` set — the
21887        // smallest possible supervisor slot declaration on a
21888        // foreign kind) surfaces the same
21889        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
21890        // variant through both dispatches, pinned by field pair
21891        // through `PartialEq`.
21892        use crate::supervisor::RestartStrategy;
21893        let mut c = bare_servico_fixture("demo");
21894        c.estrategia = Some(RestartStrategy::OneForOne);
21895        let via_method = c.validate_kind_slot_coherence().unwrap_err();
21896        let via_standalone = crate::LayoutError::supervisor_slots_on_non_supervisor(
21897            &c,
21898            c.declared_supervisor_slots(),
21899        );
21900        assert_eq!(
21901            via_method, via_standalone,
21902            "Caixa::validate_kind_slot_coherence must surface the \
21903             supervisor-tree arm's diagnostic byte-equal to the \
21904             standalone LayoutError::supervisor_slots_on_non_supervisor \
21905             ctor on the same declared_supervisor_slots list",
21906        );
21907    }
21908
21909    #[test]
21910    fn validate_kind_slot_coherence_folds_servico_arm_matches_gate() {
21911        // Per-arm equivalence pin on the M2 Servico-runtime arm —
21912        // the third and last arm on the cross-family fold. A
21913        // non-Servico caixa carrying a declared M2 slot (a `:kind
21914        // Biblioteca` fixture with `:limits` set — the smallest
21915        // possible M2 slot declaration on a foreign kind) surfaces
21916        // the same [`crate::LayoutError::ServicoSlotsOnNonServico`]
21917        // variant through both dispatches. The three arms together
21918        // enumerate every typed-slot family the substrate carries
21919        // whose "declared but ignored" footgun is gated at the
21920        // layout altitude by a `{ caixa, kind, slots }` wrap variant,
21921        // so the per-arm pins collectively cover the whole
21922        // cross-family kind-coherence axis.
21923        use crate::limits::LimitsSpec;
21924        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21925        c.kind = CaixaKind::Biblioteca;
21926        c.limits = Some(LimitsSpec {
21927            memory: Some(64 * 1024 * 1024),
21928            fuel: None,
21929            wall_clock: None,
21930            cpu: None,
21931        });
21932        let via_method = c.validate_kind_slot_coherence().unwrap_err();
21933        let via_standalone =
21934            crate::LayoutError::servico_slots_on_non_servico(&c, c.declared_servico_slots());
21935        assert_eq!(
21936            via_method, via_standalone,
21937            "Caixa::validate_kind_slot_coherence must surface the M2 \
21938             Servico-runtime arm's diagnostic byte-equal to the \
21939             standalone LayoutError::servico_slots_on_non_servico ctor \
21940             on the same declared_servico_slots list",
21941        );
21942    }
21943
21944    #[test]
21945    fn validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm() {
21946        // Cross-arm ordering pin between the first two arms of the
21947        // fold: a fixture carrying BOTH a declared M3 mesh slot
21948        // (`:membros`) AND a declared supervisor-tree slot
21949        // (`:estrategia`) on a foreign kind (a `:kind Servico` here —
21950        // foreign to both the Aplicacao arm and the Supervisor arm)
21951        // surfaces the M3 mesh diagnostic first through the compound
21952        // gate. Pins the pre-fold layout wire-up's canonical
21953        // diagnostic sequence (mesh → supervisor → servico) as a
21954        // property of the substrate primitive rather than a
21955        // convention of the layout call site. A silent reordering
21956        // regression at the primitive would surface here as a
21957        // wrong-variant match before landing at a downstream
21958        // consumer's diagnostic-ordering expectation.
21959        use crate::aplicacao::Membro;
21960        use crate::supervisor::RestartStrategy;
21961        let mut c = bare_servico_fixture("demo");
21962        c.membros = vec![Membro {
21963            caixa: "cart".into(),
21964            versao: "^0.1".into(),
21965        }];
21966        c.estrategia = Some(RestartStrategy::OneForOne);
21967        let err = c.validate_kind_slot_coherence().unwrap_err();
21968        assert!(
21969            matches!(err, crate::LayoutError::MeshSlotsOnNonAplicacao { .. }),
21970            "expected MeshSlotsOnNonAplicacao to fire before \
21971             SupervisorSlotsOnNonSupervisor under the canonical \
21972             mesh → supervisor → servico order, got {err:?}",
21973        );
21974    }
21975
21976    #[test]
21977    fn validate_kind_slot_coherence_supervisor_arm_fires_before_servico_arm() {
21978        // Cross-arm ordering pin between the second and third arms
21979        // of the fold: a fixture carrying BOTH a declared
21980        // supervisor-tree slot (`:estrategia`) AND a declared M2 slot
21981        // (`:limits`) on a kind foreign to both (a `:kind Biblioteca`
21982        // here — foreign to both the Supervisor and the Servico
21983        // arms) surfaces the supervisor-tree diagnostic first
21984        // through the compound gate. Together with the peer
21985        // `_mesh_arm_fires_before_supervisor_arm` pin above this
21986        // pins the whole three-arm canonical order (mesh →
21987        // supervisor → servico) at the substrate primitive.
21988        use crate::limits::LimitsSpec;
21989        use crate::supervisor::RestartStrategy;
21990        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21991        c.kind = CaixaKind::Biblioteca;
21992        c.estrategia = Some(RestartStrategy::OneForOne);
21993        c.limits = Some(LimitsSpec {
21994            memory: Some(64 * 1024 * 1024),
21995            fuel: None,
21996            wall_clock: None,
21997            cpu: None,
21998        });
21999        let err = c.validate_kind_slot_coherence().unwrap_err();
22000        assert!(
22001            matches!(
22002                err,
22003                crate::LayoutError::SupervisorSlotsOnNonSupervisor { .. }
22004            ),
22005            "expected SupervisorSlotsOnNonSupervisor to fire before \
22006             ServicoSlotsOnNonServico under the canonical mesh → \
22007             supervisor → servico order, got {err:?}",
22008        );
22009    }
22010
22011    #[test]
22012    fn validate_kind_slot_coherence_accepts_owner_kind_on_every_arm() {
22013        // Positive control on the identity-element arm: the owner
22014        // kind of each typed-slot family passes the compound gate
22015        // even when it declares the full slot set that family owns.
22016        // Aplicacao with `:membros` populated passes the mesh arm;
22017        // Supervisor with `:estrategia` populated passes the
22018        // supervisor arm; Servico with `:limits` populated passes
22019        // the servico arm. Pins the fold's identity element on
22020        // every owner kind — a silent regression that dropped the
22021        // paired `!kind().is_<owner>()` short-circuit guard would
22022        // surface here as a false-positive rejection of every
22023        // native-slot declaration. Peer with the
22024        // `validate_<kind>_shape_accepts_non_<kind>_kind` identity-
22025        // element pins on the sibling per-Caixa compound gates.
22026        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
22027        use crate::limits::LimitsSpec;
22028        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
22029
22030        let mut apli = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22031        apli.kind = CaixaKind::Aplicacao;
22032        apli.bibliotecas = vec![];
22033        apli.membros = vec![Membro {
22034            caixa: "cart".into(),
22035            versao: "^0.1".into(),
22036        }];
22037        apli.placement = Some(Placement {
22038            estrategia: PlacementStrategy::SingleNode,
22039            clusters: vec!["rio".into()],
22040            shard_key: None,
22041            affinity: None,
22042        });
22043        apli.validate_kind_slot_coherence().expect(
22044            "an :kind Aplicacao caixa with declared M3 mesh slots must \
22045             pass the compound gate — Aplicacao is the mesh-slot family's \
22046             owner kind and the fold's identity element on that arm",
22047        );
22048
22049        let mut sup = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22050        sup.kind = CaixaKind::Supervisor;
22051        sup.bibliotecas = vec![];
22052        sup.estrategia = Some(RestartStrategy::OneForOne);
22053        sup.children = vec![ChildSpec {
22054            caixa: "worker".into(),
22055            versao: "^0.1".into(),
22056            restart: RestartPolicy::Permanent,
22057        }];
22058        sup.validate_kind_slot_coherence().expect(
22059            "an :kind Supervisor caixa with declared supervisor-tree slots \
22060             must pass the compound gate — Supervisor is the \
22061             supervisor-slot family's owner kind and the fold's identity \
22062             element on that arm",
22063        );
22064
22065        let mut svc = bare_servico_fixture("svc");
22066        svc.limits = Some(LimitsSpec {
22067            memory: Some(64 * 1024 * 1024),
22068            fuel: None,
22069            wall_clock: None,
22070            cpu: None,
22071        });
22072        svc.validate_kind_slot_coherence().expect(
22073            "an :kind Servico caixa with declared M2 slots must pass the \
22074             compound gate — Servico is the M2-slot family's owner kind \
22075             and the fold's identity element on that arm",
22076        );
22077    }
22078
22079    #[test]
22080    fn validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind() {
22081        // Positive control on the second identity-element arm: a
22082        // bare caixa (no declared typed slots) passes the compound
22083        // gate on every kind. Pins the fold's identity element on
22084        // the empty-slot axis — the paired `Vec::is_empty` short-
22085        // circuit guard fires before the wrap dispatch on all three
22086        // arms, so a bare caixa of any kind surfaces no diagnostic.
22087        // A silent regression that dropped the emptiness guard
22088        // would surface here as a false-positive rejection of every
22089        // no-slot caixa across the whole kind axis.
22090        for kind in CaixaKind::ALL {
22091            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22092            c.kind = *kind;
22093            c.bibliotecas = vec![];
22094            c.validate_kind_slot_coherence().unwrap_or_else(|err| {
22095                panic!(
22096                    "a bare :kind {kind:?} caixa (no declared typed slots) \
22097                     must pass the compound gate — the fold's identity \
22098                     element on the empty-slot axis is the paired \
22099                     Vec::is_empty short-circuit guard, got {err:?}",
22100                )
22101            });
22102        }
22103    }
22104
22105    #[test]
22106    fn run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator() {
22107        // Fail-before-pass-after identity-element pin on the owner-kind
22108        // arm of the substrate primitive: on a caixa whose kind IS the
22109        // owner of the family named by `is_owner`, the primitive
22110        // short-circuits before dispatching `accumulator` — pinned here
22111        // by a poison-pill accumulator that panics on call. If a
22112        // regression drops the `is_owner` short-circuit and always
22113        // invokes the accumulator, the poison panic surfaces here
22114        // rather than a spurious pass. Byte-equal to the pre-lift
22115        // `if !self.kind().is_<owner>() { … }` outer guard's
22116        // short-circuit at the pre-fold layout call site.
22117        let c = bare_servico_fixture("demo");
22118        c.run_kind_owned_slot_family_gate(
22119            CaixaKind::is_servico,
22120            |_| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking accumulator on the owner kind"),
22121            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on the owner kind"),
22122        )
22123        .expect(
22124            "the owner kind of a slot family must pass the substrate \
22125             primitive as the fold's identity element on the outer \
22126             is_owner guard, without invoking accumulator or wrap",
22127        );
22128    }
22129
22130    #[test]
22131    fn run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap() {
22132        // Fail-before-pass-after identity-element pin on the empty-
22133        // accumulator arm: on a non-owner kind whose per-family
22134        // accumulator yields no declared slot, the primitive short-
22135        // circuits before dispatching `wrap` — pinned here by a
22136        // poison-pill wrap that panics on call. Byte-equal to the
22137        // pre-lift `if !<slots>.is_empty() { … }` inner emptiness
22138        // guard's short-circuit at the pre-fold layout call site.
22139        let c = bare_servico_fixture("demo");
22140        c.run_kind_owned_slot_family_gate(
22141            CaixaKind::is_aplicacao,
22142            Caixa::declared_mesh_slots,
22143            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on an empty accumulator"),
22144        )
22145        .expect(
22146            "a non-owner kind carrying no declared slot in the family \
22147             must pass the substrate primitive as the fold's identity \
22148             element on the inner emptiness guard, without invoking \
22149             wrap",
22150        );
22151    }
22152
22153    #[test]
22154    fn run_kind_owned_slot_family_gate_non_owner_non_empty_wraps_verbatim() {
22155        // Equivalence pin on the refusal arm: on a non-owner kind
22156        // whose accumulator yields a non-empty slot list, the primitive
22157        // returns the caller-supplied wrap byte-equal to the direct
22158        // ctor dispatch on the same `(caixa, slots)` pair. Pins the
22159        // three-argument route through — `is_owner` fires false, the
22160        // accumulator produces the slot list, and the wrap ctor
22161        // receives verbatim what a direct dispatch would receive.
22162        // Sibling of the peer per-arm equivalence pins on
22163        // [`Caixa::validate_kind_slot_coherence`].
22164        use crate::aplicacao::Membro;
22165        let mut c = bare_servico_fixture("demo");
22166        c.membros = vec![Membro {
22167            caixa: "cart".into(),
22168            versao: "^0.1".into(),
22169        }];
22170        let via_primitive = c
22171            .run_kind_owned_slot_family_gate(
22172                CaixaKind::is_aplicacao,
22173                Caixa::declared_mesh_slots,
22174                crate::LayoutError::mesh_slots_on_non_aplicacao,
22175            )
22176            .unwrap_err();
22177        let via_direct =
22178            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22179        assert_eq!(
22180            via_primitive, via_direct,
22181            "Caixa::run_kind_owned_slot_family_gate must route the \
22182             non-owner-kind + non-empty-accumulator arm through the \
22183             caller-supplied wrap byte-equal to the direct ctor \
22184             dispatch on the same (caixa, slots) pair",
22185        );
22186    }
22187
22188    #[test]
22189    fn validate_kind_slot_coherence_routes_each_arm_through_run_kind_owned_slot_family_gate() {
22190        // Cross-primitive routing pin: every arm of the compound gate
22191        // [`Caixa::validate_kind_slot_coherence`] routes through the
22192        // substrate primitive [`Caixa::run_kind_owned_slot_family_gate`]
22193        // on its `(is_owner, accumulator, wrap)` triple. A silent
22194        // regression that de-folded one arm and re-inlined the four-
22195        // line block would surface here as a mismatch between the
22196        // compound-gate error and the direct-primitive-dispatch error
22197        // on the same fixture. Sibling of the peer
22198        // `probe_declared_entries_routes_miss_arm_through_probe_declared_entry`
22199        // cross-primitive routing pin on the layout-pipeline
22200        // existence-probe axis.
22201        use crate::aplicacao::Membro;
22202        use crate::limits::LimitsSpec;
22203        use crate::supervisor::RestartStrategy;
22204
22205        // Mesh arm — non-Aplicacao carrying a declared M3 slot.
22206        let mut mesh = bare_servico_fixture("demo");
22207        mesh.membros = vec![Membro {
22208            caixa: "cart".into(),
22209            versao: "^0.1".into(),
22210        }];
22211        let via_compound = mesh.validate_kind_slot_coherence().unwrap_err();
22212        let via_primitive = mesh
22213            .run_kind_owned_slot_family_gate(
22214                CaixaKind::is_aplicacao,
22215                Caixa::declared_mesh_slots,
22216                crate::LayoutError::mesh_slots_on_non_aplicacao,
22217            )
22218            .unwrap_err();
22219        assert_eq!(
22220            via_compound, via_primitive,
22221            "validate_kind_slot_coherence's mesh arm must route \
22222             byte-equal through the run_kind_owned_slot_family_gate \
22223             substrate primitive",
22224        );
22225
22226        // Supervisor arm — non-Supervisor carrying a declared
22227        // supervisor-tree slot on a kind foreign to both the Aplicacao
22228        // arm and this one.
22229        let mut sup = bare_servico_fixture("demo");
22230        sup.estrategia = Some(RestartStrategy::OneForOne);
22231        let via_compound = sup.validate_kind_slot_coherence().unwrap_err();
22232        let via_primitive = sup
22233            .run_kind_owned_slot_family_gate(
22234                CaixaKind::is_supervisor,
22235                Caixa::declared_supervisor_slots,
22236                crate::LayoutError::supervisor_slots_on_non_supervisor,
22237            )
22238            .unwrap_err();
22239        assert_eq!(
22240            via_compound, via_primitive,
22241            "validate_kind_slot_coherence's supervisor arm must route \
22242             byte-equal through the run_kind_owned_slot_family_gate \
22243             substrate primitive",
22244        );
22245
22246        // Servico arm — non-Servico carrying a declared M2 slot on a
22247        // kind foreign to every prior arm (Biblioteca — foreign to
22248        // both the Aplicacao mesh arm and the Supervisor supervisor
22249        // arm and the Servico M2 arm).
22250        let mut svc = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22251        svc.kind = CaixaKind::Biblioteca;
22252        svc.limits = Some(LimitsSpec {
22253            memory: Some(64 * 1024 * 1024),
22254            fuel: None,
22255            wall_clock: None,
22256            cpu: None,
22257        });
22258        let via_compound = svc.validate_kind_slot_coherence().unwrap_err();
22259        let via_primitive = svc
22260            .run_kind_owned_slot_family_gate(
22261                CaixaKind::is_servico,
22262                Caixa::declared_servico_slots,
22263                crate::LayoutError::servico_slots_on_non_servico,
22264            )
22265            .unwrap_err();
22266        assert_eq!(
22267            via_compound, via_primitive,
22268            "validate_kind_slot_coherence's servico arm must route \
22269             byte-equal through the run_kind_owned_slot_family_gate \
22270             substrate primitive",
22271        );
22272    }
22273
22274    #[test]
22275    fn validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate() {
22276        // Fail-before-pass-after per-arm equivalence pin on the
22277        // Supervisor no-code arm of the reciprocal code-surface
22278        // fold: a `:kind Supervisor` caixa carrying a declared
22279        // `:bibliotecas` entry (the smallest possible code-surface
22280        // declaration on a no-code kind) surfaces the same
22281        // [`crate::LayoutError::SupervisorOwnsCode`] variant
22282        // through both the compound gate
22283        // [`Caixa::validate_no_code_kind_coherence`] and the
22284        // standalone constructor
22285        // [`crate::LayoutError::supervisor_owns_code`]. Pins the
22286        // fold — a silent regression that de-folded the Supervisor
22287        // arm would surface here as a mismatch between the two
22288        // dispatches. Sibling in shape to the peer
22289        // `validate_kind_slot_coherence_folds_supervisor_arm_matches_gate`
22290        // per-arm equivalence pin on the cross-family
22291        // typed-slot-coherence fold.
22292        let mut c = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22293        c.kind = CaixaKind::Supervisor;
22294        c.bibliotecas = vec!["lib/sup.lisp".into()];
22295        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22296        let via_standalone = crate::LayoutError::supervisor_owns_code(&c);
22297        assert_eq!(
22298            via_method, via_standalone,
22299            "Caixa::validate_no_code_kind_coherence must surface the \
22300             Supervisor arm's diagnostic byte-equal to the standalone \
22301             LayoutError::supervisor_owns_code ctor",
22302        );
22303    }
22304
22305    #[test]
22306    fn validate_no_code_kind_coherence_folds_aplicacao_arm_matches_gate() {
22307        // Per-arm equivalence pin on the Aplicacao no-code arm —
22308        // the sibling of the Supervisor arm on the code-surface
22309        // fold. A `:kind Aplicacao` caixa carrying a declared
22310        // `:exe` entry surfaces the same
22311        // [`crate::LayoutError::AplicacaoOwnsCode`] variant through
22312        // both dispatches. Uses the `:exe` code-surface axis (a
22313        // second axis distinct from the Supervisor arm's
22314        // `:bibliotecas` fixture) so the three per-arm pins
22315        // collectively exercise every arm of the `has_code`
22316        // disjunction (`:bibliotecas || :exe || :servicos`).
22317        let mut c = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22318        c.kind = CaixaKind::Aplicacao;
22319        c.bibliotecas = vec![];
22320        c.exe = vec!["exe/app".into()];
22321        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22322        let via_standalone = crate::LayoutError::aplicacao_owns_code(&c);
22323        assert_eq!(
22324            via_method, via_standalone,
22325            "Caixa::validate_no_code_kind_coherence must surface the \
22326             Aplicacao arm's diagnostic byte-equal to the standalone \
22327             LayoutError::aplicacao_owns_code ctor",
22328        );
22329    }
22330
22331    #[test]
22332    fn validate_no_code_kind_coherence_folds_acao_arm_matches_gate() {
22333        // Per-arm equivalence pin on the Acao no-code arm — the
22334        // third and last arm on the code-surface fold. A `:kind
22335        // Acao` caixa carrying a declared `:servicos` entry
22336        // surfaces the same [`crate::LayoutError::AcaoOwnsCode`]
22337        // variant through both dispatches. Uses the `:servicos`
22338        // code-surface axis (the third distinct axis of the
22339        // `has_code` disjunction) so the three per-arm pins
22340        // collectively cover every arm of the code-surface
22341        // disjunction plus every no-code kind of the arm
22342        // dispatch.
22343        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
22344        c.kind = CaixaKind::Acao;
22345        c.bibliotecas = vec![];
22346        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22347        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22348        let via_standalone = crate::LayoutError::acao_owns_code(&c);
22349        assert_eq!(
22350            via_method, via_standalone,
22351            "Caixa::validate_no_code_kind_coherence must surface the \
22352             Acao arm's diagnostic byte-equal to the standalone \
22353             LayoutError::acao_owns_code ctor",
22354        );
22355    }
22356
22357    #[test]
22358    fn validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis() {
22359        // Positive control on the code-owning-kind identity
22360        // element: each of the three code-owning kinds
22361        // (`Biblioteca` owning `:bibliotecas`, `Binario` owning
22362        // `:exe`, `Servico` owning `:servicos`) passes the
22363        // compound gate cleanly when it declares its native code
22364        // surface. Pins the fold's second identity element — the
22365        // paired per-arm `is_<no-code-kind>()` short-circuit
22366        // fires on every code-owning kind, so a caixa with any
22367        // native code declaration on its owner kind surfaces no
22368        // diagnostic. A silent regression that dropped the paired
22369        // `is_<no-code-kind>()` short-circuit guard on any arm
22370        // would surface here as a false-positive rejection of the
22371        // corresponding owner kind. Peer with the
22372        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22373        // identity-element pin on the sibling cross-family fold.
22374        let mut bib = Caixa::from_lisp(&Caixa::template("bib")).unwrap();
22375        bib.kind = CaixaKind::Biblioteca;
22376        bib.bibliotecas = vec!["lib/bib.lisp".into()];
22377        bib.validate_no_code_kind_coherence().expect(
22378            "a :kind Biblioteca caixa with declared :bibliotecas must pass \
22379             the compound gate — Biblioteca owns the :bibliotecas code surface",
22380        );
22381
22382        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22383        bin.kind = CaixaKind::Binario;
22384        bin.bibliotecas = vec![];
22385        bin.exe = vec!["exe/bin".into()];
22386        bin.validate_no_code_kind_coherence().expect(
22387            "a :kind Binario caixa with declared :exe must pass the compound \
22388             gate — Binario owns the :exe code surface",
22389        );
22390
22391        let svc = bare_servico_fixture("svc");
22392        svc.validate_no_code_kind_coherence().expect(
22393            "a :kind Servico caixa with declared :servicos must pass the \
22394             compound gate — Servico owns the :servicos code surface",
22395        );
22396    }
22397
22398    #[test]
22399    fn validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22400        // Positive control on the has-no-code identity element:
22401        // a bare caixa (no declared code) passes the compound
22402        // gate on every kind — including the three no-code kinds
22403        // that would otherwise fire an OwnsCode diagnostic. Pins
22404        // the fold's first identity element — the paired
22405        // `!has_code` short-circuit fires before every per-arm
22406        // wrap dispatch, so a bare caixa of any kind surfaces no
22407        // diagnostic. A silent regression that dropped the
22408        // has_code guard would surface here as a false-positive
22409        // rejection of every no-code kind that declares no code.
22410        // Peer with the
22411        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22412        // identity-element pin on the sibling cross-family fold.
22413        for kind in CaixaKind::ALL {
22414            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22415            c.kind = *kind;
22416            c.bibliotecas = vec![];
22417            c.exe = vec![];
22418            c.servicos = vec![];
22419            c.validate_no_code_kind_coherence().unwrap_or_else(|err| {
22420                panic!(
22421                    "a bare :kind {kind:?} caixa (no declared code) must pass \
22422                     the compound gate — the fold's first identity element is \
22423                     the paired !has_code short-circuit, got {err:?}",
22424                )
22425            });
22426        }
22427    }
22428
22429    #[test]
22430    fn validate_ci_kind_coherence_folds_arm_matches_gate() {
22431        // Fail-before-pass-after per-arm equivalence pin on the
22432        // `:ci`-on-non-`Acao` arm: a `:kind Biblioteca` caixa
22433        // (the smallest non-`Acao` kind) carrying a declared
22434        // `:ci` slot surfaces the same
22435        // [`crate::LayoutError::CiOnNonAcao`] variant through the
22436        // compound gate [`Caixa::validate_ci_kind_coherence`] and
22437        // an inlined struct-literal wrap carrying `caixa.nome()`
22438        // + `caixa.kind()` verbatim. Pins the fold — a silent
22439        // regression that de-folded the arm would surface here as
22440        // a mismatch between the two dispatches. Sibling in shape
22441        // to the peer
22442        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22443        // per-arm equivalence pin on the reciprocal
22444        // code-surface fold.
22445        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22446        c.kind = CaixaKind::Biblioteca;
22447        c.ci = Some(canteiro_types::CiRun {
22448            workspace: "pleme-io".into(),
22449            repo: "caixa".into(),
22450            nodes: vec![],
22451        });
22452        let via_method = c.validate_ci_kind_coherence().unwrap_err();
22453        let via_standalone = crate::LayoutError::CiOnNonAcao {
22454            caixa: c.nome().to_string(),
22455            kind: c.kind(),
22456        };
22457        assert_eq!(
22458            via_method, via_standalone,
22459            "Caixa::validate_ci_kind_coherence must surface the \
22460             :ci-on-non-Acao arm's diagnostic byte-equal to a \
22461             LayoutError::CiOnNonAcao struct literal carrying the \
22462             caixa's nome + kind",
22463        );
22464    }
22465
22466    #[test]
22467    fn validate_ci_kind_coherence_fold_names_offending_kind_on_every_non_acao_kind() {
22468        // Exhaustive per-kind sweep on the non-`Acao` arm: for each
22469        // of the five non-`Acao` kinds
22470        // (`Biblioteca` / `Binario` / `Servico` / `Supervisor` /
22471        // `Aplicacao`), a caixa carrying a declared `:ci` slot
22472        // surfaces the [`crate::LayoutError::CiOnNonAcao`]
22473        // variant naming the offending kind verbatim. A silent
22474        // regression that mistyped one arm's kind-projection
22475        // (e.g. always threading `CaixaKind::Biblioteca` regardless
22476        // of the caixa's actual kind) would surface here as a
22477        // mismatch on every kind past the first. Peer of the
22478        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22479        // exhaustive-sweep pin on the sibling code-surface fold.
22480        for kind in CaixaKind::ALL {
22481            if kind.is_acao() {
22482                continue;
22483            }
22484            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22485            c.kind = *kind;
22486            c.ci = Some(canteiro_types::CiRun {
22487                workspace: "pleme-io".into(),
22488                repo: "caixa".into(),
22489                nodes: vec![],
22490            });
22491            let err = c.validate_ci_kind_coherence().unwrap_err();
22492            match err {
22493                crate::LayoutError::CiOnNonAcao {
22494                    caixa: got_caixa,
22495                    kind: got_kind,
22496                } => {
22497                    assert_eq!(
22498                        got_caixa,
22499                        c.nome(),
22500                        "CiOnNonAcao must name the offending caixa's nome verbatim on kind {kind:?}",
22501                    );
22502                    assert_eq!(
22503                        got_kind, *kind,
22504                        "CiOnNonAcao must name the offending kind verbatim on kind {kind:?}",
22505                    );
22506                }
22507                other => panic!(
22508                    "expected CiOnNonAcao on :kind {kind:?} with declared :ci, got {other:?}",
22509                ),
22510            }
22511        }
22512    }
22513
22514    #[test]
22515    fn validate_ci_kind_coherence_accepts_acao_on_every_ci_shape() {
22516        // Positive control on the owner-kind identity element: an
22517        // `:kind Acao` caixa passes the coherence gate cleanly on
22518        // every `:ci` shape — the arm's paired
22519        // `!kind().is_acao()` short-circuit fires before the
22520        // dispatch, so the fold surfaces no diagnostic even on
22521        // fixtures whose `:ci` would fail the peer
22522        // [`Self::validate_acao_shape`] decompose gate (a
22523        // duplicate-node fixture, an unknown-dep fixture, a
22524        // cyclic fixture). Pins the fold's first identity element
22525        // — a silent regression that dropped the paired
22526        // `!kind().is_acao()` short-circuit guard would surface
22527        // here as a false-positive rejection of every `Acao`
22528        // caixa. Peer with the
22529        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
22530        // identity-element pin on the sibling code-surface fold.
22531        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22532        c.kind = CaixaKind::Acao;
22533        c.bibliotecas = vec![];
22534        c.ci = Some(canteiro_types::CiRun {
22535            workspace: "pleme-io".into(),
22536            repo: "caixa".into(),
22537            nodes: vec![],
22538        });
22539        c.validate_ci_kind_coherence().expect(
22540            "a :kind Acao caixa with declared :ci must pass the compound \
22541             coherence gate — Acao is the :ci-owning kind (a malformed \
22542             :ci on Acao surfaces via validate_acao_shape's decompose gate, \
22543             not via this kind-coherence gate)",
22544        );
22545    }
22546
22547    #[test]
22548    fn validate_ci_kind_coherence_accepts_absent_ci_on_every_kind() {
22549        // Positive control on the absent-`:ci` identity element:
22550        // a caixa with `ci = None` passes the coherence gate on
22551        // every kind — including `Acao`, whose absent `:ci`
22552        // fails a separate presence gate ([`crate::LayoutError::MissingCi`])
22553        // downstream at the layout altitude, not this coherence
22554        // gate. Pins the fold's second identity element — the
22555        // paired `ci().is_some()` short-circuit fires before every
22556        // per-arm dispatch, so a caixa with no declared `:ci`
22557        // surfaces no coherence diagnostic. A silent regression
22558        // that dropped the paired `ci().is_some()` short-circuit
22559        // would surface here as a false-positive rejection on
22560        // every non-`Acao` kind. Peer with the
22561        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22562        // identity-element pin on the sibling code-surface fold.
22563        for kind in CaixaKind::ALL {
22564            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22565            c.kind = *kind;
22566            c.ci = None;
22567            c.validate_ci_kind_coherence().unwrap_or_else(|err| {
22568                panic!(
22569                    "a :kind {kind:?} caixa with no declared :ci must pass \
22570                     the compound coherence gate — the fold's second identity \
22571                     element is the paired ci().is_some() short-circuit, got \
22572                     {err:?}",
22573                )
22574            });
22575        }
22576    }
22577
22578    #[test]
22579    fn validate_foreign_code_kind_coherence_folds_arm_matches_gate() {
22580        // Fail-before-pass-after equivalence pin on the compound
22581        // foreign-code-slot coherence fold: a `:kind Servico` caixa
22582        // carrying a declared `:exe` entry (the smallest possible
22583        // foreign-code-slot declaration on a code-running kind that
22584        // is not its owner — Servico owns `:servicos`, not `:exe`)
22585        // surfaces the same [`crate::LayoutError::ForeignCodeSlot`]
22586        // variant through both the compound gate
22587        // [`Caixa::validate_foreign_code_kind_coherence`] and the
22588        // standalone constructor
22589        // [`crate::LayoutError::foreign_code_slot`] dispatched on the
22590        // same `declared_foreign_code_slots` list. Pins the fold — a
22591        // silent regression that de-folded the arm would surface here
22592        // as a mismatch between the two dispatches. Sibling in shape
22593        // to the peer
22594        // `validate_kind_slot_coherence_folds_mesh_arm_matches_gate`
22595        // / `validate_ci_kind_coherence_folds_arm_matches_gate` /
22596        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22597        // per-arm equivalence pins on the sibling kind-coherence folds.
22598        let mut c = bare_servico_fixture("demo");
22599        c.exe = vec!["exe/foreign".into()];
22600        let via_method = c.validate_foreign_code_kind_coherence().unwrap_err();
22601        let via_standalone =
22602            crate::LayoutError::foreign_code_slot(&c, c.declared_foreign_code_slots());
22603        assert_eq!(
22604            via_method, via_standalone,
22605            "Caixa::validate_foreign_code_kind_coherence must surface the \
22606             foreign-code-slot diagnostic byte-equal to the standalone \
22607             LayoutError::foreign_code_slot ctor on the same \
22608             declared_foreign_code_slots list",
22609        );
22610    }
22611
22612    #[test]
22613    fn validate_foreign_code_kind_coherence_exe_arm_precedes_servicos_arm() {
22614        // Cross-arm ordering pin on the fold's accumulator: a fixture
22615        // carrying BOTH a declared `:exe` AND a declared `:servicos`
22616        // on a kind foreign to both (a `:kind Biblioteca` here —
22617        // foreign to both the Binario arm and the Servico arm)
22618        // surfaces `:exe` first in the `ForeignCodeSlot`'s slots
22619        // list. Pins the canonical `:exe` → `:servicos` diagnostic
22620        // order [`Caixa::declared_foreign_code_slots`] establishes,
22621        // as a property of the substrate primitive rather than an
22622        // implicit accumulator convention. A silent reordering
22623        // regression at the accumulator would surface here as a
22624        // wrong-first-slot list before landing at a downstream
22625        // consumer's diagnostic-ordering expectation.
22626        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22627        c.kind = CaixaKind::Biblioteca;
22628        c.exe = vec!["exe/demo".into()];
22629        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22630        let err = c.validate_foreign_code_kind_coherence().unwrap_err();
22631        let crate::LayoutError::ForeignCodeSlot { slots, .. } = &err else {
22632            panic!("expected ForeignCodeSlot variant, got {err:?}");
22633        };
22634        assert!(
22635            slots.starts_with(":exe"),
22636            "expected the :exe arm to precede the :servicos arm in the \
22637             ForeignCodeSlot slots list under the canonical :exe → :servicos \
22638             order, got slots = {slots:?}",
22639        );
22640        assert!(
22641            slots.contains(":servicos"),
22642            "expected the :servicos arm to also fire in the ForeignCodeSlot \
22643             slots list on a fixture carrying both foreign code surfaces, \
22644             got slots = {slots:?}",
22645        );
22646    }
22647
22648    #[test]
22649    fn validate_foreign_code_kind_coherence_accepts_native_slot_on_owner_kind() {
22650        // Positive control on the native-slot identity element: each
22651        // code-surface slot's owner kind passes the fold trivially
22652        // when it declares only its native code surface. `:kind
22653        // Binario` with a declared `:exe` and no `:servicos` passes
22654        // (the `!requires_exe()` guard short-circuits the arm inside
22655        // [`Caixa::declared_foreign_code_slots`], so the accumulator
22656        // returns empty); `:kind Servico` with a declared `:servicos`
22657        // and no `:exe` passes for the mirror reason. Pins the fold's
22658        // native-slot identity element on both arms — a silent
22659        // regression that dropped either per-arm `!requires_<slot>()`
22660        // predicate would surface here as a false-positive rejection
22661        // of every native-slot declaration on its owner kind. Peer
22662        // with the
22663        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22664        // identity-element pin on the sibling cross-family fold.
22665        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22666        bin.kind = CaixaKind::Binario;
22667        bin.bibliotecas = vec![];
22668        bin.exe = vec!["exe/bin".into()];
22669        bin.servicos = vec![];
22670        bin.validate_foreign_code_kind_coherence().expect(
22671            "a :kind Binario caixa with a declared native :exe and no \
22672             :servicos must pass the compound coherence gate — Binario is \
22673             the :exe slot's owner kind and the fold's native-slot identity \
22674             element on that arm",
22675        );
22676
22677        let mut svc = bare_servico_fixture("svc");
22678        svc.exe = vec![];
22679        svc.validate_foreign_code_kind_coherence().expect(
22680            "a :kind Servico caixa with a declared native :servicos and no \
22681             :exe must pass the compound coherence gate — Servico is the \
22682             :servicos slot's owner kind and the fold's native-slot identity \
22683             element on that arm",
22684        );
22685    }
22686
22687    #[test]
22688    fn validate_foreign_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22689        // Positive control on the empty-slot identity element: a
22690        // bare caixa (no declared `:exe` and no declared `:servicos`)
22691        // passes the compound gate on every kind. Pins the fold's
22692        // identity element on the empty-accumulator axis — the outer
22693        // `is_empty` short-circuit fires before the wrap dispatch on
22694        // every kind, so a bare caixa of any kind surfaces no
22695        // foreign-code-slot diagnostic. A silent regression that
22696        // dropped the emptiness guard would surface here as a
22697        // false-positive rejection of every no-code-slot caixa
22698        // across the whole kind axis. Peer with the
22699        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22700        // identity-element pin on the sibling cross-family fold.
22701        for kind in CaixaKind::ALL {
22702            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22703            c.kind = *kind;
22704            c.bibliotecas = vec![];
22705            c.exe = vec![];
22706            c.servicos = vec![];
22707            c.validate_foreign_code_kind_coherence()
22708                .unwrap_or_else(|err| {
22709                    panic!(
22710                        "a bare :kind {kind:?} caixa (no declared :exe / \
22711                         :servicos) must pass the compound coherence gate — \
22712                         the fold's identity element on the empty-accumulator \
22713                         axis is the outer Vec::is_empty short-circuit, got \
22714                         {err:?}",
22715                    )
22716                });
22717        }
22718    }
22719
22720    #[test]
22721    fn validate_required_kind_slot_folds_binario_arm_matches_gate() {
22722        // Fail-before-pass-after per-arm equivalence pin on the
22723        // `Binario` required-`:exe` arm of the required-slot fold:
22724        // a `:kind Binario` caixa carrying no declared `:exe` entry
22725        // surfaces the same
22726        // [`crate::LayoutError::BinarioWithoutExe`] variant through
22727        // both the compound gate
22728        // [`Caixa::validate_required_kind_slot`] and the standalone
22729        // constructor [`crate::LayoutError::binario_without_exe`].
22730        // Pins the fold — a silent regression that de-folded the
22731        // `Binario` arm would surface here as a mismatch between
22732        // the two dispatches. Sibling in shape to the peer
22733        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22734        // per-arm equivalence pin on the reciprocal code-surface
22735        // fold.
22736        let mut c = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22737        c.kind = CaixaKind::Binario;
22738        c.bibliotecas = vec![];
22739        c.exe = vec![];
22740        let via_method = c.validate_required_kind_slot().unwrap_err();
22741        let via_standalone = crate::LayoutError::binario_without_exe(&c);
22742        assert_eq!(
22743            via_method, via_standalone,
22744            "Caixa::validate_required_kind_slot must surface the \
22745             Binario arm's diagnostic byte-equal to the standalone \
22746             LayoutError::binario_without_exe ctor",
22747        );
22748    }
22749
22750    #[test]
22751    fn validate_required_kind_slot_folds_servico_arm_matches_gate() {
22752        // Per-arm equivalence pin on the `Servico` required-
22753        // `:servicos` arm — the sibling of the Binario arm on the
22754        // required-slot fold. A `:kind Servico` caixa carrying no
22755        // declared `:servicos` entry surfaces the same
22756        // [`crate::LayoutError::ServicoWithoutServicos`] variant
22757        // through both dispatches.
22758        let mut c = Caixa::from_lisp(&Caixa::template("svc")).unwrap();
22759        c.kind = CaixaKind::Servico;
22760        c.bibliotecas = vec![];
22761        c.servicos = vec![];
22762        let via_method = c.validate_required_kind_slot().unwrap_err();
22763        let via_standalone = crate::LayoutError::servico_without_servicos(&c);
22764        assert_eq!(
22765            via_method, via_standalone,
22766            "Caixa::validate_required_kind_slot must surface the \
22767             Servico arm's diagnostic byte-equal to the standalone \
22768             LayoutError::servico_without_servicos ctor",
22769        );
22770    }
22771
22772    #[test]
22773    fn validate_required_kind_slot_folds_acao_arm_matches_gate() {
22774        // Per-arm equivalence pin on the `Acao` required-`:ci` arm
22775        // — the third and last arm on the required-slot fold. A
22776        // `:kind Acao` caixa carrying no declared `:ci` slot
22777        // surfaces the same [`crate::LayoutError::MissingCi`]
22778        // variant through both dispatches. The three per-arm pins
22779        // collectively cover every required-slot axis and every
22780        // owner kind of the arm dispatch.
22781        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
22782        c.kind = CaixaKind::Acao;
22783        c.bibliotecas = vec![];
22784        c.ci = None;
22785        let via_method = c.validate_required_kind_slot().unwrap_err();
22786        let via_standalone = crate::LayoutError::missing_ci(&c);
22787        assert_eq!(
22788            via_method, via_standalone,
22789            "Caixa::validate_required_kind_slot must surface the \
22790             Acao arm's diagnostic byte-equal to the standalone \
22791             LayoutError::missing_ci ctor",
22792        );
22793    }
22794
22795    #[test]
22796    fn validate_required_kind_slot_accepts_owner_kind_with_required_slot_present() {
22797        // Positive control on the owner-kind-with-slot-present
22798        // identity element: each of the three owner kinds
22799        // (`Binario` with a non-empty `:exe`, `Servico` with a
22800        // non-empty `:servicos`, `Acao` with `ci = Some(_)`)
22801        // passes the compound gate cleanly when it declares its
22802        // required slot. Pins the fold's second identity element
22803        // — the paired `is_empty` / `is_none` short-circuit fires
22804        // on every owner kind whose required slot is present, so
22805        // a caixa with its native required slot surfaces no
22806        // diagnostic. A silent regression that dropped the paired
22807        // `is_empty` / `is_none` short-circuit guard on any arm
22808        // would surface here as a false-positive rejection of the
22809        // corresponding owner kind. Peer with the
22810        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
22811        // identity-element pin on the sibling code-surface fold.
22812        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22813        bin.kind = CaixaKind::Binario;
22814        bin.bibliotecas = vec![];
22815        bin.exe = vec!["exe/bin".into()];
22816        bin.validate_required_kind_slot().expect(
22817            "a :kind Binario caixa with declared :exe must pass the \
22818             required-slot gate — Binario's required slot is present",
22819        );
22820
22821        let svc = bare_servico_fixture("svc");
22822        svc.validate_required_kind_slot().expect(
22823            "a :kind Servico caixa with declared :servicos must pass \
22824             the required-slot gate — Servico's required slot is present",
22825        );
22826
22827        let acao = acao_fixture("acao");
22828        acao.validate_required_kind_slot().expect(
22829            "a :kind Acao caixa with declared :ci must pass the \
22830             required-slot gate — Acao's required slot is present",
22831        );
22832    }
22833
22834    #[test]
22835    fn validate_required_kind_slot_accepts_non_owner_kinds() {
22836        // Positive control on the non-owner-kind identity element:
22837        // every kind that is not one of the three owner kinds
22838        // (`Binario` / `Servico` / `Acao`) passes the compound gate
22839        // trivially — each per-arm predicate is
22840        // `self.kind().requires_<slot>()`, which returns `true`
22841        // only for the owner kind of that arm, so a non-owner kind
22842        // short-circuits every per-arm dispatch. Bibliotheca,
22843        // Supervisor, and Aplicacao are the three non-owner kinds
22844        // this pin exercises — none of them owns a required slot in
22845        // this fold (`Biblioteca`'s `:bibliotecas` default-file
22846        // fallback stays on the layout-side `MissingLib` fs-oracle
22847        // gate outside this fold; `Supervisor`'s `:children` and
22848        // `Aplicacao`'s `:membros` are carried by
22849        // [`CaixaKind::requires_children`] /
22850        // [`CaixaKind::requires_membros`] without a paired
22851        // layout-side wire-up). A silent regression that swapped a
22852        // per-arm predicate for a non-`requires_*` guard would
22853        // surface here as a false-positive rejection of the
22854        // corresponding non-owner kind. Peer with the
22855        // `validate_ci_kind_coherence_accepts_absent_ci_on_every_kind`
22856        // identity-element pin on the sibling `:ci` fold.
22857        for kind in CaixaKind::ALL {
22858            if kind.requires_exe() || kind.requires_servicos() || kind.requires_ci() {
22859                continue;
22860            }
22861            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22862            c.kind = *kind;
22863            c.bibliotecas = vec![];
22864            c.exe = vec![];
22865            c.servicos = vec![];
22866            c.ci = None;
22867            c.validate_required_kind_slot().unwrap_or_else(|err| {
22868                panic!(
22869                    "a :kind {kind:?} caixa (a non-owner kind on every \
22870                     required-slot arm) must pass the compound gate — the \
22871                     fold's identity element is the paired \
22872                     `self.kind().requires_<slot>()` short-circuit, got \
22873                     {err:?}",
22874                )
22875            });
22876        }
22877    }
22878}