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::nome_invalid(nome, reason),
4468        )
4469    }
4470
4471    /// Reject `:nome` values whose joint length with the canonical
4472    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4473    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4474    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4475    /// substrate carries materializes the caixa's `:nome` through the
4476    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4477    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4478    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4479    /// `ChartDir.name` + `Chart.yaml::name`
4480    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4481    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4482    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4483    /// `oci://<registry>/lareira-<nome>` chart ref
4484    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4485    /// admission rule strict-parses against DNS-1123-label, the Helm
4486    /// operator's tracking-secret name is derived from `release_name`
4487    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4488    /// K8s object `metadata.name` axes embed the chart name as a
4489    /// prefix — every one fails admission on a > 63-byte chart name.
4490    ///
4491    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4492    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4493    /// `:nome` of 56–63 bytes silently passed validate (the inner
4494    /// DNS-1123 check accepts the bare `:nome`) but produced a
4495    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4496    /// rejected at admission — far from the source `caixa.lisp`, with
4497    /// no field naming the overflow root cause. The
4498    /// [`lareira_chart_name`] helper's own doc comment
4499    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4500    /// "the M4 admission webhook will pin the joint-length invariant
4501    /// when it lands". This gate lands the invariant at the
4502    /// manifest-validate layer rather than waiting for the apiserver
4503    /// — the same fail-at-the-source posture every peer per-axis
4504    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4505    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4506    /// `:edicao`, etc.) takes.
4507    ///
4508    /// Thin wrapper around
4509    /// [`crate::render::is_lareira_chart_name_shape`] (the
4510    /// substrate-side predicate that composes [`lareira_chart_name`] +
4511    /// [`is_dns_1123_label`] via the lifted
4512    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4513    /// shared parser-shaped reason into the
4514    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4515    /// diagnostic is self-locating (the offending `:nome` is named
4516    /// verbatim alongside the rendered chart name and the budget) and
4517    /// the author can shorten in one edit. The gate runs across every
4518    /// `:kind` — `:nome` is the substrate-wide identity axis any
4519    /// future renderer the substrate adds can derive a
4520    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4521    /// the drift footgun where a future kind grows a chart-emitting
4522    /// render path while the validate cascade doesn't catch it.
4523    ///
4524    /// Runs *after* [`Self::validate_nome`] so the narrower
4525    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4526    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4527    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4528    /// specific shape error rather than the chart-name-budget error,
4529    /// preserving the legitimate "well-shaped `:nome` that happens to
4530    /// overflow the joint cap" arm for this gate.
4531    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4532        let nome = self.nome();
4533        crate::render::is_lareira_chart_name_shape(nome)
4534            .map_err(|reason| ManifestError::nome_chart_name_budget_exceeded(nome, reason))
4535    }
4536
4537    /// Reject `:versao` values that don't parse as [`semver::Version`].
4538    /// The top-level Caixa version flows directly into every
4539    /// substrate-side artifact that carries a "this is which version of
4540    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4541    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4542    /// SemVer-2-strict at `helm template` / `helm install` time per
4543    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4544    /// `feira publish` Zig-style `v<versao>` git tag
4545    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4546    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4547    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4548    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4549    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4550    /// `skopeo push`, the lacre closure's pinned versions
4551    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4552    /// `:upgrade-from :from` references peers in this exact `versao`
4553    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4554    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4555    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4556    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4557    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4558    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4559    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4560    /// into the version field a peer `:deps :versao` accepts;
4561    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4562    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4563    /// derive macro stores the raw String) and the failure surfaced at
4564    /// the *first* downstream consumer that strict-parses it: at
4565    /// `helm install` time as a chart-version rejection, at
4566    /// `feira publish` time as a malformed git tag, at lacre-resolve
4567    /// time as a `semver::Error` not naming the offending caixa, at
4568    /// `feira upgrade --to <versao>` time as an unresolvable
4569    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4570    /// and without any field naming the offending `:versao`.
4571    ///
4572    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4573    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4574    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4575    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4576    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4577    /// variant, carrying the offending `:versao` verbatim + a
4578    /// parser-shaped reason naming the specific violation, so the
4579    /// diagnostic is self-locating (the author can grep their
4580    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4581    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4582    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4583    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4584    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4585    /// now structurally equivalent (every value past validate is
4586    /// round-trippable through [`semver::Version::parse`] without
4587    /// re-checking at the renderer, resolver, or operator hot-upgrade
4588    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4589    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4590    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4591    ///
4592    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4593    /// the derive macro stores the raw String) is gated by the
4594    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4595    /// consulted, mirroring the empty-first cascade every per-axis
4596    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4597    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4598    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4599    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4600        let versao = self.versao();
4601        if versao.is_empty() {
4602            return Err(ManifestError::VersaoEmpty);
4603        }
4604        semver::Version::parse(versao)
4605            .map_err(|e| ManifestError::versao_invalid(versao, e.to_string()))?;
4606        Ok(())
4607    }
4608
4609    /// Compound per-`Caixa` entry gate on the M2 `:upgrade-from` slot:
4610    /// folds the three [`crate::upgrade`] top-level validators — the
4611    /// per-entry shape + cross-entry duplicate-`:from` gate
4612    /// ([`crate::upgrade::validate_upgrade_from`]), the cross-slot
4613    /// `:from < :versao` SemVer-2 precedence gate
4614    /// ([`crate::upgrade::validate_upgrade_from_against_versao`]), and the
4615    /// cross-slot `:state-change` ↔ `:on-state-change` composition gate
4616    /// ([`crate::upgrade::validate_upgrade_from_against_behavior`]) — onto
4617    /// one substrate primitive on [`Caixa`]. The three dispatches run in
4618    /// the same order the layout pipeline
4619    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4620    /// author-time gate) has always sequenced them, so the fold is
4621    /// byte-for-byte equivalent to the pre-fold three-block cascade at
4622    /// that call site (pinned by the per-arm
4623    /// `validate_upgrade_from_folds_per_entry_arm_matches_gate` /
4624    /// `_folds_versao_arm_matches_gate` / `_folds_behavior_arm_matches_gate`
4625    /// equivalence pins and by the cross-arm
4626    /// `validate_upgrade_from_per_entry_arm_fires_before_versao_arm` /
4627    /// `_versao_arm_fires_before_behavior_arm` ordering pins).
4628    ///
4629    /// Prior to this lift the three [`crate::upgrade`] top-level validators
4630    /// lived only open-coded at the layout wire-up site
4631    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4632    /// each threaded through the same `self.upgrade_from()` slice and each
4633    /// paired with the same [`crate::LayoutError::UpgradeViolation`]-wrap
4634    /// envelope: every future consumer that wanted to gate `:upgrade-from`
4635    /// as a whole — the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
4636    /// materializer's per-CR admission webhook re-checking `:upgrade-from`
4637    /// after a per-`(:from … :instructions …)` patch, a future `feira
4638    /// validate --upgrade` per-caixa admission verb, a per-`:upgrade-from`
4639    /// overlay resolver a per-cluster overlay lift would materialize —
4640    /// was structurally forced to either re-inline the three-dispatch
4641    /// cascade in lockstep with the layout wire-up (the duplication the
4642    /// PRIME DIRECTIVE names as a bug) or call the whole
4643    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4644    /// peer per-Caixa gate to re-check one slot. Post-fold each such
4645    /// consumer reaches the three-arm compound gate through one call on
4646    /// the substrate primitive.
4647    ///
4648    /// The three arms together name one contract with three axes:
4649    ///
4650    ///   - **per-entry + cross-entry graph-edge invariant** — every entry's
4651    ///     `:from` parses as SemVer-2 and every per-instruction / within-
4652    ///     entry ordering / singularity gate on each entry's
4653    ///     `:instructions` list passes, and no two entries share the same
4654    ///     parsed `:from` (the wasm-operator's OTP appup
4655    ///     `release_handler:install_release/1` analog picks at most one
4656    ///     matching block per running version — two entries with the same
4657    ///     parsed semver are an ambiguous edge in the typed upgrade graph).
4658    ///   - **cross-slot reachability invariant** — every entry's `:from`
4659    ///     is strictly less than the caixa's own `:versao` under SemVer-2
4660    ///     precedence. An entry whose `:from >= :versao` is structurally
4661    ///     unreachable by the operator's `:from`-match dispatch (the
4662    ///     operator loads the current `:versao` and matches the *running*
4663    ///     version against each entry's `:from`; an entry whose `:from >=
4664    ///     :versao` is never reached because the operator never runs a
4665    ///     version >= the current one that it could then upgrade *to* the
4666    ///     current one).
4667    ///   - **cross-slot composition invariant** — every entry carrying a
4668    ///     `(:state-change …)` instruction has a `:behavior
4669    ///     :on-state-change` callback declared on the same caixa. The
4670    ///     per-version migration script is the `gen_server:code_change/3`
4671    ///     analog and the runtime hook it is delivered through during hot
4672    ///     upgrade is the `:on-state-change` callback (the upgrade.rs
4673    ///     module doc pins the composition verbatim: "Composes with the
4674    ///     `:behavior :on-state-change` callback to deliver state migration
4675    ///     during hot upgrades").
4676    ///
4677    /// All three axes must hold together — every consumer's
4678    /// `:upgrade-from` accept-set past this compound gate is the same
4679    /// set the `feira build` author-time gate admits.
4680    ///
4681    /// The per-slot compound entry gate discipline lifted here onto the
4682    /// M2 `:upgrade-from` axis is the sibling of the peer per-kind
4683    /// compound entry gates ([`crate::render::require_supervisor_view`]
4684    /// / [`crate::render::require_aplicacao_view`] /
4685    /// [`crate::render::require_v0_servico_shape`]) that fold every
4686    /// per-kind cascade at the per-kind altitude, and of the peer
4687    /// per-slot compound gates ([`crate::AplicacaoSpec::validate_contratos`],
4688    /// [`crate::MeshPolicy::validate`],
4689    /// [`crate::SupervisorSpec::validate_children`]) that fold every
4690    /// structural axis on their slot onto one substrate primitive.
4691    /// Extended here to the last unlifted compound-cascade wire-up at
4692    /// the layout-pipeline altitude — the three-dispatch M2
4693    /// `:upgrade-from` cascade that lived only open-coded at the layout
4694    /// wire-up site.
4695    ///
4696    /// The per-instruction script-path on-disk existence-probe walk that
4697    /// [`crate::layout::StandardLayout::verify`] runs immediately after
4698    /// this gate (which resolves each entry's `:instructions
4699    /// (:state-change :script)` against the layout root) stays open-coded
4700    /// at the layout wire-up site — that arm needs the filesystem oracle
4701    /// on the [`crate::LayoutInvariants`] trait, not the pure per-Caixa
4702    /// typed-shape surface this compound gate folds. Same posture the
4703    /// peer [`Self::validate_code_paths`] takes on the sibling code-path
4704    /// axes: the typed-shape gate fires on the per-Caixa surface, the
4705    /// on-disk existence check fires on the [`crate::StandardLayout`]
4706    /// surface.
4707    ///
4708    /// # Errors
4709    ///
4710    /// Returns [`crate::UpgradeError::FromInvalid`] /
4711    /// [`crate::UpgradeError::ModuleEmpty`] /
4712    /// [`crate::UpgradeError::ModuleInvalid`] /
4713    /// [`crate::UpgradeError::EmptyScript`] /
4714    /// [`crate::UpgradeError::AbsoluteScript`] /
4715    /// [`crate::UpgradeError::ParentEscapeScript`] /
4716    /// [`crate::UpgradeError::NonLispExtensionScript`] /
4717    /// [`crate::UpgradeError::RestartNotExclusive`] /
4718    /// [`crate::UpgradeError::StateChangeWithoutPriorLoad`] /
4719    /// [`crate::UpgradeError::PurgeWithoutPriorLoad`] /
4720    /// [`crate::UpgradeError::StateChangeAfterCleanup`] /
4721    /// [`crate::UpgradeError::DuplicateLoadModule`] /
4722    /// [`crate::UpgradeError::DuplicateStateChange`] /
4723    /// [`crate::UpgradeError::DuplicateCleanup`] /
4724    /// [`crate::UpgradeError::DuplicateFrom`] on the per-entry +
4725    /// cross-entry axis; [`crate::UpgradeError::FromNotBeforeVersao`] on
4726    /// the cross-slot `:from ↔ :versao` axis;
4727    /// [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
4728    /// on the cross-slot `:state-change ↔ :on-state-change` axis.
4729    pub fn validate_upgrade_from(&self) -> Result<(), crate::UpgradeError> {
4730        crate::upgrade::validate_upgrade_from(self.upgrade_from())?;
4731        crate::upgrade::validate_upgrade_from_against_versao(self.upgrade_from(), self.versao())?;
4732        crate::upgrade::validate_upgrade_from_against_behavior(
4733            self.upgrade_from(),
4734            self.behavior(),
4735        )?;
4736        Ok(())
4737    }
4738
4739    /// Compound per-`Caixa` entry gate on the M2 `:limits` slot — folds
4740    /// the [`crate::LimitsSpec::validate`] four-axis cascade (`:memory`
4741    /// wasm32 zero-floor / below-page / above-cap / non-page-multiple;
4742    /// `:fuel` zero-floor / cap; `:wall-clock` zero-floor / cap; `:cpu`
4743    /// zero-floor / cap) onto one substrate primitive on [`Caixa`]. The
4744    /// `#[serde(default)]` absent-slot arm (`limits: None`, the
4745    /// canonical "no bound declared — engine-default applies" author
4746    /// shape [`crate::LimitsSpec::is_empty`]'s per-axis `None` cascade
4747    /// reads) is the fold's identity element and passes trivially; the
4748    /// present-slot arm (`limits: Some(l)`) dispatches to
4749    /// [`crate::LimitsSpec::validate`] verbatim, threading its per-axis
4750    /// [`crate::LimitsError`] Display through untouched.
4751    ///
4752    /// Prior to this lift the M2 `:limits` slot lived only wired
4753    /// open-coded at the layout wire-up site
4754    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4755    /// through the `if let Some(l) = caixa.limits() { l.validate() … }`
4756    /// three-line `Option::None → Ok(()) | Some(_) → …` unwrap-and-
4757    /// dispatch pattern paired with the same
4758    /// [`crate::LayoutError::LimitsViolation`]-wrap envelope: every
4759    /// future consumer that wanted to gate `:limits` as a whole — the
4760    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4761    /// per-CR admission webhook re-checking `:limits` after a per-
4762    /// `{:memory, :fuel, :wall-clock, :cpu}` patch (the exact case the
4763    /// [`Self::limits`] accessor docstring names as the second
4764    /// consumer of the slot), a future `feira validate --limits` per-
4765    /// caixa admission verb, a per-`:limits` overlay resolver a per-
4766    /// cluster `:limits-overrides` overlay lift would materialize — was
4767    /// structurally forced to either re-inline the two-line
4768    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4769    /// pattern in lockstep with the layout wire-up (the duplication the
4770    /// PRIME DIRECTIVE names as a bug) or call the whole
4771    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4772    /// peer per-Caixa gate ([`Self::validate_nome`],
4773    /// [`Self::validate_versao`], [`Self::validate_deps`],
4774    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4775    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4776    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4777    /// [`Self::validate_upgrade_from`], [`Self::validate_code_paths`],
4778    /// plus the per-kind `require_supervisor_view` /
4779    /// `require_aplicacao_view` gates, plus the on-disk existence
4780    /// walks) to re-check one slot. Post-lift each such consumer
4781    /// reaches the [`crate::LimitsSpec::validate`] four-axis cascade
4782    /// (and its identity-element on the absent slot) through one call
4783    /// on the substrate primitive.
4784    ///
4785    /// The per-slot compound entry-gate discipline lifted here onto the
4786    /// M2 `:limits` axis is the sibling of the peer per-slot compound
4787    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4788    /// [`crate::MeshPolicy::validate`],
4789    /// [`crate::SupervisorSpec::validate_children`],
4790    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`]) that
4791    /// fold every structural + cross-slot axis on their slot onto one
4792    /// substrate primitive. Extended here to the M2 `:limits` slot, the
4793    /// first of the two M2 typed slots (`:limits`, `:behavior`) whose
4794    /// per-Caixa compound-gate wire-up still lived open-coded at the
4795    /// layout altitude after the [`Self::validate_upgrade_from`] lift
4796    /// (d6801df) closed the sibling M2 slot's cascade.
4797    ///
4798    /// # Errors
4799    ///
4800    /// Returns every [`crate::LimitsError`] variant on the present-slot
4801    /// arm — verbatim from [`crate::LimitsSpec::validate`]. Passes
4802    /// trivially on the absent-slot arm (`limits: None`, the fold's
4803    /// identity element).
4804    pub fn validate_limits(&self) -> Result<(), crate::LimitsError> {
4805        match self.limits() {
4806            Some(l) => l.validate(),
4807            None => Ok(()),
4808        }
4809    }
4810
4811    /// Compound per-`Caixa` entry gate on the M2 `:behavior` slot's
4812    /// pure typed-shape surface — folds the
4813    /// [`crate::BehaviorSpec::validate`] six-slot value-shape cascade
4814    /// (each declared `:on-init` / `:on-call` / `:on-cast` / `:on-info`
4815    /// / `:on-state-change` / `:on-terminate` callback-path is
4816    /// non-empty / relative / no-`..`-parent-escape / terminating-
4817    /// `.lisp`-extension, routed through the shared
4818    /// [`crate::render::require_sandboxed_lisp_path`] arm-set) onto one
4819    /// substrate primitive on [`Caixa`]. The `#[serde(default)]`
4820    /// absent-slot arm (`behavior: None`, the canonical "no callback
4821    /// declared — the runtime falls back to the wasm-engine's default
4822    /// callback per arm" author shape [`crate::BehaviorSpec::is_empty`]'s
4823    /// per-slot `None` cascade reads) is the fold's identity element
4824    /// and passes trivially; the present-slot arm (`behavior: Some(b)`)
4825    /// dispatches to [`crate::BehaviorSpec::validate`] verbatim,
4826    /// threading its per-slot [`crate::BehaviorError`] Display through
4827    /// untouched.
4828    ///
4829    /// Scope note — the on-disk callback-path existence walk paired
4830    /// with the value-shape gate at
4831    /// [`crate::layout::StandardLayout::verify`] stays open-coded at
4832    /// the layout altitude, because it needs the
4833    /// [`crate::layout::LayoutInvariants`] filesystem oracle
4834    /// ([`crate::layout::LayoutInvariants::exists`]) that the pure
4835    /// per-Caixa typed-shape surface this compound gate folds onto has
4836    /// no reference to. Same posture the peer M2 `:upgrade-from`
4837    /// per-Caixa compound gate ([`Self::validate_upgrade_from`]
4838    /// d6801df) already carries: the pure typed-shape surface folds
4839    /// onto the substrate primitive; the per-instruction script-path
4840    /// existence probe on the paired axis (there `:state-change
4841    /// :script`; here `:on-*`) stays at the layout altitude.
4842    ///
4843    /// Prior to this lift the pure value-shape surface of the M2
4844    /// `:behavior` slot lived only wired open-coded at the layout
4845    /// wire-up site ([`crate::layout::StandardLayout::verify`],
4846    /// caixa-core/src/layout.rs), through the
4847    /// `if let Some(b) = caixa.behavior() { b.validate() … }`
4848    /// unwrap-and-dispatch pattern paired with the same
4849    /// [`crate::LayoutError::BehaviorViolation`]-wrap envelope: every
4850    /// future consumer that wanted to gate the `:behavior` slot's
4851    /// value-shape as a whole — the deferred
4852    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4853    /// admission webhook re-checking `:behavior` after a per-`{:on-init,
4854    /// :on-call, :on-cast, :on-info, :on-state-change, :on-terminate}`
4855    /// patch (the exact case the peer `:on-*` accessor docstrings on
4856    /// [`crate::BehaviorSpec`] already name as deferred consumers of
4857    /// the slot), a future `feira validate --behavior` per-caixa
4858    /// admission verb, a per-`:behavior` overlay resolver a future
4859    /// per-cluster callback-overlay lift would materialize — was
4860    /// structurally forced to either re-inline the two-line
4861    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4862    /// pattern in lockstep with the layout wire-up (the duplication the
4863    /// PRIME DIRECTIVE names as a bug) or call the whole
4864    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4865    /// peer per-Caixa gate ([`Self::validate_nome`],
4866    /// [`Self::validate_versao`], [`Self::validate_deps`],
4867    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4868    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4869    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4870    /// [`Self::validate_limits`], [`Self::validate_upgrade_from`],
4871    /// [`Self::validate_code_paths`], plus the per-kind
4872    /// `require_supervisor_view` / `require_aplicacao_view` gates, plus
4873    /// the on-disk existence walks) to re-check one slot. Post-lift
4874    /// each such consumer reaches the [`crate::BehaviorSpec::validate`]
4875    /// six-slot cascade (and its identity-element on the absent slot)
4876    /// through one call on the substrate primitive.
4877    ///
4878    /// The per-slot compound entry-gate discipline lifted here onto the
4879    /// M2 `:behavior` axis is the sibling of the peer per-slot compound
4880    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4881    /// [`crate::MeshPolicy::validate`],
4882    /// [`crate::SupervisorSpec::validate_children`],
4883    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`],
4884    /// [`Self::validate_limits`]) that fold every structural + cross-
4885    /// slot axis on their slot onto one substrate primitive. Extended
4886    /// here to the M2 `:behavior` slot, the last of the four M2 typed
4887    /// slots (`:limits`, `:behavior`, `:upgrade-from`, plus the
4888    /// supervisor-only `:children` peer) whose per-Caixa compound-gate
4889    /// wire-up still lived open-coded at the layout altitude after the
4890    /// [`Self::validate_limits`] lift (baa4688) closed the sibling M2
4891    /// `:limits` slot's cascade. With this lift the "one named per-slot
4892    /// / per-Caixa compound gate per typed slot folding every structural
4893    /// axis on that slot (plus the `Option::None` identity element for
4894    /// the `Option`-shaped slots) onto one substrate primitive"
4895    /// discipline spans every M2 typed slot uniformly, so a reader who
4896    /// has learned any peer M2 gate reads `:behavior` without a per-
4897    /// slot exception carve-out.
4898    ///
4899    /// # Errors
4900    ///
4901    /// Returns every [`crate::BehaviorError`] variant on the present-
4902    /// slot arm — verbatim from [`crate::BehaviorSpec::validate`].
4903    /// Passes trivially on the absent-slot arm (`behavior: None`, the
4904    /// fold's identity element).
4905    pub fn validate_behavior(&self) -> Result<(), crate::BehaviorError> {
4906        match self.behavior() {
4907            Some(b) => b.validate(),
4908            None => Ok(()),
4909        }
4910    }
4911
4912    /// Reject `:restart-window` values the shared
4913    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4914    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4915    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4916    /// `Option<Duration>` routed through the shared codec via `with =
4917    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4918    /// view-construction path ([`Self::supervisor_view`]) folds the
4919    /// raw string through the same shared codec and soft-swallows the
4920    /// parse error as `None` to keep the view best-effort. Without
4921    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4922    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4923    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4924    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4925    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4926    /// edge case) silently produced a `SupervisorSpec` with
4927    /// `restart_window: None`, indistinguishable from the canonical
4928    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4929    /// `MaxIntensity / Period` invariant turns into a never-reset
4930    /// supervisor far from the source `caixa.lisp`, with no field
4931    /// naming the offending `:restart-window`. Lifting the gate to a
4932    /// Caixa-level validator mirrors the trajectory of the peer
4933    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4934    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4935    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4936    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4937    ///
4938    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4939    /// (the shared codec backing `:supervisor :restart-window` as
4940    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4941    /// `:politicas :circuit-breaker :window` — all three covered by
4942    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4943    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4944    /// variant, carrying the offending raw string + a parser-shaped
4945    /// reason naming the canonical authoring form, so the diagnostic
4946    /// is self-locating (the author can grep their `caixa.lisp` for
4947    /// `:restart-window "<value>"` and fix it in one edit) and
4948    /// uniform with every other manifest-level validate diagnostic.
4949    /// With this gate the four `:restart-window`-shaped surfaces (the
4950    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4951    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4952    /// now structurally equivalent — every value past the codec is in
4953    /// one accepted set, by construction.
4954    ///
4955    /// `None` (the canonical "omit the slot to express no reset"
4956    /// shape) is accepted trivially — the gate is a no-op when the
4957    /// author didn't author a window. The empty string is rejected by
4958    /// the shared codec (its digit-only gate refuses an empty
4959    /// magnitude), surfacing the same `RestartWindowMalformed`
4960    /// diagnostic as every other rejected non-canonical shape.
4961    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4962        let Some(s) = self.restart_window() else {
4963            return Ok(());
4964        };
4965        crate::supervisor::duration_codec::parse(s)
4966            .map(|_| ())
4967            .map_err(|reason| ManifestError::RestartWindowMalformed {
4968                restart_window: s.to_string(),
4969                reason,
4970            })
4971    }
4972
4973    /// Compound per-`Caixa` entry gate on the Aplicacao-kind mesh-slot
4974    /// family — folds the paired [`crate::AplicacaoSpec::validate`]
4975    /// typed-shape cascade (per-slot gates on `:membros`, `:contratos`,
4976    /// `:entrada`, `:placement`, `:politicas`, in that declared order)
4977    /// plus the cross-slot self-edge gate
4978    /// ([`crate::aplicacao::validate_no_self_membership`], the
4979    /// `:membros :caixa` ≠ `:nome` invariant the typed view cannot
4980    /// enforce on its own because it carries the membros but not the
4981    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4982    /// non-Aplicacao kinds the fold is the identity element — the paired
4983    /// [`Self::aplicacao_view`] accessor returns `None` off the
4984    /// Aplicacao arm (peer with the [`Self::validate_limits`] /
4985    /// [`Self::validate_behavior`] M2 `Option`-arm identity element),
4986    /// so the gate passes trivially without touching the mesh slots.
4987    ///
4988    /// Prior to this lift the paired cascade lived only wired open-coded
4989    /// at the layout wire-up site
4990    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4991    /// as the three-line `let view = caixa.aplicacao_view().expect(...);
4992    /// view.validate() … validate_no_self_membership(...) …` pattern
4993    /// paired with two `.map_err(|err| LayoutError::AplicacaoViolation
4994    /// { caixa, issue })` wraps — every future consumer that wanted to
4995    /// gate the Aplicacao-shape cascade as a whole (the deferred
4996    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4997    /// admission webhook re-checking `:membros` / `:contratos` after a
4998    /// per-slot patch, a future `feira validate --aplicacao` per-caixa
4999    /// admission verb, a per-Aplicacao overlay resolver) was structurally
5000    /// forced to either re-inline the two-dispatch cascade in lockstep
5001    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5002    /// names as a bug) or call the whole
5003    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5004    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5005    /// such consumer reaches the two-arm compound gate through one call
5006    /// on the substrate primitive.
5007    ///
5008    /// Peer to the [`crate::render::require_aplicacao_view`] compound
5009    /// entry gate every per-Aplicacao *renderer* routes through
5010    /// (3aefefb folded `validate_no_self_membership` onto the renderer
5011    /// path) — this gate mirrors the same fold on the *layout* path, so
5012    /// the two consumers of the Aplicacao-shape cascade (the author-time
5013    /// gate and every per-Aplicacao renderer) share one substrate
5014    /// primitive rather than two open-coded cascades kept in lockstep.
5015    /// Same lift discipline the peer per-slot compound gates
5016    /// ([`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5017    /// b5dd55e, [`Self::validate_limits`] baa4688,
5018    /// [`Self::validate_behavior`] 0d2877a) each carry.
5019    ///
5020    /// # Errors
5021    ///
5022    /// Returns every [`crate::AplicacaoError`] variant on the present-
5023    /// kind arm — the typed-shape cascade's per-slot arms first
5024    /// (matching [`crate::AplicacaoSpec::validate`]'s declared order),
5025    /// then the cross-slot self-edge arm
5026    /// ([`crate::AplicacaoError::MembroIsSelfAplicacao`]). Passes
5027    /// trivially on non-Aplicacao kinds (the fold's identity element).
5028    pub fn validate_aplicacao_shape(&self) -> Result<(), crate::AplicacaoError> {
5029        let Some(view) = self.aplicacao_view() else {
5030            return Ok(());
5031        };
5032        view.validate()?;
5033        crate::aplicacao::validate_no_self_membership(self.membros(), self.nome())?;
5034        Ok(())
5035    }
5036
5037    /// Compound per-`Caixa` entry gate on the Supervisor-kind
5038    /// supervision-tree slot family — folds the paired
5039    /// [`crate::SupervisorSpec::validate`] typed-shape cascade
5040    /// (`:estrategia` ↔ `:children` invariants, `:max-restarts` /
5041    /// `:restart-window` bounds, per-child DNS-1123 `:caixa` names,
5042    /// semver-valid `:versao` constraints, the set-not-multiset
5043    /// duplicate-child gate) plus the cross-slot self-edge gate
5044    /// ([`crate::supervisor::validate_no_self_supervision`], the
5045    /// `:children :caixa` ≠ `:nome` invariant the typed view cannot
5046    /// enforce on its own because it carries the children but not the
5047    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
5048    /// non-Supervisor kinds the fold is the identity element — the paired
5049    /// [`Self::supervisor_view`] accessor returns `None` off the
5050    /// Supervisor arm (peer with the [`Self::validate_limits`] /
5051    /// [`Self::validate_behavior`] M2 `Option`-arm identity element and
5052    /// the sibling per-Aplicacao [`Self::validate_aplicacao_shape`]),
5053    /// so the gate passes trivially without touching the supervision-tree
5054    /// slots.
5055    ///
5056    /// Prior to this lift the paired cascade lived only wired open-coded
5057    /// at the layout wire-up site
5058    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
5059    /// as the three-line `let view = caixa.supervisor_view().expect(...);
5060    /// view.validate() … validate_no_self_supervision(...) …` pattern
5061    /// paired with two `.map_err(|err| LayoutError::SupervisorViolation
5062    /// { caixa, issue })` wraps — every future consumer that wanted to
5063    /// gate the Supervisor-shape cascade as a whole (the wasm-operator's
5064    /// hierarchical reconciliation scheduler re-checking `:children` /
5065    /// `:estrategia` after a per-slot patch, the M4
5066    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
5067    /// webhook, a future `feira validate --supervisor` per-caixa
5068    /// admission verb, a per-Supervisor overlay resolver) was structurally
5069    /// forced to either re-inline the two-dispatch cascade in lockstep
5070    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5071    /// names as a bug) or call the whole
5072    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5073    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5074    /// such consumer reaches the two-arm compound gate through one call
5075    /// on the substrate primitive.
5076    ///
5077    /// Peer to the [`crate::render::require_supervisor_view`] compound
5078    /// entry gate every per-Supervisor *renderer* would route through
5079    /// (which already folds the same `spec.validate()` +
5080    /// `validate_no_self_supervision` two-arm cascade behind its
5081    /// `require_kind` + `validate_restart_window` prelude) — this gate
5082    /// mirrors the same fold on the *layout* path, so the two consumers
5083    /// of the Supervisor-shape cascade (the author-time gate and every
5084    /// per-Supervisor renderer) share one substrate primitive rather
5085    /// than two open-coded cascades kept in lockstep. Same lift
5086    /// discipline the peer per-slot compound gates
5087    /// ([`Self::validate_aplicacao_shape`] 949a7a0,
5088    /// [`Self::validate_upgrade_from`] d6801df,
5089    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5090    /// baa4688, [`Self::validate_behavior`] 0d2877a) each carry.
5091    ///
5092    /// # Errors
5093    ///
5094    /// Returns every [`crate::SupervisorError`] variant on the present-
5095    /// kind arm — the typed-shape cascade's per-slot arms first
5096    /// (matching [`crate::SupervisorSpec::validate`]'s declared order),
5097    /// then the cross-slot self-edge arm
5098    /// ([`crate::SupervisorError::ChildSupervisesSelf`]). Passes
5099    /// trivially on non-Supervisor kinds (the fold's identity element).
5100    pub fn validate_supervisor_shape(&self) -> Result<(), crate::SupervisorError> {
5101        let Some(view) = self.supervisor_view() else {
5102            return Ok(());
5103        };
5104        view.validate()?;
5105        crate::supervisor::validate_no_self_supervision(self.children(), self.nome())?;
5106        Ok(())
5107    }
5108
5109    /// Compound per-`Caixa` entry gate on the Acao-kind `:ci` slot
5110    /// family — folds the [`crate::decompose_ci`] typed decompose gate
5111    /// (`canteiro_types::decompose` refusing every illegal
5112    /// [`canteiro_types::CiRun`] shape: duplicate node name, dependency
5113    /// on an undeclared node, dependency cycle) onto one substrate
5114    /// primitive on [`Caixa`]. On non-`Acao` kinds the fold is the
5115    /// identity element — the paired [`Self::kind`] `is_acao()` guard
5116    /// short-circuits before the decompose gate ever fires (peer with
5117    /// the [`Self::validate_aplicacao_shape`] /
5118    /// [`Self::validate_supervisor_shape`] typed-view identity element
5119    /// and the [`Self::validate_limits`] / [`Self::validate_behavior`]
5120    /// M2 `Option`-arm identity element), so the gate passes trivially
5121    /// without touching the `:ci` slot. An `:kind Acao` caixa with
5122    /// `ci = None` is also an identity-element pass: the presence gate
5123    /// is the sibling axis owned by [`crate::LayoutError::MissingCi`] /
5124    /// [`crate::require_ci`] / [`crate::MissingCiSlot`], not by the
5125    /// decompose gate — a caixa that carries no `:ci` slot has no run
5126    /// to decompose. Same split the peer per-Servico
5127    /// [`crate::LayoutError::ServicoWithoutServicos`] presence gate and
5128    /// per-Binario [`crate::LayoutError::BinarioWithoutExe`] presence
5129    /// gate keep from their sibling per-slot shape gates, so the two
5130    /// axes stay separately diagnosable at the layout altitude.
5131    ///
5132    /// Prior to this lift the decompose gate lived only wired
5133    /// open-coded at the [`caixa_actions::validate`] renderer-side
5134    /// entry gate (routed through the substrate-canonical
5135    /// [`crate::require_acao_view`] compound helper) — the *layout*
5136    /// pipeline ([`crate::layout::StandardLayout::verify`], caixa-core/
5137    /// src/layout.rs) only checked `:ci` *presence* via
5138    /// [`crate::LayoutError::MissingCi`], so a `:kind Acao` caixa
5139    /// carrying a structurally illegal `:ci` (a duplicate node name, a
5140    /// dependency on an undeclared node, a dependency cycle) passed
5141    /// `feira build` cleanly and surfaced the diagnostic only when
5142    /// [`caixa_actions::validate`] later refused it — far from the
5143    /// source `caixa.lisp` on the author-time gate side. Every future
5144    /// consumer that wanted to gate the Acao-shape cascade as a whole
5145    /// (a per-`Acao` CR materializer's admission webhook re-checking
5146    /// `:ci` after a per-node patch, a future `feira validate --acao`
5147    /// per-caixa admission verb, a per-`Acao` overlay resolver
5148    /// rejecting an added / renamed node against a cluster-local
5149    /// snapshot) was structurally forced to either re-inline the
5150    /// decompose dispatch in lockstep with the renderer-side wire-up
5151    /// (the duplication the PRIME DIRECTIVE names as a bug) or call
5152    /// the whole [`caixa_actions::validate`] renderer and pay the
5153    /// per-node accumulation to re-check one slot. Post-fold each such
5154    /// consumer reaches the decompose gate through one call on the
5155    /// substrate primitive.
5156    ///
5157    /// Peer to the [`crate::require_acao_view`] compound entry gate
5158    /// every per-`Acao` *renderer* routes through (which already folds
5159    /// the same `require_ci + decompose_ci` two-arm cascade behind its
5160    /// `require_kind` prelude) — this gate mirrors the same fold on
5161    /// the *layout* path, so the two consumers of the Acao-shape
5162    /// cascade (the author-time gate and every per-`Acao` renderer)
5163    /// share one substrate primitive rather than two open-coded
5164    /// cascades kept in lockstep. Same lift discipline the peer
5165    /// per-kind compound gates ([`Self::validate_aplicacao_shape`]
5166    /// 949a7a0, [`Self::validate_supervisor_shape`] 4c70105,
5167    /// [`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5168    /// b5dd55e, [`Self::validate_limits`] baa4688,
5169    /// [`Self::validate_behavior`] 0d2877a) each carry. Closes the
5170    /// last per-kind asymmetry: with this lift the four typed
5171    /// named-caixa kinds (`Servico` / `Aplicacao` / `Supervisor` /
5172    /// `Acao`) each carry a compound per-`Caixa` shape gate on the
5173    /// substrate, and the layout pipeline routes through the same one
5174    /// substrate primitive per kind rather than four open-coded
5175    /// cascades.
5176    ///
5177    /// # Errors
5178    ///
5179    /// Returns the [`crate::CiDecomposeFailure`] typed view on the
5180    /// present-slot arm — the caixa's `:nome` alongside the borrowed
5181    /// [`canteiro_types::DecomposeError`] source (`DuplicateNode` /
5182    /// `UnknownDep` / `Cycle`) verbatim, so a consumer that fans on
5183    /// the specific arm reaches for `err.source` directly rather than
5184    /// re-parsing the Display bytes. Passes trivially on non-`Acao`
5185    /// kinds and on `:kind Acao` caixas with absent `:ci` (the fold's
5186    /// two identity-element arms).
5187    pub fn validate_acao_shape(&self) -> Result<(), crate::CiDecomposeFailure> {
5188        if !self.kind().is_acao() {
5189            return Ok(());
5190        }
5191        let Some(ci) = self.ci() else {
5192            return Ok(());
5193        };
5194        crate::render::decompose_ci(self, ci).map(|_| ())
5195    }
5196
5197    /// Compound per-`Caixa` kind ↔ typed-slot coherence gate on the
5198    /// three "declared but ignored" typed-slot families — M3 mesh
5199    /// (`:membros` / `:contratos` / `:politicas` / `:placement` /
5200    /// `:entrada`, owned by `:kind Aplicacao`, MESH-COMPOSITION §III.1),
5201    /// supervisor-tree (`:estrategia` / `:max-restarts` /
5202    /// `:restart-window` / `:children`, owned by `:kind Supervisor`,
5203    /// INSPIRATIONS §II.2), and M2 Servico-runtime (`:limits` /
5204    /// `:behavior` / `:upgrade-from`, owned by `:kind Servico`,
5205    /// INSPIRATIONS §III.1 / §II.3 / §II.4). Folds the three sibling
5206    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5207    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5208    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
5209    /// gates — each pre-lift a self-similar five-line
5210    /// `if !caixa.kind().is_<owner>() { let slots = caixa.declared_
5211    /// <family>_slots(); if !slots.is_empty() { return
5212    /// Err(LayoutError::<family>_on_non_<owner>(caixa, slots)); } }`
5213    /// block at [`crate::layout::StandardLayout::verify`] — onto one
5214    /// substrate primitive on [`Caixa`]. Every arm passes as an
5215    /// identity element on the owner kind (the paired
5216    /// [`Self::kind`] `is_<owner>()` guard short-circuits before the
5217    /// per-family `declared_*_slots` gate fires) and on non-owner
5218    /// kinds carrying no declared slot in that family (the
5219    /// [`Vec::is_empty`] check short-circuits before the wrap fires),
5220    /// so a bare no-code caixa on any kind passes the fold trivially
5221    /// on all three arms.
5222    ///
5223    /// Prior to this lift the three-arm cascade lived only wired
5224    /// open-coded at the layout wire-up site
5225    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/
5226    /// layout.rs) as three self-similar five-line blocks paired with
5227    /// three [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5228    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5229    /// [`crate::LayoutError::servico_slots_on_non_servico`] ctor
5230    /// dispatches (each of which the peer
5231    /// [`crate::layout::layout_slot_kind_ctors!`] macro already folds
5232    /// onto one substrate primitive per typed variant, 0419438) —
5233    /// every future consumer that wanted to gate the whole
5234    /// kind-coherence cascade as a unit (the deferred
5235    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5236    /// webhook re-checking every typed-slot family after a per-slot
5237    /// patch, a future `feira validate --kind-coherence` per-caixa
5238    /// admission verb, a per-`Caixa` overlay resolver rejecting a
5239    /// kind-foreign patch against a cluster-local snapshot) was
5240    /// structurally forced to either re-inline the three-block
5241    /// cascade in lockstep with the layout wire-up (the duplication
5242    /// the PRIME DIRECTIVE names as a bug) or call the whole
5243    /// [`crate::layout::StandardLayout::verify`] pipeline and pay
5244    /// every peer per-`Caixa` gate to re-check three slot families.
5245    /// Post-fold each such consumer reaches the three-arm cascade
5246    /// through one call on the substrate primitive.
5247    ///
5248    /// Diagnostic order matches the pre-fold layout wire-up
5249    /// canonical sequence — mesh → supervisor → servico — pinned by
5250    /// the load-bearing
5251    /// `validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm`
5252    /// / `_supervisor_arm_fires_before_servico_arm` ordering pins
5253    /// below. The three arms enumerate every typed-slot family the
5254    /// substrate carries whose "declared but ignored" footgun is
5255    /// gated at the layout altitude by a `{ caixa, kind, slots }`
5256    /// wrap variant — the peer
5257    /// [`crate::LayoutError::ForeignCodeSlot`] gate on the
5258    /// code-surface family sits outside this fold because
5259    /// [`Self::declared_foreign_code_slots`] bakes the kind-check
5260    /// into the helper (so the layout wire-up carries no outer
5261    /// `if !caixa.kind().is_<owner>()` guard), and the peer
5262    /// [`crate::LayoutError::CiOnNonAcao`] gate on the `:ci` axis
5263    /// carries a distinct `{ caixa, kind }` wrap shape (no `slots`
5264    /// field — `:ci` is a single `Option` not a `Vec`-of-named-slots)
5265    /// and rides on its own peer substrate primitive
5266    /// [`Self::validate_ci_kind_coherence`] (the direct sibling to
5267    /// this fold on the `:ci` axis) — the two folds share the same
5268    /// altitude and diagnostic order at the layout wire-up site but
5269    /// keep their distinct envelope shapes, so no consumer of
5270    /// `CiOnNonAcao` sees a variant rename.
5271    ///
5272    /// Peer to the per-kind compound entry gates every substrate
5273    /// primitive on the M2/M3 typed-slot family already carries
5274    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5275    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5276    /// [`Self::validate_upgrade_from`] d6801df,
5277    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5278    /// [`Self::validate_supervisor_shape`] 4c70105,
5279    /// [`Self::validate_acao_shape`] 5d6df54): the author-time gate
5280    /// axis on the *per-slot* algebra now shares one substrate
5281    /// primitive per compound gate, and this lift closes the
5282    /// symmetric axis on the *cross-family* kind ↔ slot coherence
5283    /// algebra so the layout pipeline routes the three self-similar
5284    /// gates through one substrate primitive rather than three
5285    /// open-coded blocks. Every future kind that adds its own
5286    /// exclusive typed-slot family (an `Actor`-owned per-virtual-
5287    /// actor grain slot the M5 Orleans-inspired kind reaches
5288    /// through, a per-Aplicacao overlay slot the M4 CR materializer
5289    /// consults) folds onto this compound gate as one arm addition
5290    /// rather than a fourth open-coded block at the wire-up site.
5291    ///
5292    /// # Errors
5293    ///
5294    /// Returns the first [`crate::LayoutError`] variant surfacing
5295    /// under the canonical mesh → supervisor → servico order:
5296    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] on a non-
5297    /// Aplicacao caixa with a declared M3 mesh slot,
5298    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] on a
5299    /// non-Supervisor caixa with a declared supervisor-tree slot,
5300    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] on a
5301    /// non-Servico caixa with a declared M2 slot. Passes trivially
5302    /// on the owner kind of each family and on non-owner kinds
5303    /// carrying no declared slot in that family (the fold's identity
5304    /// element on both axes).
5305    pub fn validate_kind_slot_coherence(&self) -> Result<(), crate::LayoutError> {
5306        // Each of the three arms routes through the shared
5307        // [`Self::run_kind_owned_slot_family_gate`] substrate primitive
5308        // — the outer non-owner-kind guard + inner accumulator + inner
5309        // emptiness-guard + wrap arm shape now lands on one dispatch
5310        // per family rather than a four-line open-coded block in
5311        // lockstep across all three arms. Canonical mesh → supervisor
5312        // → servico order preserved (the primitive short-circuits
5313        // arm-by-arm; the outer `?;` cascade at this altitude threads
5314        // the first surfaced arm's error verbatim). Each of the three
5315        // ctors ([`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5316        // [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5317        // [`crate::LayoutError::servico_slots_on_non_servico`]) was
5318        // already lifted onto the substrate by the peer
5319        // [`crate::layout::layout_slot_kind_ctors!`] macro, so each arm
5320        // routes through the same substrate-canonical
5321        // `Self::<variant> { caixa, kind, slots }` wrap per arm as the
5322        // pre-lift open-coded blocks — byte-equal, pinned by the
5323        // paired `validate_kind_slot_coherence_folds_<family>_arm_matches_gate`
5324        // equivalence pins and the peer
5325        // `validate_kind_slot_coherence_{mesh,supervisor}_arm_fires_before_<next>_arm`
5326        // ordering pins.
5327        self.run_kind_owned_slot_family_gate(
5328            crate::CaixaKind::is_aplicacao,
5329            Caixa::declared_mesh_slots,
5330            crate::LayoutError::mesh_slots_on_non_aplicacao,
5331        )?;
5332        self.run_kind_owned_slot_family_gate(
5333            crate::CaixaKind::is_supervisor,
5334            Caixa::declared_supervisor_slots,
5335            crate::LayoutError::supervisor_slots_on_non_supervisor,
5336        )?;
5337        self.run_kind_owned_slot_family_gate(
5338            crate::CaixaKind::is_servico,
5339            Caixa::declared_servico_slots,
5340            crate::LayoutError::servico_slots_on_non_servico,
5341        )?;
5342        Ok(())
5343    }
5344
5345    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on
5346    /// the three no-code kinds — `Supervisor` (supervises other
5347    /// caixas, INSPIRATIONS §II.2), `Aplicacao` (composes Servicos,
5348    /// MESH-COMPOSITION §III.1), and `Acao` (owns a typed CI run,
5349    /// CANTEIRO §7.1-C). Each carries no code of its own, so
5350    /// declaring any of `:bibliotecas` / `:exe` / `:servicos`
5351    /// silently passes the layout's path-existence loops (the paths
5352    /// still resolve on disk) and then vanishes downstream — the
5353    /// per-kind renderers gate emission on
5354    /// [`crate::render::require_kind`] and only emit the code
5355    /// surface for its owning kind, so a declared code slot on a
5356    /// no-code kind is the manifest field's documented "ignored
5357    /// otherwise" footgun.
5358    ///
5359    /// Pre-lift each of the three arms lived as a self-similar
5360    /// `if !caixa.kind().is_<no-code-kind>() { … } else if has_code
5361    /// { return Err(LayoutError::<kind>_owns_code(caixa)); }` block
5362    /// at [`crate::layout::StandardLayout::verify`] — three
5363    /// consumers, three identical shapes. Every future consumer
5364    /// that wanted to gate the whole code-surface coherence cascade
5365    /// as a unit (the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
5366    /// materializer's admission webhook re-checking after a
5367    /// per-slot patch, a future `feira validate --no-code-kind`
5368    /// per-caixa admission verb, a per-`Caixa` overlay resolver
5369    /// rejecting a kind-foreign patch) was structurally forced to
5370    /// either re-inline the three-block cascade in lockstep with
5371    /// the layout wire-up (the duplication the PRIME DIRECTIVE
5372    /// names as a bug) or call the whole
5373    /// [`crate::layout::StandardLayout::verify`] pipeline. Post-fold
5374    /// each such consumer reaches the three-arm cascade through
5375    /// one call.
5376    ///
5377    /// Mirror of the sibling [`Self::validate_kind_slot_coherence`]
5378    /// fold (f0d286e) on the author-time typed-slot coherence axis:
5379    /// that gate closes the "non-owner kind declares owner-only
5380    /// typed slots" three-arm cascade on the M2 / supervisor-tree /
5381    /// M3 slot families; this gate closes the reciprocal
5382    /// "no-code kind declares code" three-arm cascade on the
5383    /// `:bibliotecas` / `:exe` / `:servicos` code surface. Together
5384    /// the two folds route every kind ↔ author-shape coherence
5385    /// diagnostic at the layout altitude through one substrate
5386    /// primitive per axis.
5387    ///
5388    /// The gate carries two identity elements:
5389    /// - **`has_code == false`** — any kind (including the three
5390    ///   no-code kinds) that declares no code passes the paired
5391    ///   `!has_code` short-circuit before every per-arm dispatch.
5392    /// - **Code-owning kinds** (`Biblioteca` owning
5393    ///   `:bibliotecas`, `Binario` owning `:exe`, `Servico` owning
5394    ///   `:servicos`) — the three no-code arm-firing predicates
5395    ///   short-circuit on every code-owning kind, so the gate
5396    ///   passes trivially regardless of what code they declare.
5397    ///   Foreign-code-slot violations on a code-owning kind (e.g.
5398    ///   `:kind Servico` declaring `:exe`) surface through the
5399    ///   sibling [`crate::LayoutError::ForeignCodeSlot`] gate on
5400    ///   [`Self::declared_foreign_code_slots`], not through this
5401    ///   gate.
5402    ///
5403    /// Unlike the sibling cross-family
5404    /// [`Self::validate_kind_slot_coherence`], the three arms of
5405    /// this fold are mutually exclusive by construction — `:kind`
5406    /// is a single-valued [`CaixaKind`] discriminator so at most
5407    /// one arm can fire per caixa — and no cross-arm ordering pin
5408    /// is meaningful (the pre-fold three-block cascade at the
5409    /// wire-up site was already unreachable past the first
5410    /// matching arm).
5411    ///
5412    /// Peer to the per-kind compound entry gates every substrate
5413    /// primitive on the M2/M3 typed-slot family already carries
5414    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5415    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5416    /// [`Self::validate_upgrade_from`] d6801df,
5417    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5418    /// [`Self::validate_supervisor_shape`] 4c70105,
5419    /// [`Self::validate_acao_shape`] 5d6df54,
5420    /// [`Self::validate_kind_slot_coherence`] f0d286e): the
5421    /// author-time gate axis on the *per-slot* and *cross-family
5422    /// typed-slot* algebras each share one substrate primitive per
5423    /// compound gate, and this lift closes the third axis on the
5424    /// *code-surface* algebra so the layout pipeline routes all
5425    /// three coherence axes through one substrate primitive rather
5426    /// than nine open-coded blocks. Every future no-code kind
5427    /// (an `Actor` virtual-actor arm the M5 Orleans-inspired kind
5428    /// reaches through if it lands as a no-code composer, a future
5429    /// `Namespace` grouping kind) folds onto this compound gate
5430    /// as one arm addition rather than a fourth open-coded block
5431    /// at the wire-up site.
5432    ///
5433    /// # Errors
5434    ///
5435    /// Returns the [`crate::LayoutError`] variant naming the
5436    /// offending no-code kind:
5437    /// [`crate::LayoutError::SupervisorOwnsCode`] on a `:kind
5438    /// Supervisor` caixa with any declared code,
5439    /// [`crate::LayoutError::AplicacaoOwnsCode`] on a `:kind
5440    /// Aplicacao` caixa with any declared code,
5441    /// [`crate::LayoutError::AcaoOwnsCode`] on a `:kind Acao` caixa
5442    /// with any declared code. Passes trivially on every kind with
5443    /// no declared code and on every code-owning kind regardless
5444    /// of declared code (the fold's two identity-element arms).
5445    pub fn validate_no_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5446        let has_code =
5447            !self.bibliotecas().is_empty() || !self.exe().is_empty() || !self.servicos().is_empty();
5448        if !has_code {
5449            return Ok(());
5450        }
5451        if self.kind().is_supervisor() {
5452            return Err(crate::LayoutError::supervisor_owns_code(self));
5453        }
5454        if self.kind().is_aplicacao() {
5455            return Err(crate::LayoutError::aplicacao_owns_code(self));
5456        }
5457        if self.kind().is_acao() {
5458            return Err(crate::LayoutError::acao_owns_code(self));
5459        }
5460        Ok(())
5461    }
5462
5463    /// Compound per-`Caixa` kind ↔ `:ci` coherence gate — the `Acao`
5464    /// axis-only companion to the sibling three-arm
5465    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) on the
5466    /// M3 mesh / supervisor-tree / M2 Servico-runtime typed-slot
5467    /// families. `:ci` carries a typed CI run
5468    /// ([`canteiro_types::CiRun`], CANTEIRO §7.1-C) that only the
5469    /// `caixa-actions` renderer decomposes + validates and only for a
5470    /// `:kind Acao`. On any *other* kind a declared `:ci` is the
5471    /// manifest field's documented "ignored otherwise" — it silently
5472    /// passes verify and then vanishes (never decomposed, never
5473    /// rendered), far from the source `caixa.lisp`.
5474    ///
5475    /// Pre-lift the arm lived as a self-similar
5476    /// `if caixa.ci().is_some() && !caixa.kind().is_acao() { return
5477    /// Err(LayoutError::CiOnNonAcao { caixa: caixa.nome().to_string(),
5478    /// kind: caixa.kind() }); }` block at
5479    /// [`crate::layout::StandardLayout::verify`] — one consumer today
5480    /// but every future consumer that wanted to gate the `:ci`
5481    /// coherence axis as a unit (the deferred
5482    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5483    /// webhook re-checking after a per-slot patch, a future
5484    /// `feira validate --ci-coherence` per-caixa admission verb, a
5485    /// per-`Caixa` overlay resolver rejecting a kind-foreign `:ci`
5486    /// patch) was structurally forced to either re-inline the
5487    /// two-condition guard in lockstep with the layout wire-up (the
5488    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5489    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5490    /// Post-fold each such consumer reaches the arm through one call.
5491    ///
5492    /// Peer of the sibling three-arm
5493    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5494    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5495    /// axes under a uniform `{ caixa, kind, slots }` envelope
5496    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5497    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5498    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]). The `:ci`
5499    /// axis stays on its own primitive because
5500    /// [`crate::LayoutError::CiOnNonAcao`] carries a distinct
5501    /// `{ caixa, kind }` wrap shape (no `slots` field — `:ci` is a
5502    /// single `Option` not a `Vec`-of-named-slots) whose reshape
5503    /// onto the sibling `{ caixa, kind, slots }` envelope would
5504    /// force a variant rename touching every consumer of
5505    /// `CiOnNonAcao`; the two folds share the same
5506    /// author-time-vs-renderer split and diagnostic altitude, and
5507    /// route through peer substrate primitives on the same
5508    /// [`Caixa`] surface.
5509    ///
5510    /// Peer to the per-kind compound entry gates every substrate
5511    /// primitive on the M2/M3 typed-slot family already carries
5512    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5513    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5514    /// [`Self::validate_upgrade_from`] d6801df,
5515    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5516    /// [`Self::validate_supervisor_shape`] 4c70105,
5517    /// [`Self::validate_acao_shape`] 5d6df54,
5518    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5519    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2): every
5520    /// author-time coherence axis on the typed [`Caixa`] surface now
5521    /// routes through one substrate primitive per axis rather than
5522    /// an open-coded block at the layout wire-up site.
5523    ///
5524    /// The gate carries two identity elements:
5525    /// - **`ci().is_none()`** — a caixa that declares no `:ci`
5526    ///   passes the first short-circuit before every per-arm
5527    ///   dispatch, on every kind. The canonical shape of the four
5528    ///   non-`Acao` kinds (`Biblioteca` / `Binario` / `Servico` /
5529    ///   `Supervisor` / `Aplicacao`) is `ci = None` — the arm
5530    ///   never fires on a well-shaped fixture.
5531    /// - **`:kind Acao`** — the owner-kind arm short-circuits on
5532    ///   every `Acao` caixa regardless of its `:ci` shape; a
5533    ///   malformed `:ci` on an `Acao` surfaces through the peer
5534    ///   [`Self::validate_acao_shape`] compound decompose gate
5535    ///   (5d6df54), not through this coherence gate.
5536    ///
5537    /// # Errors
5538    ///
5539    /// Returns [`crate::LayoutError::CiOnNonAcao`] naming the
5540    /// offending caixa's nome + kind on any non-`Acao` caixa with
5541    /// `:ci` declared. Passes trivially on every kind that declares
5542    /// no `:ci` and on every `:kind Acao` caixa regardless of
5543    /// declared `:ci` (the fold's two identity-element arms).
5544    pub fn validate_ci_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5545        if self.ci().is_some() && !self.kind().is_acao() {
5546            return Err(crate::LayoutError::CiOnNonAcao {
5547                caixa: self.nome().to_string(),
5548                kind: self.kind(),
5549            });
5550        }
5551        Ok(())
5552    }
5553
5554    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on the
5555    /// two exclusive code-surface slots — `:exe` (owned only by
5556    /// [`crate::CaixaKind::Binario`], the nix-built executable surface)
5557    /// and `:servicos` (owned only by [`crate::CaixaKind::Servico`],
5558    /// the wasm-component + `ComputeUnit` daemon surface). The
5559    /// `caixa-helm` / `caixa-flux` / `caixa-flake` renderers gate
5560    /// emission on [`crate::render::require_kind`]`(_, <owning-kind>)`
5561    /// and only emit the slot for its owning kind — so on any *other*
5562    /// code-running kind a declared `:exe` / `:servicos` is the
5563    /// manifest field's documented "ignored otherwise": the path is
5564    /// validated by the per-kind path-existence loops in
5565    /// [`crate::layout::StandardLayout::verify`], but the value is
5566    /// never rendered into a build target or programs.yaml entry —
5567    /// it silently passes `feira build` and then vanishes, far from
5568    /// the source `caixa.lisp`, with no field naming which slot is
5569    /// foreign.
5570    ///
5571    /// Pre-lift the arm lived as a self-similar four-line `let
5572    /// foreign_code_slots = caixa.declared_foreign_code_slots(); if
5573    /// !foreign_code_slots.is_empty() { return
5574    /// Err(LayoutError::foreign_code_slot(caixa, foreign_code_slots));
5575    /// }` block at [`crate::layout::StandardLayout::verify`] — one
5576    /// consumer today but every future consumer that wanted to gate
5577    /// the code-surface coherence axis as a unit (the deferred
5578    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5579    /// webhook re-checking after a per-slot patch, a future
5580    /// `feira validate --foreign-code` per-caixa admission verb, a
5581    /// per-`Caixa` overlay resolver rejecting a kind-foreign code-
5582    /// slot patch) was structurally forced to either re-inline the
5583    /// two-condition guard in lockstep with the layout wire-up (the
5584    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5585    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5586    /// Post-fold each such consumer reaches the arm through one call.
5587    ///
5588    /// Peer of the sibling three-arm
5589    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5590    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5591    /// axes under the uniform `{ caixa, kind, slots }` envelope
5592    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5593    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5594    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]); this gate
5595    /// carries the code-surface axis under the same
5596    /// `{ caixa, kind, slots }` envelope
5597    /// ([`crate::LayoutError::ForeignCodeSlot`]). The two folds share
5598    /// the envelope shape but stay separate primitives because the
5599    /// per-arm predicate differs: the cross-family fold rides on the
5600    /// outer `!self.kind().is_<owner>()` guard *paired* with a
5601    /// per-family `declared_<family>_slots` accumulator, while this
5602    /// fold's per-arm kind-check is baked into
5603    /// [`Self::declared_foreign_code_slots`] itself (each arm's
5604    /// `!self.kind().requires_<slot>()` guard fires inside the
5605    /// accumulator, not around it) — so a `:kind Binario` declaring
5606    /// `:servicos` and a `:kind Servico` declaring `:exe` are both
5607    /// caught by one accumulator sweep rather than by two independent
5608    /// arm dispatches. Peer with [`Self::validate_ci_kind_coherence`]
5609    /// (9b55beb) which carries the `:ci` axis on its own primitive
5610    /// for the same "distinct per-arm predicate shape, shared
5611    /// diagnostic altitude" reason.
5612    ///
5613    /// Peer to the per-kind and per-slot compound entry gates every
5614    /// substrate primitive on the M2/M3 typed-slot family already
5615    /// carries ([`Self::validate_deps`] b5dd55e,
5616    /// [`Self::validate_limits`] baa4688,
5617    /// [`Self::validate_behavior`] 0d2877a,
5618    /// [`Self::validate_upgrade_from`] d6801df,
5619    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5620    /// [`Self::validate_supervisor_shape`] 4c70105,
5621    /// [`Self::validate_acao_shape`] 5d6df54,
5622    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5623    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5624    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5625    /// author-time coherence axis on the typed [`Caixa`] surface now
5626    /// routes through one substrate primitive per axis rather than an
5627    /// open-coded block at the layout wire-up site. This closes the
5628    /// last open-coded kind ↔ slot coherence gate at the layout
5629    /// altitude — every kind-coherence diagnostic is now a substrate
5630    /// primitive.
5631    ///
5632    /// The gate carries three identity elements:
5633    /// - **Code-owning kinds on their native slot** — a
5634    ///   [`crate::CaixaKind::Binario`] declaring `:exe`, a
5635    ///   [`crate::CaixaKind::Servico`] declaring `:servicos` — each
5636    ///   arm's `!requires_<slot>()` predicate short-circuits inside
5637    ///   [`Self::declared_foreign_code_slots`], so the accumulator
5638    ///   returns an empty `Vec` and the outer `is_empty` short-
5639    ///   circuits before the wrap fires.
5640    /// - **Bare caixas** — a caixa with no declared code on any kind
5641    ///   passes the same accumulator's `is_empty` short-circuit on
5642    ///   every arm.
5643    /// - **No-code kinds** ([`crate::CaixaKind::Supervisor`] /
5644    ///   [`crate::CaixaKind::Aplicacao`] / [`crate::CaixaKind::Acao`])
5645    ///   declaring code — dominated upstream by the sibling
5646    ///   [`Self::validate_no_code_kind_coherence`] (3bbf6a2) which
5647    ///   surfaces [`crate::LayoutError::SupervisorOwnsCode`] /
5648    ///   [`crate::LayoutError::AplicacaoOwnsCode`] /
5649    ///   [`crate::LayoutError::AcaoOwnsCode`] first at the layout
5650    ///   wire-up site, so this gate never fires on a no-code kind
5651    ///   through the layout pipeline. A standalone caller reaching
5652    ///   this primitive without the sibling `_no_code_` gate first
5653    ///   would see a no-code kind's declared `:exe` / `:servicos`
5654    ///   surface `ForeignCodeSlot` here (the two folds partition the
5655    ///   diagnostic responsibility along the "declared no-code slot"
5656    ///   axis: no-code kinds get `OwnsCode`, code-running kinds get
5657    ///   `ForeignCodeSlot`), and the layout wire-up's canonical
5658    ///   `_no_code_` → `_foreign_code_` ordering keeps the
5659    ///   [`crate::LayoutError::SupervisorOwnsCode`] / … arm the one
5660    ///   that surfaces in the composed pipeline.
5661    ///
5662    /// Diagnostic order within the arm matches the pre-fold layout
5663    /// wire-up canonical sequence — `:exe` → `:servicos` — pinned by
5664    /// [`Self::declared_foreign_code_slots`]'s per-arm push order.
5665    ///
5666    /// # Errors
5667    ///
5668    /// Returns [`crate::LayoutError::ForeignCodeSlot`] naming the
5669    /// offending caixa's nome + kind + declared foreign-code slot
5670    /// list on any code-running kind ([`crate::CaixaKind::Biblioteca`]
5671    /// / [`crate::CaixaKind::Binario`] / [`crate::CaixaKind::Servico`])
5672    /// declaring another code-running kind's exclusive code surface.
5673    /// Passes trivially on every native-slot declaration (Binario
5674    /// with `:exe`, Servico with `:servicos`), on every bare caixa,
5675    /// and on every no-code kind (dominated upstream by the sibling
5676    /// [`Self::validate_no_code_kind_coherence`] `OwnsCode` gates —
5677    /// see the identity-element notes above).
5678    pub fn validate_foreign_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5679        let foreign_code_slots = self.declared_foreign_code_slots();
5680        if !foreign_code_slots.is_empty() {
5681            return Err(crate::LayoutError::foreign_code_slot(
5682                self,
5683                foreign_code_slots,
5684            ));
5685        }
5686        Ok(())
5687    }
5688
5689    /// Compound per-`Caixa` required-slot gate on the three
5690    /// [`crate::CaixaKind`] arms whose sole payload is a canonical
5691    /// typed slot: `Binario`'s `:exe`, `Servico`'s `:servicos`,
5692    /// `Acao`'s `:ci`. Each arm refuses a caixa on its owner kind
5693    /// that declares no value in the corresponding required slot,
5694    /// so `feira build` (the canonical author-time gate) surfaces the
5695    /// self-locating "this kind needs this slot" diagnostic at the
5696    /// source `caixa.lisp` rather than deferring the failure to a
5697    /// downstream consumer (a nix build with no `:exe` to build, a
5698    /// programs.yaml fan-out with no `:servicos` to enumerate, a
5699    /// `caixa-actions` decompose with no `:ci` to walk).
5700    ///
5701    /// Pre-lift each of the three arms lived as a self-similar
5702    /// `if caixa.kind().requires_<slot>() && caixa.<slot>().is_<empty>() {
5703    /// return Err(LayoutError::<kind>_without_<slot>(caixa)); }`
5704    /// block at [`crate::layout::StandardLayout::verify`] — three
5705    /// consumers, three identical shapes, one substrate primitive on
5706    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
5707    /// a bug. Each of the three inner ctors
5708    /// ([`crate::LayoutError::binario_without_exe`] /
5709    /// [`crate::LayoutError::servico_without_servicos`] /
5710    /// [`crate::LayoutError::missing_ci`]) was already lifted onto
5711    /// the substrate by the peer [`crate::layout::layout_nome_only_ctors!`]
5712    /// macro, so the primitive routes through the same
5713    /// `Self::<variant>(caixa.nome().to_string())` tuple-literal
5714    /// wrap per arm as the pre-lift open-coded blocks.
5715    ///
5716    /// The paired `Biblioteca`-arm required-slot check
5717    /// ([`crate::LayoutError::MissingLib`]) stays open-coded at the
5718    /// layout wire-up site by design: it needs the filesystem oracle
5719    /// on [`crate::layout::LayoutInvariants`] to check the default
5720    /// `lib/<nome>.lisp` fallback path, which the pure per-`Caixa`
5721    /// typed-shape surface this fold rides on has no reference to.
5722    /// Same posture the peer [`Self::validate_no_code_kind_coherence`]
5723    /// fold takes on the on-disk existence loops.
5724    ///
5725    /// Diagnostic order at the primitive matches the pre-fold layout
5726    /// wire-up canonical sequence — `:exe` → `:servicos` → `:ci` —
5727    /// the same three-arm sweep the peer [`crate::CaixaKind`]
5728    /// discriminator carries at its `requires_*` accessors. Unlike
5729    /// the sibling cross-family [`Self::validate_kind_slot_coherence`]
5730    /// fold, the three arms of this fold are mutually exclusive by
5731    /// construction — `:kind` is a single-valued [`crate::CaixaKind`]
5732    /// discriminator so at most one arm can fire per caixa — and no
5733    /// cross-arm ordering pin is meaningful (the pre-fold three-block
5734    /// cascade at the wire-up site was already unreachable past the
5735    /// first matching arm).
5736    ///
5737    /// Peer to the per-kind and per-slot compound entry gates every
5738    /// substrate primitive on the M2/M3 typed-slot family already
5739    /// carries ([`Self::validate_deps`] b5dd55e,
5740    /// [`Self::validate_limits`] baa4688,
5741    /// [`Self::validate_behavior`] 0d2877a,
5742    /// [`Self::validate_upgrade_from`] d6801df,
5743    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5744    /// [`Self::validate_supervisor_shape`] 4c70105,
5745    /// [`Self::validate_acao_shape`] 5d6df54,
5746    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5747    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5748    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5749    /// author-time coherence axis on the typed [`Caixa`] surface
5750    /// now routes through one substrate primitive per axis rather
5751    /// than an open-coded block at the layout wire-up site.
5752    ///
5753    /// The gate carries two identity elements:
5754    /// - **Non-owner kinds** — each per-arm predicate is
5755    ///   `self.kind().requires_<slot>()`, which returns `true` only
5756    ///   for the owning kind ([`crate::CaixaKind::Binario`] on `:exe`,
5757    ///   [`crate::CaixaKind::Servico`] on `:servicos`,
5758    ///   [`crate::CaixaKind::Acao`] on `:ci`). Every non-owner kind
5759    ///   passes each per-arm dispatch trivially.
5760    /// - **Owner kinds with the required slot present** — a
5761    ///   [`crate::CaixaKind::Binario`] with a non-empty `:exe`, a
5762    ///   [`crate::CaixaKind::Servico`] with a non-empty `:servicos`,
5763    ///   an [`crate::CaixaKind::Acao`] with `ci = Some(_)` — passes
5764    ///   its arm's `is_empty` / `is_none` short-circuit.
5765    ///
5766    /// # Errors
5767    ///
5768    /// Returns the [`crate::LayoutError`] variant naming the
5769    /// offending owner kind:
5770    /// [`crate::LayoutError::BinarioWithoutExe`] on a
5771    /// [`crate::CaixaKind::Binario`] caixa with no declared `:exe`,
5772    /// [`crate::LayoutError::ServicoWithoutServicos`] on a
5773    /// [`crate::CaixaKind::Servico`] caixa with no declared
5774    /// `:servicos`, [`crate::LayoutError::MissingCi`] on a
5775    /// [`crate::CaixaKind::Acao`] caixa with no declared `:ci`.
5776    /// Passes trivially on every non-owner kind and on every owner
5777    /// kind with its required slot present.
5778    pub fn validate_required_kind_slot(&self) -> Result<(), crate::LayoutError> {
5779        if self.kind().requires_exe() && self.exe().is_empty() {
5780            return Err(crate::LayoutError::binario_without_exe(self));
5781        }
5782        if self.kind().requires_servicos() && self.servicos().is_empty() {
5783            return Err(crate::LayoutError::servico_without_servicos(self));
5784        }
5785        if self.kind().requires_ci() && self.ci().is_none() {
5786            return Err(crate::LayoutError::missing_ci(self));
5787        }
5788        Ok(())
5789    }
5790
5791    /// Reject per-entry values on the three Caixa-level code-surface
5792    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
5793    /// layout checker's `root.join(p)` sandbox would silently subvert.
5794    /// Same three structural footguns the peer
5795    /// [`BehaviorSpec::validate`] (b0c8389) and
5796    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
5797    /// (26da2c7) already close on the M2 `:behavior :on-*` and
5798    /// `:upgrade-from :state-change :script` axes, here lifted onto
5799    /// the three top-level code-path axes through the shared
5800    /// [`is_sandboxed_relative_path`] predicate:
5801    ///
5802    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
5803    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
5804    ///     [`Path::join`] as the base itself — `root.join("")` ==
5805    ///     `root`, so the existence check (`self.exists(&root)`)
5806    ///     trivially passes (the project root exists), and the layout
5807    ///     silently treats the project root as a biblioteca / exe /
5808    ///     servico entry. The `:bibliotecas` loop then hands the root
5809    ///     to `tatara_lisp::read` at `feira build` time as if the root
5810    ///     directory itself were a Lisp source file — a parse error
5811    ///     far from the source `caixa.lisp` with no field naming the
5812    ///     offending entry.
5813    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
5814    ///     [`Path::join`] *replaces* the base when the right-hand side
5815    ///     is absolute, so `root.join("/etc/passwd")` resolves to
5816    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
5817    ///     The existence check then silently consults whatever the
5818    ///     escaped path resolves to — for `:bibliotecas`, the layout
5819    ///     has no `starts_with`-fence (only `:exe` is fenced under
5820    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
5821    ///     `:bibliotecas` entry that happens to resolve on disk
5822    ///     silently passes. For `:exe` / `:servicos` the fence catches
5823    ///     the absolute case downstream as `ExeOutsideDir` /
5824    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
5825    ///     doesn't exist), but with a downstream-shaped diagnostic
5826    ///     that names the resolved escape path rather than the
5827    ///     authoring footgun at the source.
5828    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
5829    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
5830    ///     [`std::path::Component::ParentDir`] anywhere round-trips
5831    ///     through [`Path::join`] as a traversal above the caixa root.
5832    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
5833    ///     *component-aware* (not canonical-path-aware), so
5834    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
5835    ///     is **true** even though the canonical resolution
5836    ///     `{parent of root}/escape.lisp` lives outside the caixa root
5837    ///     — the fence silently lets the parent-escape through, and
5838    ///     the existence check passes if that escape-target happens
5839    ///     to exist. Caught regardless of where the `..` sits
5840    ///     (leading, mid-path, trailing) so the gate matches the peer
5841    ///     predicate's full coverage.
5842    ///
5843    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
5844    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
5845    /// same per-slot diagnostic shape every peer per-axis path-gate
5846    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
5847    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
5848    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
5849    /// order [`Caixa::declared_foreign_code_slots`] uses for its
5850    /// canonical foreign-code-slot diagnostic, so a manifest with
5851    /// multiple malformed slots surfaces the lexicographically-earliest
5852    /// slot's diagnostic deterministically.
5853    ///
5854    /// Lifted to the typed surface as a Caixa-level validator (peer
5855    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
5856    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
5857    /// and wired into [`crate::StandardLayout::verify`] before the
5858    /// existence-check loops so the diagnostic names the offending
5859    /// slot at the source caixa.lisp rather than reporting a
5860    /// downstream `MissingEntry` / `ExeOutsideDir` /
5861    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
5862    /// The fourth typed code-path surface — every author-supplied
5863    /// path on the manifest — is now structurally accept-shaped
5864    /// past validate, peer with `:behavior :on-*` and
5865    /// `:upgrade-from :state-change :script`.
5866    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
5867        /// Per-slot file-type contract for the three Caixa-level
5868        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
5869        /// Each variant names the predicate the per-entry file-type
5870        /// gate consults; [`Self::None`] opts the slot out of any
5871        /// file-type contract. Lifted as a typed local enum so the
5872        /// per-slot dispatch is exhaustive at the `match` — adding a
5873        /// future axis to the typed-substrate `:` slot set (the
5874        /// future `:assets` resource axis the M5 roadmap names, the
5875        /// future `:nix-flake` derivation axis the caixa-flake
5876        /// emitter consults) lands as one variant + one `match` arm,
5877        /// not a coordinated rewrite of every per-slot bool flag.
5878        ///
5879        /// Peer of the typed-substrate per-slot variant disciplines
5880        /// already established on this surface
5881        /// ([`crate::supervisor::RestartStrategy`] +
5882        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
5883        /// supervision-tree axis,
5884        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
5885        /// placement axis, [`crate::aplicacao::WitTarget`] on the
5886        /// `:contratos` payload-target axis): the typed `enum` is
5887        /// the substrate's single source of truth for the per-axis
5888        /// dispatch, and every consumer (the per-arm body here, the
5889        /// future feira-lint per-slot diagnostic renderer, the M4
5890        /// per-axis admission webhook) reaches for the same typed
5891        /// surface rather than re-deriving the partition from inline
5892        /// flag combinations.
5893        enum CodePathFileType {
5894            /// `:exe` — nix-build derivation output, no terminating-
5895            /// extension contract (the canonical `"exe/<name>"`
5896            /// fixtures the layout's `ExeOutsideDir` error message
5897            /// documents carry no extension by convention).
5898            None,
5899            /// `:bibliotecas` — tatara-lisp source files the
5900            /// `feira build` loop reads through `tatara_lisp::read`
5901            /// at parse time. Routes to [`is_lisp_extension`].
5902            LispSource,
5903            /// `:servicos` — ComputeUnit-CR YAML files the
5904            /// caixa-helm / caixa-flux renderers consume through
5905            /// `serde_yaml::from_str`. Routes to
5906            /// [`is_computeunit_yaml_extension`].
5907            ComputeUnitYaml,
5908        }
5909
5910        // The per-slot [`CodePathFileType`] selects which axes carry the
5911        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
5912        // source axis (the `feira build` loop at
5913        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
5914        // `tatara_lisp::read` at parse time) — the lifted
5915        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
5916        // `:exe` is the nix-built executable surface (per the canonical
5917        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
5918        // error message documents and every in-tree
5919        // `caixa_with_code_paths` positive control uses) — its file-type
5920        // contract is "nix-build derivation output", not a typed source
5921        // file, so [`CodePathFileType::None`] opts the slot out of any
5922        // file-type gate. `:servicos` is the `.computeunit.yaml`
5923        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
5924        // renderers consume each entry through `serde_yaml::from_str` as
5925        // a typed `ComputeUnit` CR) — the lifted
5926        // [`is_computeunit_yaml_extension`] predicate gates the compound
5927        // `.computeunit.yaml` suffix. All three axes are surfaced through
5928        // the same iteration so the sandbox-shape + duplicate gates
5929        // apply uniformly; the typed file-type dispatch fires per-slot
5930        // exactly where the downstream consumer's accepted set demands
5931        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
5932        // compounding lift on the peer 64772a9 `:bibliotecas`
5933        // `.lisp`-gate trajectory — the second of the three code-path
5934        // axes to land on a typed compound-suffix gate, with the same
5935        // self-locating per-slot diagnostic shape every peer per-axis
5936        // file-type lift uses (`*NonLispExtension { slot, path }` /
5937        // `*NonComputeUnitYamlExtension { slot, path }`).
5938        for (slot, list, file_type) in [
5939            (
5940                ":bibliotecas",
5941                &self.bibliotecas,
5942                CodePathFileType::LispSource,
5943            ),
5944            (":exe", &self.exe, CodePathFileType::None),
5945            (
5946                ":servicos",
5947                &self.servicos,
5948                CodePathFileType::ComputeUnitYaml,
5949            ),
5950        ] {
5951            // Per-slot set-not-multiset gate on the typed code-path axis.
5952            // Every peer Vec-shaped author-supplied list past validate is
5953            // a set, not a multiset: `:membros :caixa`
5954            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
5955            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
5956            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
5957            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
5958            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
5959            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
5960            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
5961            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
5962            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
5963            // the three code-path lists are the last Vec-shaped author-
5964            // supplied slots on the typed Caixa surface still admitting a
5965            // duplicate entry silently. Scope is per-list (`:bibliotecas`
5966            // duplicates are flagged within `:bibliotecas`, not across
5967            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
5968            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
5969            // legitimate dev-vs-runtime shape on the dep axis, fenced
5970            // separately by [`crate::dep::validate_no_self_dep`]). On the
5971            // code-path axis a cross-slot collision is structurally
5972            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
5973            // fence — `:exe` and `:servicos` entries are confined to their
5974            // own directory trees, so the only way a string could appear
5975            // on two code-path lists is the (rare, structurally invalid)
5976            // case where `:bibliotecas` carries an `"exe/<x>"` or
5977            // `"servicos/<x>.yaml"`-shaped path.
5978            //
5979            // Without the gate three authoring footguns silently passed:
5980            //
5981            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
5982            //     canonical copy-paste-the-wrong-file footgun. `feira
5983            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
5984            //     list and re-parses the same file twice, wasting work
5985            //     and silently masking the author's intent to declare a
5986            //     *second* biblioteca.
5987            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
5988            //     Binario surface. The future `caixa-flake` `nix flake`
5989            //     emitter that materializes each `:exe` entry as a flake
5990            //     `packages.<exe-name>` derivation would collide on the
5991            //     duplicate package name and surface a flake-eval error
5992            //     far from the source `caixa.lisp`.
5993            //   - `:servicos ("servicos/x.computeunit.yaml"
5994            //     "servicos/x.computeunit.yaml")` — the same footgun on
5995            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
5996            //     renderers already refuse `:servicos.len() != 1` with
5997            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
5998            //     that diagnostic surfaces "too many servicos" without
5999            //     naming "duplicate entry" — the typed self-locating
6000            //     "which entry is the duplicate" framing only lands at
6001            //     this gate.
6002            //
6003            // Same `seen.insert(entry.as_str())` shape every peer per-list
6004            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
6005            // 86c769b, `:deps` 359fba5) and the same "structural shape
6006            // checks fire before the duplicate check on the same entry"
6007            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
6008            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
6009            // empty entry first, not the duplicate on the later pair).
6010            let mut seen = std::collections::HashSet::new();
6011            for entry in list {
6012                let path = Path::new(entry);
6013                match is_sandboxed_relative_path(path) {
6014                    Ok(()) => {}
6015                    Err(PathShapeViolation::Empty) => {
6016                        return Err(ManifestError::CodePathEmpty { slot });
6017                    }
6018                    Err(PathShapeViolation::Absolute) => {
6019                        return Err(ManifestError::code_path_absolute(slot, path));
6020                    }
6021                    Err(PathShapeViolation::ParentEscape) => {
6022                        return Err(ManifestError::code_path_parent_escape(slot, path));
6023                    }
6024                }
6025                // The per-slot file-type gate dispatched through the
6026                // typed [`CodePathFileType`] selector above. Each variant
6027                // routes to the lifted predicate the downstream consumer
6028                // demands:
6029                //
6030                //   - [`LispSource`] → [`is_lisp_extension`] for
6031                //     `:bibliotecas` (the `feira build` loop's
6032                //     `tatara_lisp::read` consumer);
6033                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
6034                //     for `:servicos` (the caixa-helm / caixa-flux
6035                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
6036                //     accepted set);
6037                //   - [`None`] for `:exe` — the nix-build derivation-
6038                //     output axis has no terminating-extension contract.
6039                //
6040                // Fires after the sandbox-shape arms so a path that is
6041                // *both* sandbox-escaping and wrong-extension surfaces
6042                // the more fundamental sandbox-shape diagnostic first
6043                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
6044                // `ParentEscape` → `NonLispExtension` arm-ordering on
6045                // `:behavior :on-*` c97815a, and `EmptyScript` →
6046                // `AbsoluteScript` → `ParentEscapeScript` →
6047                // `NonLispExtensionScript` on
6048                // `:upgrade-from :state-change :script` 33cc830), and
6049                // before the duplicate gate so the narrower per-entry
6050                // file-type shape dominates the cross-entry uniqueness
6051                // diagnostic (a
6052                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
6053                // `:servicos` surfaces
6054                // `CodePathNonComputeUnitYamlExtension` on the first
6055                // entry rather than `CodePathDuplicate` on the pair —
6056                // peer with the 64772a9 `:bibliotecas`
6057                // `("lib/x.txt" "lib/x.txt")` ordering).
6058                match file_type {
6059                    CodePathFileType::None => {}
6060                    CodePathFileType::LispSource => {
6061                        if !is_lisp_extension(path) {
6062                            return Err(ManifestError::code_path_non_lisp_extension(slot, path));
6063                        }
6064                    }
6065                    CodePathFileType::ComputeUnitYaml => {
6066                        if !is_computeunit_yaml_extension(path) {
6067                            return Err(ManifestError::code_path_non_computeunit_yaml_extension(
6068                                slot, path,
6069                            ));
6070                        }
6071                    }
6072                }
6073                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
6074                    ManifestError::code_path_duplicate(slot, path)
6075                })?;
6076            }
6077        }
6078        Ok(())
6079    }
6080
6081    /// Reject `:etiquetas` lists with an empty entry or with two entries
6082    /// agreeing on the same string. `:etiquetas` is the universal
6083    /// registry-search-tag axis on [`Caixa`] (every kind carries the
6084    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6085    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
6086    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
6087    /// a [`std::collections::BTreeSet`] alongside the four substrate-
6088    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
6089    /// Two authoring footguns silently passed validate without this gate:
6090    ///
6091    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
6092    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
6093    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
6094    ///     `chart.metadata.keywords` admits the value without a strict
6095    ///     parser-side gate, but the empty keyword has no operational
6096    ///     meaning — it indexes nothing in the future caixa-registry
6097    ///     search axis and clutters the rendered chart with a no-op tag.
6098    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
6099    ///     copy-paste-the-wrong-tag footgun) silently passed validate
6100    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
6101    ///     at chart render — a "second wins / one silently disappears"
6102    ///     shape divergent from every peer typed-graph set gate
6103    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
6104    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
6105    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6106    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6107    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6108    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
6109    ///     on `:upgrade-from`, the per-instruction-class singularity
6110    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
6111    ///     [`crate::UpgradeError::DuplicateStateChange`] /
6112    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
6113    ///     discipline is uniform: every Vec-shaped author-supplied list
6114    ///     past validate is set-not-multiset, by construction.
6115    ///
6116    /// Past the empty arm the gate enforces the chart-keyword shape
6117    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
6118    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
6119    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
6120    /// continuation. Closes the canonical paste-from-doc footguns the
6121    /// bare empty + duplicate arms left open: paste-from-aligned-doc
6122    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
6123    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
6124    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
6125    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
6126    /// — the author meant three separate list entries), path-separator
6127    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
6128    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
6129    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
6130    /// control bytes that would silently land as malformed search tags
6131    /// in the rendered Chart.yaml `keywords:` array and break the
6132    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
6133    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
6134    /// established on the sibling universal-axis `Vec<String>` surface
6135    /// — the second universal-axis Vec<String> surface to land the
6136    /// empty-first-then-shape-then-duplicate per-entry cascade.
6137    ///
6138    /// Same empty-first cascade discipline every peer per-axis gate
6139    /// uses: the per-entry empty arm fires before the per-entry shape
6140    /// arm fires before the cross-entry duplicate arm, so an
6141    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
6142    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
6143    /// has no value" defect) before either the shape or the duplicate
6144    /// diagnostic. Walks the list in declaration order so the
6145    /// first-collision diagnostic surfaces the lexicographically-
6146    /// earliest offending position, peer with every other duplicate
6147    /// gate on this surface.
6148    ///
6149    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
6150    /// caixa-build gate alongside the peer universal gates
6151    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6152    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
6153    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6154    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6155    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6156    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6157    /// slot sets. The future caixa-registry search axis can reach for
6158    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
6159    /// chart-keyword-shaped string without re-deriving the precondition.
6160    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
6161        let mut seen = std::collections::HashSet::new();
6162        for etiqueta in self.etiquetas() {
6163            if etiqueta.is_empty() {
6164                return Err(ManifestError::EtiquetaEmpty);
6165            }
6166            crate::render::is_chart_keyword_shape(etiqueta)
6167                .map_err(|reason| ManifestError::etiqueta_invalid(etiqueta, reason))?;
6168            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
6169                ManifestError::etiqueta_duplicate(etiqueta)
6170            })?;
6171        }
6172        Ok(())
6173    }
6174
6175    /// Reject `:autores` lists with an empty entry or with two entries
6176    /// agreeing on the same string. `:autores` is the universal
6177    /// maintainer-axis on [`Caixa`] (every kind carries the
6178    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6179    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
6180    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
6181    /// to a `Maintainer { name, email: None }` without dedup). Two
6182    /// authoring footguns silently passed validate without this gate:
6183    ///
6184    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
6185    ///     blank-doc footgun) rendered as
6186    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
6187    ///     empty maintainer name has no operational meaning — it
6188    ///     identifies no one in the substrate's authorship index and
6189    ///     clutters the rendered chart with a no-op maintainer.
6190    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
6191    ///     the copy-paste-the-wrong-author footgun) silently passed
6192    ///     validate and rendered as two identical maintainer entries.
6193    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
6194    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
6195    ///     rendered `keywords:` array at chart-render time), the
6196    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
6197    ///     entries stack verbatim in the chart, divergent from every
6198    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
6199    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
6200    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6201    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6202    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6203    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
6204    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
6205    ///     `:etiquetas`).
6206    ///
6207    /// Past the empty arm the gate enforces the chart-maintainer-name
6208    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
6209    /// the structural single-line printable-UTF-8 floor every realistic
6210    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
6211    /// or trailing whitespace, no ASCII control characters anywhere,
6212    /// Unicode bytes accepted. Closes the canonical paste-from-doc
6213    /// footguns the bare empty + duplicate arms left open:
6214    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
6215    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
6216    /// pasted a multi-line block of author records into one `:autores`
6217    /// entry instead of splitting into one entry per author),
6218    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
6219    /// and the paste-from-binary-blob control bytes that would silently
6220    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
6221    /// `maintainers:` array. Mirrors the shape-predicate cascade
6222    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6223    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
6224    /// establish past their own empty arms on the sibling universal-axis
6225    /// `Option<String>` surfaces — the first universal-axis Vec<String>
6226    /// surface to land the empty-first-then-shape-then-duplicate per-entry
6227    /// cascade.
6228    ///
6229    /// Same empty-first cascade discipline every peer per-axis gate
6230    /// uses: the per-entry empty arm fires before the per-entry shape
6231    /// arm before the cross-entry duplicate arm. Walks the list in
6232    /// declaration order so the first-collision diagnostic surfaces the
6233    /// lexicographically-earliest offending position, peer with every
6234    /// other duplicate gate on this surface.
6235    ///
6236    /// Universal-axis (every kind carries `:autores`), so wired at the
6237    /// caixa-build gate alongside the peer universal gates
6238    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6239    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6240    /// [`Self::validate_code_paths`] — before the kind-coherence gates
6241    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6242    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6243    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6244    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6245    /// slot sets.
6246    pub fn validate_autores(&self) -> Result<(), ManifestError> {
6247        let mut seen = std::collections::HashSet::new();
6248        for autor in self.autores() {
6249            if autor.is_empty() {
6250                return Err(ManifestError::AutorEmpty);
6251            }
6252            crate::render::is_chart_maintainer_name_shape(autor)
6253                .map_err(|reason| ManifestError::autor_invalid(autor, reason))?;
6254            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
6255                ManifestError::autor_duplicate(autor)
6256            })?;
6257        }
6258        Ok(())
6259    }
6260
6261    /// Reject `:repositorio` values whose shape the shared
6262    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
6263    /// `repositorio: Option<String>` slot on [`Caixa`] is the
6264    /// universal git-shaped homepage axis every kind carries — the
6265    /// substrate routes the same string through two load-bearing
6266    /// consumers:
6267    ///
6268    ///   - [`caixa-helm`] folds it verbatim into the rendered
6269    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
6270    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
6271    ///     the chart `README.md` `repo = …` interpolation
6272    ///     (`caixa-helm/src/lib.rs:359`).
6273    ///   - [`caixa-flux`] folds it verbatim into the standalone
6274    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
6275    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
6276    ///     `GitRepository.spec.url` the cluster's source-controller
6277    ///     polls — the load-bearing deploy-time axis.
6278    ///
6279    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6280    /// substitute a placeholder when the slot is absent (`None` → the
6281    /// fallback fires); a `Some("")` *skips the fallback* and silently
6282    /// passes the empty string through to `Chart.yaml home: ""` /
6283    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
6284    /// controller both reject the empty URL far from the source
6285    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
6286    /// Similarly a malformed `:repositorio` (whitespace, control char,
6287    /// missing `:` separator, leading `-`) silently lands in the
6288    /// rendered artifacts and breaks at `git clone` / `helm template`
6289    /// / `flux reconcile` time.
6290    ///
6291    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
6292    /// same shared predicate the peer [`crate::DepSource::validate`]
6293    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
6294    /// gate the two `git URL`-shaped surfaces on the typed Caixa
6295    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
6296    /// structurally equivalent: every value past validate is
6297    /// guaranteed-acceptable by the predicate's union of constraints
6298    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
6299    /// control chars, ASCII only, no leading `:`, contains a `:`
6300    /// separator). The predicate accepts every documented authoring
6301    /// shape — `github:org/repo` shorthand, `https://host/path`,
6302    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
6303    /// scp-style SSH, `file:///path` — and refuses the canonical
6304    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
6305    /// injection footguns at validate time. Maps the predicate's
6306    /// `String` reason verbatim into the
6307    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
6308    /// offending value + parser-shaped reason so the diagnostic is
6309    /// self-locating (the author can grep their `caixa.lisp` for
6310    /// `:repositorio "<value>"` and fix it in one edit).
6311    ///
6312    /// `None` (the canonical "omit the slot to express no published
6313    /// homepage" shape) is accepted trivially — the gate is a no-op
6314    /// when the author didn't declare a value. `Some("")` is gated by
6315    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
6316    /// shape predicate is consulted, mirroring the empty-first cascade
6317    /// every peer per-axis identity gate uses
6318    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
6319    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
6320    /// [`crate::DepError::FonteRepoEmpty`] →
6321    /// [`crate::DepError::FonteRepoInvalid`]).
6322    ///
6323    /// Universal-axis (every kind carries `:repositorio`), so wired at
6324    /// the caixa-build gate alongside the peer universal gates
6325    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6326    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6327    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
6328    /// before the kind-coherence gates
6329    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6330    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6331    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6332    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6333    /// specific slot sets.
6334    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
6335        let Some(s) = self.repositorio() else {
6336            return Ok(());
6337        };
6338        if s.is_empty() {
6339            return Err(ManifestError::RepositorioEmpty);
6340        }
6341        is_git_repo_url(s).map_err(|reason| ManifestError::repositorio_invalid(s, reason))
6342    }
6343
6344    /// Reject `:descricao` values that are the empty string. The flat
6345    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
6346    /// free-form-prose homepage axis every kind carries — the
6347    /// substrate routes the same string through two load-bearing
6348    /// consumers in the [`caixa-helm`] renderer:
6349    ///
6350    ///   - `build_chart_yaml` folds it verbatim into the rendered
6351    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
6352    ///     field (`caixa-helm/src/lib.rs:232-235`).
6353    ///   - `build_readme` folds it verbatim into the rendered chart
6354    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
6355    ///
6356    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6357    /// substitute a `caixa.nome`-derived placeholder when the slot is
6358    /// absent (`None` → the fallback fires); a `Some("")` *skips the
6359    /// fallback* and silently passes the empty string through to
6360    /// `Chart.yaml description: ""` / a blank chart `README.md`
6361    /// header. Helm's chart spec requires a non-empty `description:`
6362    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
6363    /// `WARNING [chart.metadata.description]: description is required`),
6364    /// so the empty `Some("")` silently lands in the rendered
6365    /// artifacts and breaks at `helm lint` / `helm install` time far
6366    /// from the source `caixa.lisp`, with no field naming the
6367    /// offending `:descricao`.
6368    ///
6369    /// `None` (the canonical "omit the slot to defer to the renderer's
6370    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
6371    /// the gate is a no-op when the author didn't declare a value.
6372    /// `Some("")` is gated by the narrower
6373    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
6374    /// shape every peer per-axis empty gate uses
6375    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6376    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6377    /// [`ManifestError::RepositorioEmpty`]).
6378    ///
6379    /// Universal-axis (every kind carries `:descricao`), so wired at
6380    /// the caixa-build gate alongside the peer universal gates
6381    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6382    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6383    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6384    /// [`Self::validate_code_paths`] — before the kind-coherence
6385    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6386    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6387    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6388    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6389    /// specific slot sets.
6390    ///
6391    /// Past the empty arm the gate enforces the chart-description
6392    /// shape predicate via [`crate::render::is_chart_description_shape`]:
6393    /// the structural single-line UTF-8 floor every realistic chart
6394    /// description in the wild matches — 1..=512 bytes, no leading
6395    /// or trailing whitespace, no ASCII control characters anywhere
6396    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
6397    /// carriage return, and every other control byte), Unicode
6398    /// continuation bytes accepted (the canonical fixtures carry
6399    /// `→` and `—`). Closes the canonical paste-from-doc footguns
6400    /// the bare empty-arm gate left open: paste-from-aligned-doc
6401    /// leading / trailing whitespace (`" Checkout flow."`,
6402    /// `"Checkout flow. "`), paste-from-multiline-doc newline
6403    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
6404    /// (`"Checkout\rflow."`), tab-from-aligned-doc
6405    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
6406    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
6407    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
6408    /// [`Self::validate_edicao`] establish past their own empty arms
6409    /// on the sibling universal-axis `Option<String>` Caixa-level
6410    /// value-shape surfaces.
6411    ///
6412    /// The empty-first cascade discipline mirrors every peer per-axis
6413    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
6414    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
6415    /// diagnostic surfaces on `Some("")` rather than the broader
6416    /// shape-predicate diagnostic — peer with how
6417    /// [`ManifestError::LicencaEmpty`] runs before
6418    /// [`ManifestError::LicencaInvalid`],
6419    /// [`ManifestError::EdicaoEmpty`] runs before
6420    /// [`ManifestError::EdicaoInvalid`],
6421    /// [`ManifestError::RepositorioEmpty`] runs before
6422    /// [`ManifestError::RepositorioInvalid`].
6423    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
6424        let Some(s) = self.descricao() else {
6425            return Ok(());
6426        };
6427        if s.is_empty() {
6428            return Err(ManifestError::DescricaoEmpty);
6429        }
6430        crate::render::is_chart_description_shape(s)
6431            .map_err(|reason| ManifestError::descricao_invalid(s, reason))?;
6432        Ok(())
6433    }
6434
6435    /// Reject `:licenca` values that are the empty string. The flat
6436    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
6437    /// SPDX-shaped license-expression axis every kind carries — the
6438    /// substrate routes the same string through the [`caixa-helm`]
6439    /// renderer's `build_readme` which folds it verbatim into the
6440    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
6441    /// section (`caixa-helm/src/lib.rs:361`) via
6442    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
6443    /// fallback only fires on `None`; a `Some("")` *skips the
6444    /// fallback* and silently passes the empty string through to a
6445    /// chart `README.md` whose `License` section renders as the bare
6446    /// trailing period (`.\n`) — peer footgun with the
6447    /// `Some("")`-skips-`unwrap_or_else` shape the
6448    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
6449    /// gates close on the sibling free-form-prose and git-URL axes.
6450    ///
6451    /// `None` (the canonical "omit the slot to defer to the
6452    /// renderer's `MIT` fallback" shape every existing fixture
6453    /// carries) is accepted trivially — the gate is a no-op when the
6454    /// author didn't declare a value. `Some("")` is gated by the
6455    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
6456    /// empty-arm shape every peer per-axis empty gate uses
6457    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6458    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6459    /// [`ManifestError::RepositorioEmpty`],
6460    /// [`ManifestError::DescricaoEmpty`]).
6461    ///
6462    /// Universal-axis (every kind carries `:licenca`), so wired at
6463    /// the caixa-build gate alongside the peer universal gates
6464    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6465    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6466    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6467    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
6468    /// — before the kind-coherence gates
6469    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6470    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6471    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6472    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6473    /// specific slot sets.
6474    ///
6475    /// Past the empty arm the gate enforces the SPDX-expression shape
6476    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
6477    /// structural alphabet floor every realistic SPDX expression in
6478    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
6479    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
6480    /// single ASCII space (token separator). Closes the canonical
6481    /// paste-from-doc footguns the bare empty-arm gate left open:
6482    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
6483    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
6484    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
6485    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
6486    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
6487    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
6488    /// Apache-2.0"`), and semicolon-list-separator confusion
6489    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
6490    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
6491    /// establish past their own empty arms.
6492    ///
6493    /// The empty-first cascade discipline mirrors every peer per-axis
6494    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
6495    /// [`ManifestError::LicencaInvalid`], so the narrower empty
6496    /// diagnostic surfaces on `Some("")` rather than the broader
6497    /// shape-predicate diagnostic — peer with how
6498    /// [`ManifestError::EdicaoEmpty`] runs before
6499    /// [`ManifestError::EdicaoInvalid`],
6500    /// [`ManifestError::RepositorioEmpty`] runs before
6501    /// [`ManifestError::RepositorioInvalid`].
6502    ///
6503    /// A future tightening on this axis can extend the alphabet
6504    /// floor into a full SPDX expression parser + license-id
6505    /// allowlist (rejecting alphabet-valid values that don't name a
6506    /// real SPDX license identifier — e.g., `"NotAReal"` is
6507    /// alphabet-valid but no `NotAReal` license-id exists). That
6508    /// parser only becomes meaningful past a real SPDX-spec
6509    /// dependency; this gate establishes the structural floor by
6510    /// refusing every non-SPDX-alphabet value at validate time.
6511    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
6512        let Some(s) = self.licenca() else {
6513            return Ok(());
6514        };
6515        if s.is_empty() {
6516            return Err(ManifestError::LicencaEmpty);
6517        }
6518        crate::render::is_spdx_expression_shape(s)
6519            .map_err(|reason| ManifestError::licenca_invalid(s, reason))?;
6520        Ok(())
6521    }
6522
6523    /// Reject `:edicao` values that are the empty string. The flat
6524    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
6525    /// language-edition axis every kind carries — it determines the
6526    /// tatara-lisp macro surface + compatibility flags the substrate
6527    /// applies when building a caixa, and lands verbatim in the
6528    /// `Caixa::template` author-time scaffold (the canonical
6529    /// `:edicao "2026"` line every `feira init` emits via
6530    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
6531    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
6532    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
6533    /// `caixa-core/src/render.rs:2510`) via
6534    /// `edicao: Some("2026".into())`.
6535    ///
6536    /// `None` (the canonical "omit the slot to defer to the
6537    /// substrate's default edition" shape every existing
6538    /// [`caixa-resolver`] integration test fixture carries via
6539    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6540    /// is accepted trivially — the gate is a no-op when the author
6541    /// didn't declare a value. `Some("")` is gated by the narrower
6542    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
6543    /// shape every peer per-axis empty gate uses
6544    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6545    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6546    /// [`ManifestError::RepositorioEmpty`],
6547    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
6548    ///
6549    /// Universal-axis (every kind carries `:edicao`), so wired at
6550    /// the caixa-build gate alongside the peer universal gates
6551    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6552    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6553    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6554    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6555    /// [`Self::validate_code_paths`] — before the kind-coherence
6556    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6557    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6558    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6559    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6560    /// specific slot sets.
6561    ///
6562    /// Past the empty arm the gate enforces the canonical year-shape
6563    /// predicate: every documented tatara-lisp edition is a 4-digit
6564    /// ASCII decimal year (`"2026"` is the only edition currently
6565    /// minted; future-introduced siblings will follow the same
6566    /// shape, peer with Cargo's `[package] edition` grammar which
6567    /// every value Cargo has ever accepted matches — `"2015"`,
6568    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
6569    /// 4 ASCII decimal bytes is rejected with the narrower
6570    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
6571    /// shape-predicate cascade [`Self::validate_repositorio`]
6572    /// establishes past its own empty arm
6573    /// ([`ManifestError::RepositorioEmpty`] →
6574    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
6575    /// paste-from-doc footguns the bare empty-arm gate left open:
6576    ///
6577    ///   - leading / trailing whitespace from a paste-from-doc
6578    ///     (`"2026 "`, `" 2026"`)
6579    ///   - control characters / CRLF from a paste-from-multiline-doc
6580    ///     (`"2026\n"`)
6581    ///   - non-ASCII look-alikes from a fullwidth keyboard
6582    ///     (`"2026"`) which would silently land as a non-ASCII
6583    ///     string in the rendered caixa.lisp
6584    ///   - free-form non-year values (`"x"`, `"latest"`,
6585    ///     `"nightly"`) that have no operational meaning on the
6586    ///     substrate's build-time edition selector
6587    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
6588    ///     `"r2026"`) — common version-tag idioms that don't apply
6589    ///     to the year-shaped edition axis
6590    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
6591    ///     edition is a year, not a fractional version
6592    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
6593    ///     `"00026"`) that don't name a year
6594    ///
6595    /// `None` (the canonical "omit the slot to defer to the
6596    /// substrate's default edition" shape every existing
6597    /// [`caixa-resolver`] integration test fixture carries via
6598    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6599    /// is accepted trivially — the gate is a no-op when the author
6600    /// didn't declare a value. The empty-first cascade discipline
6601    /// mirrors every peer per-axis identity gate:
6602    /// [`ManifestError::EdicaoEmpty`] runs before
6603    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
6604    /// diagnostic surfaces on `Some("")` rather than the broader
6605    /// shape-predicate diagnostic — peer with how
6606    /// [`ManifestError::NomeEmpty`] runs before
6607    /// [`ManifestError::NomeInvalid`],
6608    /// [`ManifestError::VersaoEmpty`] runs before
6609    /// [`ManifestError::VersaoInvalid`],
6610    /// [`ManifestError::RepositorioEmpty`] runs before
6611    /// [`ManifestError::RepositorioInvalid`].
6612    ///
6613    /// A future tightening on this axis can extend the shape
6614    /// predicate into a known-edition allowlist (rejecting
6615    /// year-shaped values that don't name a tatara-lisp edition
6616    /// the substrate actually understands — e.g., `"1999"` is
6617    /// year-shaped but no `1999` edition exists). That allowlist
6618    /// only becomes meaningful past the introduction of a sibling
6619    /// edition to `"2026"`; this gate establishes the structural
6620    /// floor by refusing every non-year-shaped value at validate
6621    /// time.
6622    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
6623        let Some(s) = self.edicao() else {
6624            return Ok(());
6625        };
6626        if s.is_empty() {
6627            return Err(ManifestError::EdicaoEmpty);
6628        }
6629        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
6630            return Err(ManifestError::edicao_invalid(
6631                s,
6632                "must be a 4-digit ASCII decimal year (canonical \"2026\")",
6633            ));
6634        }
6635        Ok(())
6636    }
6637
6638    /// Compose the supervisor-related flat slots into a single
6639    /// [`SupervisorSpec`] for validation. Returns `None` when the
6640    /// caixa isn't a `:kind Supervisor`.
6641    ///
6642    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
6643    /// simple (one form, no nested `:supervisor (…)` block); this view
6644    /// is the "typed shape" the operator + supervisor reconciler
6645    /// consume.
6646    #[must_use]
6647    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
6648        if !self.kind().is_supervisor() {
6649            return None;
6650        }
6651        // Fold through the shared `supervisor::duration_codec::parse`
6652        // — the same parser the serde-routed `with = "duration_codec"`
6653        // on `SupervisorSpec::restart_window`, the `:politicas
6654        // :timeout` codec, and the `:politicas :circuit-breaker
6655        // :window` codec all consume. The prior inline f64-shaped
6656        // duplicate (`parse_window_inline`) admitted every magnitude
6657        // the integer-magnitude gate (1c55a2a) rejects on the three
6658        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
6659        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
6660        // `None` (i.e. "no reset"), divergent from the shared codec's
6661        // integer-magnitude discipline by construction. The fold
6662        // closes the divergence: every value the typed
6663        // `SupervisorSpec` carries past `supervisor_view` is in the
6664        // shared codec's accepted set. The `.ok()` here preserves the
6665        // existing soft-swallow shape on this view-construction path;
6666        // the new [`Caixa::validate_restart_window`] (sibling of
6667        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
6668        // the offending raw string at build time so authoring tools
6669        // (`feira lint`, the future layout-side wire-up) surface a
6670        // self-locating diagnostic instead of a silently dropped
6671        // window.
6672        let restart_window = self
6673            .restart_window()
6674            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
6675        Some(SupervisorSpec {
6676            // Route the author-omitted `:estrategia` arm through the
6677            // substrate-canonical
6678            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
6679            // `pub const` rather than the transitively-derived
6680            // [`RestartStrategy::default`] route the prior
6681            // `.unwrap_or_default()` fold reached for — one source of
6682            // truth for the Erlang/OTP `one_for_one` half of Learn You
6683            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
6684            // supervisor canonical default that also backs the
6685            // [`crate::supervisor::Default for RestartStrategy`] impl
6686            // and the [`crate::supervisor::Default for SupervisorSpec`]
6687            // impl's struct-literal `estrategia` field, all now routed
6688            // through the same lifted constant. Prior to the lift the
6689            // composition site carried `.unwrap_or_default()` with no
6690            // compile-time link back to the shared OTP-canonical
6691            // default that the peer paired
6692            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
6693            // arm on the sibling `:max-restarts` axis routes through —
6694            // so a future rebrand of the OTP-canonical strategy default
6695            // (a widening to `rest_for_one` once the substrate
6696            // discovers startup-order-coupled child cohorts as the more
6697            // common shape, a per-cluster overlay the operator pins
6698            // through the MESH-COMPOSITION §III.2 supervision-canary
6699            // `:estrategia-overrides` roadmap slot) would have had to
6700            // migrate the paired `MaxIntensity` + `Period` halves
6701            // through the lifted constants and the `one_for_one` half
6702            // through a `RestartStrategy::default()` route in lockstep
6703            // or the three halves of the same OTP-canonical default
6704            // would silently drift out of pairing. Byte-parity against
6705            // the lifted constant closes the split. Pinned by
6706            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
6707            // in the tests module.
6708            estrategia: self
6709                .estrategia()
6710                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
6711            // Route the author-omitted `:max-restarts` arm through the
6712            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
6713            // typed `pub const` rather than the raw `5` literal — one
6714            // source of truth for the Erlang/OTP-canonical
6715            // `{intensity, 5, 60}` `MaxIntensity` default that also
6716            // backs the serde-side wire-format author-omitted arm on
6717            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
6718            // `#[serde(default = "default_max_restarts")]` and the
6719            // [`Default for SupervisorSpec`] impl's struct-literal
6720            // default field. Prior to the lift the composition site
6721            // carried a raw `5` with no compile-time link back to the
6722            // serde-side default, so a future rebrand of the OTP-
6723            // canonical default (a tightening to Elixir's `3`, a
6724            // widening to a per-cluster overlay the operator pins
6725            // through the MESH-COMPOSITION §III.2 supervision-canary
6726            // `:supervisor :max-restarts-overrides` roadmap slot)
6727            // would have had to be threaded through both open-coded
6728            // copies in lockstep or the wire-format author-omitted arm
6729            // and this view-construction author-omitted arm would
6730            // silently disagree on which restart-budget an omitted
6731            // `:max-restarts` resolves to. Pinned by
6732            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
6733            // in the tests module.
6734            max_restarts: self
6735                .max_restarts()
6736                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
6737            restart_window,
6738            children: self.children().to_vec(),
6739        })
6740    }
6741
6742    /// A minimal starter manifest emitted by `feira init`.
6743    #[must_use]
6744    pub fn template(nome: &str) -> String {
6745        format!(
6746            "(defcaixa\n  \
6747               :nome        {nome:?}\n  \
6748               :versao      \"0.1.0\"\n  \
6749               :kind        Biblioteca\n  \
6750               :edicao      \"2026\"\n  \
6751               :descricao   \"FIXME — describe this caixa\"\n  \
6752               :autores     ()\n  \
6753               :etiquetas   ()\n  \
6754               :deps        ()\n  \
6755               :deps-dev    ()\n  \
6756               :bibliotecas (\"lib/{nome}.lisp\"))\n"
6757        )
6758    }
6759
6760    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
6761    /// back after mutation (e.g. `feira add`).
6762    ///
6763    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
6764    /// The derive-macro `compile_from_sexp` path is the inverse, so any
6765    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
6766    #[must_use]
6767    pub fn to_lisp(&self) -> String {
6768        let json = serde_json::to_value(self).expect("Caixa serialize");
6769        let sexp = tatara_lisp::domain::json_to_sexp(&json);
6770        let tatara_lisp::Sexp::List(items) = sexp else {
6771            return format!("(defcaixa {sexp})\n");
6772        };
6773        let mut out = String::from("(defcaixa");
6774        let mut i = 0;
6775        while i + 1 < items.len() {
6776            out.push_str("\n  ");
6777            out.push_str(&items[i].to_string());
6778            out.push(' ');
6779            out.push_str(&items[i + 1].to_string());
6780            i += 2;
6781        }
6782        out.push_str(")\n");
6783        out
6784    }
6785}
6786
6787/// Errors raised by top-level [`Caixa`] validators that don't fit
6788/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
6789/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
6790/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
6791/// through every substrate-side artifact's `metadata.name` /
6792/// version derivation.
6793///
6794/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
6795/// doc-comment anticipates) can hold one of each per-axis error
6796/// family without reshaping individual diagnostics; this enum is
6797/// the first such per-Caixa-identity family.
6798#[derive(Debug, Error, PartialEq, Eq)]
6799pub enum ManifestError {
6800    #[error(
6801        ":nome is empty (every caixa must name itself; the value flows \
6802         into every K8s artifact's `metadata.name` derivation and into \
6803         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
6804    )]
6805    NomeEmpty,
6806    #[error(
6807        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
6808         apiserver enforces this rule on every `metadata.name` the \
6809         caixa's substrate-side renderers derive from `:nome` — the \
6810         `lareira-<nome>` Helm chart name, the programs.yaml entry \
6811         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
6812         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
6813         name; use a lowercase alphanumeric + hyphen identifier like \
6814         `\"checkout\"` or `\"cart-v2\"`)"
6815    )]
6816    NomeInvalid { nome: String, reason: String },
6817    #[error(
6818        ":nome {nome:?} overflows the joint-length budget on the canonical \
6819         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
6820         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
6821         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
6822         `chart:` slot, `caixa-tatara`'s `release_name` + \
6823         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
6824         joint name through the canonical `lareira_chart_name` helper, and \
6825         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
6826         DNS-1123 label cap on every chart-name-derived `metadata.name` \
6827         reject any joint name exceeding 63 bytes; the narrower \
6828         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
6829         arm gates the chart-name budget downstream renderers inherit)"
6830    )]
6831    NomeChartNameBudgetExceeded { nome: String, reason: String },
6832    #[error(
6833        ":versao is empty (every caixa must pin its own version; the value flows \
6834         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
6835         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
6836         `:latest` tags, the lacre closure's `concrete_versao`, and the \
6837         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
6838    )]
6839    VersaoEmpty,
6840    #[error(
6841        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
6842         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
6843         with optional `-prerelease` and `+build` — across every artifact derived \
6844         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
6845         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
6846         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
6847         and the `:upgrade-from :from` peers that match against this exact shape; \
6848         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
6849         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
6850         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
6851    )]
6852    VersaoInvalid { versao: String, reason: String },
6853    #[error(
6854        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
6855         substrate consumes this string through the shared \
6856         `supervisor::duration_codec` — the same parser routed via `with = \
6857         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
6858         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
6859         the canonical authoring form is `<integer><unit>` where the unit is one \
6860         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
6861         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
6862         Without this gate a malformed `:restart-window` silently produced a \
6863         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
6864         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
6865         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
6866         layer with the offending value named verbatim. Omit the slot entirely to \
6867         express \"no reset\"; carry a positive integer duration to express the \
6868         sliding window)"
6869    )]
6870    RestartWindowMalformed {
6871        restart_window: String,
6872        reason: String,
6873    },
6874    #[error(
6875        "{slot} entry is an empty path string — every {slot} entry must name \
6876         a file relative to the caixa root; omit the entry to omit the file \
6877         (the layout checker's `root.join(\"\")` resolves to the caixa root \
6878         itself, so an empty entry silently aliases the project root as a \
6879         declared {slot} file, then fails downstream at parse / existence \
6880         time with a diagnostic that names the root rather than the offending \
6881         entry)"
6882    )]
6883    CodePathEmpty { slot: &'static str },
6884    #[error(
6885        "{slot} entry {} is an absolute path — entries must be relative to \
6886         the caixa root, since `Path::join` replaces the base with an absolute \
6887         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
6888         outside the caixa root sandbox; rewrite the entry as a relative path \
6889         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
6890         `\"servicos/<name>.computeunit.yaml\"`)",
6891        path.display()
6892    )]
6893    CodePathAbsolute { slot: &'static str, path: PathBuf },
6894    #[error(
6895        "{slot} entry {} contains a `..` component — entries must not traverse \
6896         above the caixa root (the layout's `starts_with(<dir>)` fence on \
6897         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
6898         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
6899         has no such fence, so a leading `..` escapes unconditionally if the \
6900         resolved target happens to exist)",
6901        path.display()
6902    )]
6903    CodePathParentEscape { slot: &'static str, path: PathBuf },
6904    #[error(
6905        "{slot} entry {} does not terminate in the `.lisp` extension — every \
6906         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
6907         loop reads through `tatara_lisp::read` at parse time, so any other \
6908         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
6909         structurally a parser error far from the source caixa.lisp, with \
6910         no field naming the offending `:bibliotecas` entry. Pin a relative \
6911         path under the caixa root whose terminating extension is \
6912         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
6913         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
6914         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
6915         (33cc830) axes already carry through the same lifted \
6916         `is_lisp_extension` predicate",
6917        path.display()
6918    )]
6919    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
6920    #[error(
6921        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
6922         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
6923         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
6924         through `serde_yaml::from_str` at chart / FluxCD bundle render \
6925         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
6926         off-by-one-segment `.computeunit-yaml`, the editor-backup \
6927         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
6928         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
6929         source caixa.lisp, with no field naming the offending `:servicos` \
6930         entry. Pin a relative path under the caixa root whose terminating \
6931         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
6932         `\"servicos/<name>.computeunit.yaml\"`, \
6933         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
6934         contract the sibling `:bibliotecas` axis (64772a9) already carries \
6935         on the tatara-lisp-source axis through the peer lifted \
6936         `is_lisp_extension` predicate, here on the compound-suffix axis \
6937         `Path::extension` can't express on its own through the lifted \
6938         `is_computeunit_yaml_extension` predicate",
6939        path.display()
6940    )]
6941    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
6942    #[error(
6943        "{slot} entry {} appears more than once (the code-path list is \
6944         a set, not a multiset; every peer Vec-shaped author-supplied \
6945         list past validate is set-not-multiset — `:membros :caixa`, \
6946         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6947         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
6948         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
6949         code-path lists are the last Vec-shaped author-supplied slots on \
6950         the typed Caixa surface still admitting a duplicate entry. \
6951         `:bibliotecas` duplicates re-parse the same file at \
6952         `feira build` time and silently mask the author's intent to \
6953         declare a *second* biblioteca; `:exe` duplicates collide on the \
6954         flake `packages.<name>` derivation key at the future \
6955         `caixa-flake` materializer; `:servicos` duplicates surface as the \
6956         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
6957         rejection far from the source `caixa.lisp`. Drop the duplicate \
6958         or rename it to the actual second file intended)",
6959        path.display()
6960    )]
6961    CodePathDuplicate { slot: &'static str, path: PathBuf },
6962    #[error(
6963        ":etiquetas entry is empty (every tag must carry a non-empty \
6964         registry-search identifier; the empty entry has no operational \
6965         meaning — it indexes nothing in the future caixa-registry search \
6966         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
6967         with a no-op tag; omit the entry to express \"no tag on this \
6968         position\")"
6969    )]
6970    EtiquetaEmpty,
6971    #[error(
6972        ":etiquetas entry {etiqueta:?} appears more than once (the \
6973         registry-search tag set is a set, not a multiset; duplicate \
6974         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
6975         at chart render — a \"second wins / one silently disappears\" \
6976         shape divergent from every peer typed-graph set gate \
6977         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
6978         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
6979         duplicate or rename it to the actual tag intended)"
6980    )]
6981    EtiquetaDuplicate { etiqueta: String },
6982    #[error(
6983        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
6984         {reason} (the substrate consumes this string through the shared \
6985         `crate::render::is_chart_keyword_shape` predicate — the same \
6986         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
6987         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
6988         continuation. The canonical authoring shapes are short kebab-case \
6989         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
6990         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
6991         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
6992         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
6993         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
6994         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
6995         `\"mesh,http,grpc\"` — the author meant to author three separate \
6996         list entries; path-separator confusion `\"caixa/servico\"`; \
6997         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
6998         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
6999         `\"café\"` — every legitimate search tag is strict ASCII; \
7000         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
7001         passed `from_lisp` + `validate_etiquetas` + \
7002         `StandardLayout::verify` and landed in the rendered \
7003         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
7004         malformed search tag — Artifact Hub's keyword index + the future \
7005         caixa-registry's keyword index would either silently drop the \
7006         tag or fail to index it far from the source caixa.lisp; the gate \
7007         moves the diagnostic to the manifest layer with the offending \
7008         value named verbatim)"
7009    )]
7010    EtiquetaInvalid { etiqueta: String, reason: String },
7011    #[error(
7012        ":autores entry is empty (every maintainer must carry a non-empty \
7013         identifier; the empty entry has no operational meaning — it \
7014         identifies no one in the substrate's authorship index and renders \
7015         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
7016         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
7017         omit the entry to express \"no maintainer on this position\")"
7018    )]
7019    AutorEmpty,
7020    #[error(
7021        ":autores entry {autor:?} appears more than once (the maintainer \
7022         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
7023         `maintainers:` rendering does *no* dedup — duplicate entries \
7024         stack verbatim in `Chart.yaml` as two identical \
7025         `Maintainer {{ name, email: None }}` records, divergent from every \
7026         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
7027         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
7028         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
7029         rename it to the actual author intended)"
7030    )]
7031    AutorDuplicate { autor: String },
7032    #[error(
7033        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
7034         {reason} (the substrate consumes this string through the shared \
7035         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
7036         single-line-UTF-8 floor every realistic chart maintainer name carries: \
7037         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
7038         characters anywhere, Unicode bytes accepted. The canonical authoring \
7039         shapes are short single-line identifiers like `\"pleme-io\"`, \
7040         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
7041         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
7042         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
7043         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
7044         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
7045         records into one entry instead of splitting into one entry per author; \
7046         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
7047         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
7048         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7049         `validate_autores` + `StandardLayout::verify` and landed in the \
7050         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
7051         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
7052         round-trip — every chart-aware UI (`helm list`, `helm search`, \
7053         Artifact Hub maintainer index) would render the maintainer name in a \
7054         single-line column far from the source caixa.lisp; the gate moves the \
7055         diagnostic to the manifest layer with the offending value named \
7056         verbatim)"
7057    )]
7058    AutorInvalid { autor: String, reason: String },
7059    #[error(
7060        ":repositorio is the empty string (every published caixa names its \
7061         git source via a non-empty `:repositorio` locator — the value \
7062         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
7063         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
7064         `GitRepository.spec.url` via `caixa-flux`'s \
7065         `ClusterBundleOpts::for_caixa`; both consumers' \
7066         `Option::unwrap_or_else` fallbacks only fire when the slot is \
7067         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
7068         `url: \"\"` in the rendered artifacts and breaks at `helm \
7069         template` / FluxCD source-controller reconcile time far from the \
7070         source caixa.lisp; omit the slot entirely to defer to the \
7071         renderer's `https://github.com/pleme-io/<nome>` / \
7072         `caixa.nome`-derived fallback, or carry a canonical authoring \
7073         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
7074         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
7075         `\"file:///path\"`)"
7076    )]
7077    RepositorioEmpty,
7078    #[error(
7079        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
7080         (the substrate consumes this string through the shared \
7081         `crate::render::is_git_repo_url` predicate — the same parser the \
7082         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
7083         value through via `DepSource::validate`; the canonical authoring \
7084         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
7085         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
7086         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
7087         scp-style SSH form. Without this gate a malformed `:repositorio` \
7088         (whitespace from a paste-from-doc; control characters / CRLF \
7089         from a paste-from-multiline-doc; a leading `-` from a \
7090         CLI-argument-injection footgun; a missing `:` separator from a \
7091         bare `org/repo` shape git treats as a relative filesystem path) \
7092         silently landed in the rendered `Chart.yaml home:` and the \
7093         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
7094         FluxCD reconcile time far from the source caixa.lisp; the gate \
7095         moves the diagnostic to the manifest layer with the offending \
7096         value named verbatim)"
7097    )]
7098    RepositorioInvalid { repositorio: String, reason: String },
7099    #[error(
7100        ":descricao is the empty string (every published caixa names \
7101         its purpose via a non-empty `:descricao` summary — the value \
7102         flows verbatim into the rendered `lareira-<nome>` Helm \
7103         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
7104         `build_chart_yaml` and into the chart `README.md` header via \
7105         `build_readme`; both consumers' `Option::unwrap_or_else` \
7106         `caixa.nome`-derived fallbacks only fire when the slot is \
7107         `None`, so an empty `Some(\"\")` silently lands as \
7108         `description: \"\"` / a blank `README.md` header in the \
7109         rendered artifacts and breaks at `helm lint` time \
7110         (`WARNING [chart.metadata.description]: description is \
7111         required` on `apiVersion: v2` charts) far from the source \
7112         caixa.lisp; omit the slot entirely to defer to the \
7113         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
7114         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
7115         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
7116         Servico.\"`)"
7117    )]
7118    DescricaoEmpty,
7119    #[error(
7120        ":descricao {descricao:?} is not a valid chart-description shape: \
7121         {reason} (the substrate consumes this string through the shared \
7122         `crate::render::is_chart_description_shape` predicate — the same \
7123         single-line-UTF-8 floor every realistic chart description carries: \
7124         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
7125         characters anywhere, Unicode prose bytes accepted. The canonical \
7126         authoring shapes are short single-line summaries like `\"Canonical \
7127         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
7128         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
7129         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
7130         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
7131         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
7132         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
7133         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
7134         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7135         `validate_descricao` + `StandardLayout::verify` and landed in the \
7136         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
7137         field + `README.md` header paragraph as a YAML-illegal multi-line \
7138         scalar or a silently-trimmed whitespace round-trip — every \
7139         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
7140         render the description in a single-line column far from the source \
7141         caixa.lisp; the gate moves the diagnostic to the manifest layer \
7142         with the offending value named verbatim)"
7143    )]
7144    DescricaoInvalid { descricao: String, reason: String },
7145    #[error(
7146        ":licenca is the empty string (every published caixa names \
7147         its license via a non-empty `:licenca` SPDX expression — the \
7148         value flows verbatim into the rendered `lareira-<nome>` Helm \
7149         chart's `README.md` `## License` section via `caixa-helm`'s \
7150         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
7151         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
7152         only fires when the slot is `None`, so an empty `Some(\"\")` \
7153         silently lands as a bare trailing period in the rendered \
7154         chart `README.md` `License` section far from the source \
7155         caixa.lisp; omit the slot entirely to defer to the \
7156         renderer's `MIT` fallback, or carry a canonical SPDX \
7157         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
7158         `\"Apache-2.0 OR MIT\"`)"
7159    )]
7160    LicencaEmpty,
7161    #[error(
7162        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
7163         (the substrate consumes this string through the shared \
7164         `crate::render::is_spdx_expression_shape` predicate — the same \
7165         alphabet-floor parser every peer per-axis value-shape gate routes \
7166         its value through; the canonical authoring shapes are single \
7167         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
7168         compound expressions like `\"Apache-2.0 OR MIT\"`, \
7169         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
7170         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
7171         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
7172         like `\"LicenseRef-MyLicense\"` / \
7173         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
7174         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
7175         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
7176         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
7177         a smart-quote paste; underscore-instead-of-hyphen typo \
7178         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
7179         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
7180         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
7181         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
7182         `README.md` `## License` section + a future SPDX-aware \
7183         `Chart.yaml license:` emitter would refuse the value at \
7184         `helm lint` time far from the source caixa.lisp; the gate moves \
7185         the diagnostic to the manifest layer with the offending value \
7186         named verbatim)"
7187    )]
7188    LicencaInvalid { licenca: String, reason: String },
7189    #[error(
7190        ":edicao is the empty string (every published caixa names \
7191         its language edition via a non-empty `:edicao` value — the \
7192         edition determines the tatara-lisp macro surface + \
7193         compatibility flags the substrate applies when building \
7194         the caixa; the canonical `Caixa::template` scaffold every \
7195         `feira init` emits carries `:edicao \"2026\"` verbatim and \
7196         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
7197         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
7198         construction, so an empty `Some(\"\")` silently lands as a \
7199         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
7200         a future renderer-side consumer that folds it through \
7201         `Option::unwrap_or_else` will skip the fallback and pass the \
7202         empty edition through to the substrate's build-time edition \
7203         selector far from the source caixa.lisp; omit the slot \
7204         entirely to defer to the substrate's default edition, or \
7205         carry a canonical edition like `\"2026\"`)"
7206    )]
7207    EdicaoEmpty,
7208    #[error(
7209        ":edicao {edicao:?} is not a valid edition: {reason} (every \
7210         documented tatara-lisp edition is a 4-digit ASCII decimal \
7211         year — `\"2026\"` is the only edition currently minted; \
7212         future-introduced siblings will follow the same shape, peer \
7213         with Cargo's `[package] edition` grammar which every value \
7214         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
7215         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
7216         paste-from-doc footguns silently passed: a trailing space \
7217         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
7218         from a paste-from-multiline-doc, a fullwidth-keyboard \
7219         look-alike (`\"2026\"`), a free-form non-year value \
7220         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
7221         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
7222         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
7223         wrong-length numeric value (`\"26\"`, `\"202\"`, \
7224         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
7225         rendered caixa.lisp and broke at the substrate's \
7226         build-time edition selector far from the source caixa.lisp; \
7227         omit the slot entirely to defer to the substrate's default \
7228         edition, or carry a canonical 4-digit ASCII decimal year \
7229         like `\"2026\"`)"
7230    )]
7231    EdicaoInvalid { edicao: String, reason: String },
7232}
7233
7234// Fold the five `Err(ManifestError::CodePath{Absolute,ParentEscape,
7235// NonLispExtension,NonComputeUnitYamlExtension,Duplicate} { slot,
7236// path: path.to_path_buf() })` four-line struct-variant wire-up sites at
7237// [`Caixa::validate_code_path_lists`]'s per-slot per-entry cascade onto
7238// one substrate-primitive family on the `ManifestError` envelope — the
7239// five open-coded ctor sites remaining on the `:bibliotecas` / `:exe` /
7240// `:servicos` code-path-list value-shape trajectory this envelope carries,
7241// and the family sibling of the peer [`crate::behavior::behavior_slot_path_ctors!`]
7242// (67c31ec) two-slot `{ slot: &'static str, path: PathBuf }` envelope on
7243// the [`crate::BehaviorError`] surface that keys off the exact same
7244// `(slot: &'static str, path: &Path)` argument tuple.
7245//
7246// The five wire-up sites this fold closes are the sandbox-shape
7247// absolute-path arm (`return Err(ManifestError::CodePathAbsolute { slot,
7248// path: path.to_path_buf() })` on the [`is_sandboxed_relative_path`]
7249// `PathShapeViolation::Absolute` branch), the sandbox-shape
7250// parent-escape arm (`return Err(ManifestError::CodePathParentEscape {
7251// slot, path: path.to_path_buf() })` on the sibling
7252// `PathShapeViolation::ParentEscape` branch), the LispSource
7253// terminating-extension arm (`return Err(ManifestError::CodePathNonLispExtension {
7254// slot, path: path.to_path_buf() })` on the `!is_lisp_extension(path)`
7255// branch of the `:bibliotecas` file-type gate), the ComputeUnitYaml
7256// compound-suffix arm (`return Err(ManifestError::CodePathNonComputeUnitYamlExtension
7257// { slot, path: path.to_path_buf() })` on the
7258// `!is_computeunit_yaml_extension(path)` branch of the `:servicos`
7259// file-type gate), and the cross-entry duplicate arm
7260// (`ManifestError::CodePathDuplicate { slot, path: path.to_path_buf() }`
7261// inside the closure passed to [`crate::render::insert_first_seen`]) —
7262// each opened the identical `ManifestError::CodePath* { slot,
7263// path: path.to_path_buf() }` four-line struct-literal against the same
7264// `(slot: &'static str, path: &Path)` local tuple, the exact "same
7265// block re-inlined at every consumer" shape the PRIME DIRECTIVE names
7266// as a bug. The variant discriminator is the only thing that varies
7267// between the five sites; the rest of the struct-literal is a
7268// byte-for-byte re-inline.
7269//
7270// The macro below generates one `#[must_use]` inherent constructor per
7271// variant of shape `fn <ctor>(slot: &'static str, path: &std::path::Path)
7272// -> Self`, so every wire-up site collapses onto one dispatch:
7273// `ManifestError::<ctor>(slot, path)`, byte-equal to the pre-lift
7274// struct-literal on the same `(&'static str, &Path)` fixture. The
7275// uniform two-field construction (`slot` verbatim as `&'static str`,
7276// `path.to_path_buf()`) is spelled once — inside the macro — rather
7277// than at every wire-up site. The `slot` parameter stays `&'static str`
7278// (not `&str`) so every arm continues to carry a program-lifetime
7279// `:bibliotecas` / `:exe` / `:servicos` author-key label — one of the
7280// three `&'static str` literals threaded through the outer per-slot
7281// iterator at [`Caixa::validate_code_path_lists`] — matching the
7282// enum-field type. A runtime-borrowed `&str` would silently downgrade
7283// the label lifetime and let a caller stash a non-`'static` borrow into
7284// the returned error. The `&Path` parameter accepts both
7285// `&Path` and `&PathBuf` (via Deref coercion), so every existing
7286// wire-up — each already binds `let path = Path::new(entry);` from the
7287// per-entry loop — threads through the ctor without a pre-conversion.
7288//
7289// Every future consumer that wants to construct one of these five
7290// variants outside the five in-crate wire-up sites (a deferred
7291// `feira validate --code-paths` per-caixa admission verb re-checking
7292// each declared `:bibliotecas` / `:exe` / `:servicos` entry against the
7293// same sandbox-shape + file-type + duplicate cascade, a future
7294// caixa-registry per-lacre code-path re-validator at lacre-resolve
7295// time, a per-`Caixa` overlay resolver rejecting an author-supplied
7296// code-path against a cluster-local snapshot) now reaches each variant
7297// through one call rather than re-inlining the four-line struct-literal
7298// in lockstep with the five in-crate wire-up sites.
7299macro_rules! manifest_code_path_slot_path_ctors {
7300    ($($ctor:ident => $variant:ident),* $(,)?) => {
7301        impl ManifestError {
7302            $(
7303                #[doc = concat!(
7304                    "Construct a [`ManifestError::",
7305                    stringify!($variant),
7306                    "`] naming the offending `:bibliotecas` / `:exe` / ",
7307                    "`:servicos` code-path list `slot` label and the ",
7308                    "offending entry `path`. Folds the uniform `Self::",
7309                    stringify!($variant),
7310                    " { slot, path: path.to_path_buf() }` two-field ",
7311                    "struct-literal onto one substrate primitive so ",
7312                    "every wire-up on this variant at ",
7313                    "[`Caixa::validate_code_path_lists`] reads through ",
7314                    "one dispatch rather than the pre-lift four-line ",
7315                    "open-coded block. The `slot` label threads verbatim ",
7316                    "from the outer per-slot iterator (one of the three ",
7317                    "code-path author-key `&'static str` consts) and the ",
7318                    "`path` from the per-entry inner iterator's ",
7319                    "`Path::new(entry)` binding."
7320                )]
7321                #[must_use]
7322                pub fn $ctor(slot: &'static str, path: &std::path::Path) -> Self {
7323                    Self::$variant {
7324                        slot,
7325                        path: path.to_path_buf(),
7326                    }
7327                }
7328            )*
7329        }
7330    };
7331}
7332
7333manifest_code_path_slot_path_ctors! {
7334    code_path_absolute => CodePathAbsolute,
7335    code_path_parent_escape => CodePathParentEscape,
7336    code_path_non_lisp_extension => CodePathNonLispExtension,
7337    code_path_non_computeunit_yaml_extension => CodePathNonComputeUnitYamlExtension,
7338    code_path_duplicate => CodePathDuplicate,
7339}
7340
7341// Fold the nine `ManifestError::{Nome, NomeChartNameBudgetExceeded, Versao,
7342// Etiqueta, Autor, Repositorio, Descricao, Licenca, Edicao}Invalid
7343// { <field>: <val>.to_string() | <val>.clone(), reason: <expr> }` wire-up
7344// sites at the per-axis [`Caixa::validate_*`] cascade onto one substrate-
7345// primitive family per typed variant — the direct sibling on the
7346// [`ManifestError`] envelope of the peer
7347// [`crate::aplicacao::aplicacao_field_reason_ctors!`] (981060b, 7 variants
7348// on `AplicacaoError` at `MembroCaixaInvalid` / `EntradaParaInvalid` /
7349// `EntradaHostInvalid` / `EntradaPathInvalid` / `PlacementClusterInvalid` /
7350// `PlacementAffinityInvalid` / `ShardKeyInvalid`) on the M3 mesh side, and
7351// of the peer [`crate::dep::dep_nome_axis_reason_ctors!`] (5621f8a,
7352// 3 variants on `DepError` at `VersaoInvalid` / `FonteRepoShape` /
7353// `CaracteristicaInvalid`) on the sibling `:deps` envelope's mirror-
7354// symmetric `{ nome: String, <axis>: String, reason: String }` three-slot
7355// shape (the `nome` axis added at the per-dep-owned altitude). Every one
7356// of the peer four-family `LayoutError` ctor set
7357// ([`crate::layout::layout_violation_ctors!`] 131ca0d — 16 variants on
7358// `{ caixa, issue }`, [`crate::layout::layout_slot_kind_ctors!`] 0419438
7359// — 4 variants on `{ caixa, kind, slots }`,
7360// [`crate::LayoutError::missing_entry`] 1b09f9d — 1 variant on
7361// `{ kind, path }`, [`crate::layout::layout_nome_only_ctors!`] 3fe3dd7 —
7362// 6 variants on `<Variant>(String)`) and the peer three
7363// [`crate::limits::limits_codec_value_*_ctors!`] codec families (81c856c)
7364// each carry the same discipline on their sibling envelopes.
7365//
7366// The nine variants share the identical `{ <field>: String,
7367// reason: String }` two-slot shape:
7368//   - `NomeInvalid { nome, reason }` at [`Caixa::validate_nome`]
7369//     (`|reason| ManifestError::NomeInvalid { nome: nome.to_string(),
7370//     reason }` inside [`crate::render::require_valid_dns_1123_label`]'s
7371//     `on_invalid` bracket-closure slot);
7372//   - `NomeChartNameBudgetExceeded { nome, reason }` at
7373//     [`Caixa::validate_nome_chart_name_budget`]
7374//     (`|reason| ManifestError::NomeChartNameBudgetExceeded { nome:
7375//     nome.to_string(), reason }` after
7376//     [`crate::render::is_lareira_chart_name_shape`] rejects the offending
7377//     `:nome`);
7378//   - `VersaoInvalid { versao, reason }` at [`Caixa::validate_versao`]
7379//     (`|e| ManifestError::VersaoInvalid { versao: versao.to_string(),
7380//     reason: e.to_string() }` after [`semver::Version::parse`] rejects
7381//     the offending `:versao`);
7382//   - `EtiquetaInvalid { etiqueta, reason }` at
7383//     [`Caixa::validate_etiquetas`]
7384//     (`|reason| ManifestError::EtiquetaInvalid { etiqueta:
7385//     etiqueta.clone(), reason }` after
7386//     [`crate::render::is_chart_keyword_shape`] rejects the offending
7387//     `:etiquetas` entry);
7388//   - `AutorInvalid { autor, reason }` at [`Caixa::validate_autores`]
7389//     (`|reason| ManifestError::AutorInvalid { autor: autor.clone(),
7390//     reason }` after [`crate::render::is_chart_maintainer_name_shape`]
7391//     rejects the offending `:autores` entry);
7392//   - `RepositorioInvalid { repositorio, reason }` at
7393//     [`Caixa::validate_repositorio`]
7394//     (`|reason| ManifestError::RepositorioInvalid { repositorio:
7395//     s.to_string(), reason }` after
7396//     [`crate::render::is_git_repo_url`] rejects the offending
7397//     `:repositorio`);
7398//   - `DescricaoInvalid { descricao, reason }` at
7399//     [`Caixa::validate_descricao`]
7400//     (`|reason| ManifestError::DescricaoInvalid { descricao:
7401//     s.to_string(), reason }` after
7402//     [`crate::render::is_chart_description_shape`] rejects the offending
7403//     `:descricao`);
7404//   - `LicencaInvalid { licenca, reason }` at [`Caixa::validate_licenca`]
7405//     (`|reason| ManifestError::LicencaInvalid { licenca: s.to_string(),
7406//     reason }` after [`crate::render::is_spdx_expression_shape`] rejects
7407//     the offending `:licenca`);
7408//   - `EdicaoInvalid { edicao, reason }` at [`Caixa::validate_edicao`]
7409//     (`return Err(ManifestError::EdicaoInvalid { edicao: s.to_string(),
7410//     reason: "must be a 4-digit ASCII decimal year (canonical
7411//     \"2026\")".to_string() })` on the direct year-shape arm).
7412//
7413// Each opened the identical four-line
7414// `ManifestError::<Variant> { <field>: <val>.to_string() | .clone(),
7415// reason: <expr> }` struct-literal against the caller-side `<field>: &str`
7416// / `<field>: &String` local — the exact "same block re-inlined at every
7417// consumer" shape the PRIME DIRECTIVE names as a bug, on the same altitude
7418// the peer `aplicacao_field_reason_ctors!` / `dep_nome_axis_reason_ctors!`
7419// / `LayoutError` / `LimitsError` / `BehaviorError` / `UpgradeError`
7420// families each closed on their sibling envelopes.
7421//
7422// The macro below generates one `#[must_use]` inherent constructor per
7423// variant of shape `fn <ctor>(<field>: &str, reason: impl Into<String>)
7424// -> Self`, collapsing every site onto one dispatch per arm:
7425// `ManifestError::<ctor>(<val>, <reason>)`, byte-equal to the pre-lift
7426// struct-literal on the same `(<field>, reason)` pair. The uniform
7427// two-field construction (`<field>: <field>.to_string()`,
7428// `reason: reason.into()`) is spelled once — inside the macro — rather
7429// than at every wire-up site. The `reason: impl Into<String>` bound
7430// accepts owned `String` (the parser-shaped reason every predicate
7431// returns via `Result<(), String>`; the `e.to_string()` result the
7432// `semver::Version::parse` arm passes; the literal `"…".to_string()` the
7433// `EdicaoInvalid` direct arm passes), `&str` literals, and `format!(…)`
7434// outputs verbatim so no wire-up site changes its per-arm diagnostic
7435// shape at the lift, matching the peer
7436// [`crate::aplicacao::aplicacao_field_reason_ctors!`] and
7437// [`crate::dep::dep_nome_axis_reason_ctors!`] bounds on the sibling
7438// two- and three-slot envelopes. The `<field>: &str` parameter accepts
7439// both `&str` (from the [`Caixa::nome`] / [`Caixa::versao`] /
7440// [`Caixa::repositorio`] / [`Caixa::descricao`] / [`Caixa::licenca`] /
7441// [`Caixa::edicao`] accessors) and `&String` (from the
7442// [`Caixa::etiquetas`] / [`Caixa::autores`] slice iterators) via Deref
7443// coercion, so every existing wire-up threads through the ctor without a
7444// pre-conversion. `#[must_use]` fires a compile warning at any wire-up
7445// that mistakenly discards the constructed error rather than routing it
7446// through `return Err(…)` / `.map_err(…)` / a closure return.
7447//
7448// Every future consumer that wants to construct one of these nine
7449// variants outside the current in-crate wire-up sites (a deferred
7450// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-manifest-axis
7451// admission validators re-checking each declared identity / metadata
7452// axis against a cluster-local snapshot, a future `feira validate
7453// --manifest` per-caixa admission verb re-running the same
7454// value-shape gates on demand, a per-lacre overlay resolver rejecting
7455// an author-supplied manifest override against a cluster-local snapshot
7456// the M4 CR materializer projects, a future
7457// `caixa-registry` per-lacre re-validator at lacre-resolve time
7458// re-checking each declared axis against the same predicates) now
7459// reaches each variant through one call rather than re-inlining the
7460// four-line struct-literal in lockstep with the nine in-crate wire-up
7461// sites.
7462macro_rules! manifest_field_reason_ctors {
7463    ($($ctor:ident => $variant:ident { $field:ident }),* $(,)?) => {
7464        impl ManifestError {
7465            $(
7466                #[doc = concat!(
7467                    "Construct a [`ManifestError::",
7468                    stringify!($variant),
7469                    "`] naming the offending `",
7470                    stringify!($field),
7471                    "` under the given `reason`. Folds the uniform ",
7472                    "`Self::",
7473                    stringify!($variant),
7474                    " { ",
7475                    stringify!($field),
7476                    ": ",
7477                    stringify!($field),
7478                    ".to_string(), reason: reason.into() }` two-slot ",
7479                    "construction onto one substrate primitive so every ",
7480                    "wire-up on this variant reads through one dispatch ",
7481                    "rather than the pre-lift four-line struct-literal ",
7482                    "block. `reason` accepts owned `String`, `&str` ",
7483                    "literals, and `format!(…)` outputs through the ",
7484                    "`impl Into<String>` bound; the `",
7485                    stringify!($field),
7486                    ": &str` parameter accepts both `&str` and `&String` ",
7487                    "via Deref coercion."
7488                )]
7489                #[must_use]
7490                pub fn $ctor($field: &str, reason: impl Into<String>) -> Self {
7491                    Self::$variant {
7492                        $field: $field.to_string(),
7493                        reason: reason.into(),
7494                    }
7495                }
7496            )*
7497        }
7498    };
7499}
7500
7501manifest_field_reason_ctors! {
7502    nome_invalid => NomeInvalid { nome },
7503    nome_chart_name_budget_exceeded => NomeChartNameBudgetExceeded { nome },
7504    versao_invalid => VersaoInvalid { versao },
7505    etiqueta_invalid => EtiquetaInvalid { etiqueta },
7506    autor_invalid => AutorInvalid { autor },
7507    repositorio_invalid => RepositorioInvalid { repositorio },
7508    descricao_invalid => DescricaoInvalid { descricao },
7509    licenca_invalid => LicencaInvalid { licenca },
7510    edicao_invalid => EdicaoInvalid { edicao },
7511}
7512
7513// Fold the two `ManifestError::{EtiquetaDuplicate, AutorDuplicate}
7514// { <field>: <val>.clone() }` single-`String`-slot wire-up sites at
7515// [`Caixa::validate_etiquetas`] and [`Caixa::validate_autores`] onto one
7516// substrate-primitive family per typed variant — the direct sibling on
7517// the [`ManifestError`] envelope of the peer
7518// [`crate::aplicacao::aplicacao_caixa_only_ctors!`] (d9f6867, 4 variants
7519// on `AplicacaoError` at `ContratoMemberMissing` / `MembroVersaoEmpty` /
7520// `MembroDuplicate` / `MembroIsSelfAplicacao` on the `{ caixa: String }`
7521// shape) and [`crate::aplicacao::aplicacao_path_only_ctors!`] (3ba8de6,
7522// 2 variants on `AplicacaoError` at `EntradaPathNotAbsolute` /
7523// `EntradaPathDuplicate` on the `{ path: String }` shape) on the sibling
7524// M3 mesh `AplicacaoError` envelope, and of the peer
7525// [`crate::supervisor::supervisor_caixa_only_ctors!`] (db09650, 3 variants
7526// on the sibling M2 `SupervisorError` envelope's `{ caixa: String }`
7527// shape), [`crate::dep::dep_nome_only_ctors!`] (792aa92, 5 variants on
7528// `DepError { nome: String }`), and [`crate::upgrade::upgrade_script_only_ctors!`]
7529// (7468ca9, 3 variants on `UpgradeError { script: PathBuf }`) folds on
7530// the sibling envelopes — the last two open-coded single-slot
7531// `{ <field>: String }` struct-literal sites on `ManifestError` fold
7532// onto one substrate primitive per typed variant, matching the
7533// "one substrate primitive per typed variant on the single-slot
7534// `{ <ident>: String }` envelope shape" fold discipline every peer
7535// per-Caixa-identity family already carries.
7536//
7537// Both wire-up sites — one at [`Caixa::validate_etiquetas`]'s per-entry
7538// [`crate::render::insert_first_seen`] dedup closure
7539// (`|| ManifestError::EtiquetaDuplicate { etiqueta: etiqueta.clone() }`
7540// against the per-`:etiquetas` `&String` loop head) and one at
7541// [`Caixa::validate_autores`]'s per-entry [`crate::render::insert_first_seen`]
7542// dedup closure (`|| ManifestError::AutorDuplicate
7543// { autor: autor.clone() }` against the per-`:autores` `&String` loop
7544// head) — opened the identical `ManifestError::<Variant>Duplicate
7545// { <field>: <val>.clone() }` three-line struct-literal against a
7546// caller-side `&String`, the exact "same block re-inlined at every
7547// consumer" shape the PRIME DIRECTIVE names as a bug. The two variants
7548// share one `{ <field>: String }` shape, so the fold routes each wire-up
7549// site through one dispatch per typed variant.
7550//
7551// The macro below generates one `#[must_use]` inherent constructor per
7552// variant of shape `fn <ctor>(<field>: &str) -> ManifestError`, so every
7553// wire-up site collapses onto one dispatch:
7554// `ManifestError::<ctor>(<&str>)`, byte-equal to the pre-lift
7555// struct-literal on the same `&str` fixture. The uniform one-field
7556// construction (`<field>: <field>.to_string()`) is spelled once — inside
7557// the macro — rather than at every wire-up site. The `<field>: &str`
7558// parameter accepts both `&str` and `&String` (via Deref coercion), so
7559// each existing dedup-closure wire-up threading `<val>.as_str()` — or a
7560// bare `&String` head — through the ctor routes through one dispatch
7561// without a pre-conversion, and the `.clone()` the pre-lift wire-up
7562// carried at the closure body folds into the ctor's canonical
7563// `.to_string()` (byte-equal on the same underlying bytes). Every
7564// constructor is `#[must_use]` so a caller who mistakenly discards the
7565// constructed error trips a compile warning at the wire-up site.
7566//
7567// Every future consumer that wants to construct one of these two
7568// variants outside the current in-crate wire-up sites — a deferred
7569// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission webhook
7570// re-checking one added/renamed `:etiquetas` / `:autores` entry against
7571// the same dedup axis, a future `feira validate --etiquetas` /
7572// `--autores` per-caixa admission verb re-running the same per-entry
7573// dedup gate on demand, a per-lacre overlay resolver rejecting an
7574// author-supplied duplicate `:etiquetas` / `:autores` entry against a
7575// cluster-local snapshot the M4 CR materializer projects, a future
7576// `caixa-registry` per-lacre re-validator at lacre-resolve time
7577// re-checking each declared list against the same dedup predicate — now
7578// reaches each variant through one call rather than re-inlining the
7579// three-line struct-literal in lockstep with the two in-crate wire-up
7580// sites.
7581macro_rules! manifest_field_only_ctors {
7582    ($($ctor:ident => $variant:ident { $field:ident }),* $(,)?) => {
7583        impl ManifestError {
7584            $(
7585                #[doc = concat!(
7586                    "Construct a [`ManifestError::",
7587                    stringify!($variant),
7588                    "`] naming the offending `",
7589                    stringify!($field),
7590                    "` entry. Folds the uniform `Self::",
7591                    stringify!($variant),
7592                    " { ",
7593                    stringify!($field),
7594                    ": ",
7595                    stringify!($field),
7596                    ".to_string() }` one-field struct-literal onto one ",
7597                    "substrate primitive so every wire-up on this variant ",
7598                    "reads through one dispatch rather than the pre-lift ",
7599                    "three-line open-coded struct-literal block. The `",
7600                    stringify!($field),
7601                    ": &str` parameter accepts both `&str` and `&String` ",
7602                    "via Deref coercion."
7603                )]
7604                #[must_use]
7605                pub fn $ctor($field: &str) -> Self {
7606                    Self::$variant {
7607                        $field: $field.to_string(),
7608                    }
7609                }
7610            )*
7611        }
7612    };
7613}
7614
7615manifest_field_only_ctors! {
7616    etiqueta_duplicate => EtiquetaDuplicate { etiqueta },
7617    autor_duplicate => AutorDuplicate { autor },
7618}
7619
7620#[cfg(test)]
7621mod tests {
7622    use super::*;
7623
7624    #[test]
7625    fn template_round_trips() {
7626        let src = Caixa::template("demo");
7627        let c = Caixa::from_lisp(&src).expect("template must parse");
7628        assert_eq!(c.nome, "demo");
7629        assert_eq!(c.versao, "0.1.0");
7630        assert_eq!(c.kind, CaixaKind::Biblioteca);
7631        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
7632        assert!(c.deps.is_empty());
7633        assert!(c.deps_dev.is_empty());
7634    }
7635
7636    #[test]
7637    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
7638        // Fail-before-pass-after pin on [`Caixa::nome`] +
7639        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
7640        // accessor projects the top-level manifest's per-`:nome` /
7641        // per-`:versao` [`String`] storage through the `pub const fn`
7642        // [`String::as_str`] (const-stable since Rust 1.87, well within
7643        // the workspace MSRV) — any future accidental downgrade to
7644        // non-`const` fails the corresponding `<name>_via_const_fn`
7645        // wrapper at caixa-core build time with E0015 (`cannot call
7646        // non-const method`), strictly stronger than a runtime
7647        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
7648        // scalar-accessor family pins on the sibling `const`-eval-
7649        // surface passes ([`crate::CaixaVersion::as_str`] at the
7650        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
7651        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
7652        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
7653        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
7654        // axis, [`crate::supervisor::ChildSpec::nome`] /
7655        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
7656        // M2 supervisor-tree axis,
7657        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
7658        // upgrade axis, [`crate::dep::Dep::nome`] /
7659        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
7660        // axis, and the per-`:contratos`
7661        // [`crate::aplicacao::WitContract::source`] /
7662        // [`crate::aplicacao::WitContract::destination`] /
7663        // [`crate::aplicacao::WitContract::world_ref`] trio the
7664        // sibling pin at 279823b already anchors).
7665        const fn nome_via_const_fn(c: &Caixa) -> &str {
7666            c.nome()
7667        }
7668        const fn versao_via_const_fn(c: &Caixa) -> &str {
7669            c.versao()
7670        }
7671        let src = Caixa::template("demo");
7672        let c = Caixa::from_lisp(&src).expect("template must parse");
7673        assert_eq!(nome_via_const_fn(&c), c.nome());
7674        assert_eq!(versao_via_const_fn(&c), c.versao());
7675        assert_eq!(c.nome(), "demo");
7676        assert_eq!(c.versao(), "0.1.0");
7677    }
7678
7679    #[test]
7680    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
7681        // Fail-before-pass-after pin on the five per-`Caixa`
7682        // `Option<String> → Option<&str>` scalar accessors
7683        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
7684        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
7685        // manifest's optional universal-axis surface, plus
7686        // [`Caixa::restart_window`] on the M2 supervisor-tree
7687        // per-`SupervisorSpec` peer raw-window-string projection axis).
7688        // Each accessor destructures the typed slot's `Option<String>`
7689        // storage through the `match &self.<field> { Some(s) =>
7690        // Some(s.as_str()), None => None }` shape — routing through
7691        // [`String::as_str`] (const-stable since Rust 1.87, well within
7692        // the workspace MSRV) rather than the non-const
7693        // [`Option::as_deref`] the pre-lift bodies carried — and any
7694        // future accidental downgrade to non-`const` fails the
7695        // corresponding `<name>_via_const_fn` wrapper at caixa-core
7696        // build time with E0015 (`cannot call non-const method`),
7697        // strictly stronger than a runtime `assert!` and strictly
7698        // stronger than a module-scope `const _: () = assert!(…)` pin
7699        // (which cannot be formed on a `&Caixa` fixture because the
7700        // type's `String` / `Option<String>` carriers rule out
7701        // `const`-context value construction; the `const fn` wrapper
7702        // is the load-bearing shape that side-steps the destructor-in-
7703        // const restriction on the value axis while still pinning the
7704        // `const`-fn posture on the callee — mirror of the sibling
7705        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7706        // pin's discipline verbatim on the peer non-`Option`
7707        // `String → &str` axis at the same struct).
7708        //
7709        // Peer of the sibling per-M2/M3-slot `Option<String> →
7710        // Option<&str>` accessor family pin
7711        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
7712        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
7713        // [`WitContract::subject`] / [`WitContract::slot`] on the
7714        // per-`:contratos` payload-carrier trio,
7715        // [`Placement::shard_key`] / [`Placement::affinity`] on the
7716        // per-`:placement` optional-scalar pair).
7717        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
7718            c.licenca()
7719        }
7720        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
7721            c.repositorio()
7722        }
7723        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
7724            c.descricao()
7725        }
7726        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
7727            c.edicao()
7728        }
7729        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
7730            c.restart_window()
7731        }
7732        // Sweep both the `Some`-carrying arm (author-declared slot,
7733        // the byte-string projection payload) and the `None`-carrying
7734        // arm (author-omitted slot, the default-path projection) on
7735        // every accessor so the `const fn` wrapper family pins each
7736        // axis's canonical two-arm partition through the same const
7737        // dispatch as the runtime path.
7738        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7739        c1.licenca = Some("MIT".to_string());
7740        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
7741        c1.descricao = Some("demo caixa".to_string());
7742        c1.edicao = Some("2024".to_string());
7743        c1.restart_window = Some("60s".to_string());
7744        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
7745        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
7746        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
7747        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
7748        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
7749        assert_eq!(c1.licenca(), Some("MIT"));
7750        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
7751        assert_eq!(c1.descricao(), Some("demo caixa"));
7752        assert_eq!(c1.edicao(), Some("2024"));
7753        assert_eq!(c1.restart_window(), Some("60s"));
7754        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7755        c2.licenca = None;
7756        c2.repositorio = None;
7757        c2.descricao = None;
7758        c2.edicao = None;
7759        c2.restart_window = None;
7760        assert_eq!(licenca_via_const_fn(&c2), None);
7761        assert_eq!(repositorio_via_const_fn(&c2), None);
7762        assert_eq!(descricao_via_const_fn(&c2), None);
7763        assert_eq!(edicao_via_const_fn(&c2), None);
7764        assert_eq!(restart_window_via_const_fn(&c2), None);
7765    }
7766
7767    #[test]
7768    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
7769        // Fail-before-pass-after pin on the two outer-[`Caixa`]
7770        // `Copy`-return accessors — [`Caixa::kind`] on the required
7771        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
7772        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
7773        // axis. Both accessors project a `Copy`-carrier field
7774        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
7775        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
7776        // `Option<RestartStrategy>: Copy`) by value through a bare
7777        // `self.<field>` field-access — no dispatch, no destructor, no
7778        // heap. Any future accidental downgrade to non-`const` fails
7779        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
7780        // build time with E0015 (`cannot call non-const method`),
7781        // strictly stronger than a runtime `assert!` and strictly
7782        // stronger than a module-scope `const _: () = assert!(…)` pin
7783        // (which cannot be formed on a `&Caixa` fixture because the
7784        // type's `String` / `Vec` / `Option<Composite>` carriers rule
7785        // out `const`-context value construction; the `const fn`
7786        // wrapper is the load-bearing shape that side-steps the
7787        // destructor-in-const restriction on the value axis while still
7788        // pinning the `const`-fn posture on the callee — mirror of the
7789        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7790        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
7791        // pins' discipline verbatim on the peer outer-`Caixa`
7792        // `String → &str` + `Option<String> → Option<&str>` axes at the
7793        // same struct).
7794        //
7795        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
7796        // family on the inner-altitude nested-spec typed-slot
7797        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
7798        // + [`crate::supervisor::ChildSpec::restart`] on the M2
7799        // supervisor-tree axis (pinned at 152c868), and
7800        // [`crate::aplicacao::Placement::estrategia`] +
7801        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
7802        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
7803        // unlifted altitude for the `Copy`-return-accessor family.
7804        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
7805            c.kind()
7806        }
7807        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
7808            c.estrategia()
7809        }
7810        // Sweep every arm of both discriminant partitions the accessors
7811        // fan on — every [`CaixaKind`] variant the six-arm required
7812        // discriminant carries (Biblioteca / Binario / Servico /
7813        // Supervisor / Aplicacao / Acao) and both arms of the
7814        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
7815        // (`Some(<strategy>)` on an author-declared supervisor and
7816        // `None` on the author-omitted default arm every non-Supervisor
7817        // caixa carries by `#[serde(default)]`) — so the `const fn`
7818        // wrapper family pins the closed-set partition through the
7819        // same const dispatch as the runtime path.
7820        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7821        c1.kind = CaixaKind::Servico;
7822        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
7823        assert_eq!(kind_via_const_fn(&c1), c1.kind());
7824        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
7825        assert_eq!(c1.kind(), CaixaKind::Servico);
7826        assert_eq!(
7827            c1.estrategia(),
7828            Some(crate::supervisor::RestartStrategy::OneForAll)
7829        );
7830        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7831        c2.kind = CaixaKind::Aplicacao;
7832        c2.estrategia = None;
7833        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
7834        assert_eq!(estrategia_via_const_fn(&c2), None);
7835        // Anchor the remaining discriminant arms so any future
7836        // reordering of [`CaixaKind`]'s six-variant enum surfaces
7837        // through the wrapper dispatch, not just through the direct
7838        // method call.
7839        for kind in [
7840            CaixaKind::Biblioteca,
7841            CaixaKind::Binario,
7842            CaixaKind::Servico,
7843            CaixaKind::Supervisor,
7844            CaixaKind::Aplicacao,
7845            CaixaKind::Acao,
7846        ] {
7847            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7848            c.kind = kind;
7849            assert_eq!(kind_via_const_fn(&c), kind);
7850        }
7851    }
7852
7853    #[test]
7854    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
7855        // Fail-before-pass-after pin on the five outer-[`Caixa`]
7856        // `Vec<String> → &[String]` slice-return accessors on the
7857        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
7858        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
7859        // Each body is a bare `self.<field>.as_slice()` dispatch through
7860        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7861        // the workspace MSRV). Any future accidental downgrade to
7862        // non-`const` fails the corresponding `<name>_via_const_fn`
7863        // wrapper at caixa-core build time with E0015 (`cannot call
7864        // non-const method`) — mirror of the sibling
7865        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
7866        // discipline on the peer outer-`Caixa` `Copy`-return accessor
7867        // axis, and peer of the sibling composite-carrier slice-return
7868        // pin below on the peer outer-`Caixa` composite-slice axis.
7869        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
7870            c.autores()
7871        }
7872        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
7873            c.etiquetas()
7874        }
7875        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
7876            c.bibliotecas()
7877        }
7878        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
7879            c.exe()
7880        }
7881        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
7882            c.servicos()
7883        }
7884        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
7885        // `servicos` — the template's `Vec::new()` default) and the
7886        // populated arm (mutated below) on every accessor so the
7887        // `const fn` wrapper family pins each axis's two-arm partition
7888        // through the same const dispatch as the runtime path.
7889        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
7890        // so that arm's "empty" fixture is the populated arm the
7891        // mutation sweep covers.
7892        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7893        assert!(autores_via_const_fn(&c_empty).is_empty());
7894        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
7895        assert!(exe_via_const_fn(&c_empty).is_empty());
7896        assert!(servicos_via_const_fn(&c_empty).is_empty());
7897        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7898        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
7899        c_full.etiquetas = vec!["compounding".to_string()];
7900        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
7901        c_full.exe = vec!["exe/cli.lisp".to_string()];
7902        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
7903        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
7904        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
7905        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
7906        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
7907        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
7908        assert_eq!(
7909            bibliotecas_via_const_fn(&c_full),
7910            &["lib/one.lisp", "lib/two.lisp"]
7911        );
7912        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
7913        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
7914        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
7915        assert_eq!(
7916            servicos_via_const_fn(&c_full),
7917            &["servicos/one.computeunit.yaml"]
7918        );
7919    }
7920
7921    #[test]
7922    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
7923        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
7924        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
7925        // / [`Caixa::deps_dev`] on the dep-graph axis,
7926        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
7927        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
7928        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
7929        // bare `self.<field>.as_slice()` dispatch through
7930        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7931        // the workspace MSRV) — peer of the sibling `String`-payload
7932        // slice-return pin above on the peer outer-`Caixa` universal-
7933        // axis surface, and peer of the sibling inner-composite-
7934        // altitude reference-return pin family
7935        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
7936        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
7937        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
7938        // (all pinned at 0b23e0f).
7939        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
7940            c.deps()
7941        }
7942        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
7943            c.deps_dev()
7944        }
7945        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
7946            c.upgrade_from()
7947        }
7948        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
7949            c.children()
7950        }
7951        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
7952            c.membros()
7953        }
7954        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
7955            c.contratos()
7956        }
7957        // Empty-arm sweep on all six composite-carrier axes — every
7958        // `Caixa::template` starts with `Vec::new()` on each.
7959        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7960        assert!(deps_via_const_fn(&c_empty).is_empty());
7961        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
7962        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
7963        assert!(children_via_const_fn(&c_empty).is_empty());
7964        assert!(membros_via_const_fn(&c_empty).is_empty());
7965        assert!(contratos_via_const_fn(&c_empty).is_empty());
7966        // Populate `:membros` / `:contratos` directly via struct literals
7967        // — the parser-side validation path fans on `:kind`-gated cross-
7968        // slot invariants irrelevant to the accessor dispatch under test.
7969        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7970        c_full.membros = vec![
7971            crate::aplicacao::Membro {
7972                caixa: "demo-a".to_string(),
7973                versao: "^0.1.0".to_string(),
7974            },
7975            crate::aplicacao::Membro {
7976                caixa: "demo-b".to_string(),
7977                versao: "^0.2.0".to_string(),
7978            },
7979        ];
7980        c_full.contratos = vec![crate::aplicacao::WitContract {
7981            de: "demo-a".to_string(),
7982            para: "demo-b".to_string(),
7983            wit: "wasi:http/proxy".to_string(),
7984            endpoint: Some("/edge".to_string()),
7985            subject: None,
7986            slot: None,
7987        }];
7988        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
7989        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
7990        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
7991        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
7992        // Alias-borrow check on the four remaining composite-carrier
7993        // slice-return arms — the wrapper's return borrow must alias the
7994        // caller's borrow so any future accessor re-routing that skips
7995        // the storage field surfaces through the assertion.
7996        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
7997        assert!(std::ptr::eq(
7998            deps_dev_via_const_fn(&c_full),
7999            c_full.deps_dev()
8000        ));
8001        assert!(std::ptr::eq(
8002            upgrade_from_via_const_fn(&c_full),
8003            c_full.upgrade_from()
8004        ));
8005        assert!(std::ptr::eq(
8006            children_via_const_fn(&c_full),
8007            c_full.children()
8008        ));
8009    }
8010
8011    #[test]
8012    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
8013        // Fail-before-pass-after pin on the six outer-[`Caixa`]
8014        // `Option<Composite> → Option<&Composite>` reference-return
8015        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
8016        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
8017        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
8018        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
8019        // Each body is a bare `self.<field>.as_ref()` dispatch through
8020        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
8021        // the workspace MSRV of 1.89). Any future accidental downgrade
8022        // to non-`const` fails the corresponding `<name>_via_const_fn`
8023        // wrapper at caixa-core build time with E0015 (`cannot call
8024        // non-const method`), strictly stronger than a runtime `assert!`
8025        // and strictly stronger than a module-scope `const _: () =
8026        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
8027        // because the type's `String` / `Vec` / `Option<Composite>`
8028        // carriers rule out `const`-context value construction; the
8029        // `const fn` wrapper is the load-bearing shape that side-steps
8030        // the destructor-in-const restriction on the value axis while
8031        // still pinning the `const`-fn posture on the callee — mirror
8032        // of the sibling
8033        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
8034        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
8035        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
8036        // pins' discipline verbatim on the peer outer-`Caixa` axes at
8037        // the same struct).
8038        //
8039        // Closes the outer-`Caixa` `Option<&Composite>` composite-
8040        // reference-return sub-family — the last unlifted altitude on
8041        // the outer-`Caixa` accessor-family const-eval surface after
8042        // the sibling `Copy`-return / universal-axis-`&str` /
8043        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
8044        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
8045        // 231a968 (the last of these pins the `Vec<T> → &[T]`
8046        // composite-slice arm the six accessors here close as their
8047        // `Option<Composite> → Option<&Composite>` peer). Peer of the
8048        // sibling inner-altitude nested-spec composite-reference-return
8049        // pin family — [`crate::AplicacaoSpec::politicas`] /
8050        // [`crate::AplicacaoSpec::placement`] /
8051        // [`crate::AplicacaoSpec::entrada`] on the inner
8052        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
8053        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
8054        // the same shape so both altitudes of the reference-return
8055        // discipline (per-`Caixa` outer-slot presence + per-
8056        // `AplicacaoSpec` inner-slot presence) route through one typed
8057        // const dispatch on the substrate primitive.
8058        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
8059            c.limits()
8060        }
8061        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
8062            c.behavior()
8063        }
8064        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
8065            c.politicas()
8066        }
8067        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
8068            c.placement()
8069        }
8070        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
8071            c.entrada()
8072        }
8073        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
8074            c.ci()
8075        }
8076        // Both-arm sweep on every accessor: the `None` author-omitted
8077        // arm (template default — no M2/M3/CI slot declared) and the
8078        // `Some(<composite>)` authored arm (mutated below via struct-
8079        // literal seeds, side-stepping the parser-side `:kind`-gated
8080        // cross-slot invariants irrelevant to the accessor dispatch
8081        // under test). Both arms route through the `const fn` wrapper
8082        // family so the two-arm `Option` partition is pinned through
8083        // the same const dispatch as the runtime path.
8084        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
8085        assert!(limits_via_const_fn(&c_empty).is_none());
8086        assert!(behavior_via_const_fn(&c_empty).is_none());
8087        assert!(politicas_via_const_fn(&c_empty).is_none());
8088        assert!(placement_via_const_fn(&c_empty).is_none());
8089        assert!(entrada_via_const_fn(&c_empty).is_none());
8090        assert!(ci_via_const_fn(&c_empty).is_none());
8091        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
8092        c_full.limits = Some(LimitsSpec::default());
8093        c_full.behavior = Some(crate::BehaviorSpec::default());
8094        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
8095        c_full.placement = Some(crate::aplicacao::Placement::default());
8096        c_full.entrada = Some(crate::aplicacao::Entrada {
8097            host: "demo.quero.cloud".to_string(),
8098            para: "demo".to_string(),
8099            paths: Vec::new(),
8100            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
8101        });
8102        c_full.ci = Some(canteiro_types::CiRun {
8103            workspace: "pleme-io".into(),
8104            repo: "caixa".into(),
8105            nodes: vec![],
8106        });
8107        assert!(limits_via_const_fn(&c_full).is_some());
8108        assert!(behavior_via_const_fn(&c_full).is_some());
8109        assert!(politicas_via_const_fn(&c_full).is_some());
8110        assert!(placement_via_const_fn(&c_full).is_some());
8111        assert!(entrada_via_const_fn(&c_full).is_some());
8112        assert!(ci_via_const_fn(&c_full).is_some());
8113        // Alias-borrow check on every arm: the wrapper's inner-`Option`
8114        // reference must alias the caller's borrow so any future accessor
8115        // re-routing that skips the storage field surfaces through the
8116        // assertion.
8117        assert!(std::ptr::eq(
8118            limits_via_const_fn(&c_full).unwrap(),
8119            c_full.limits().unwrap()
8120        ));
8121        assert!(std::ptr::eq(
8122            behavior_via_const_fn(&c_full).unwrap(),
8123            c_full.behavior().unwrap()
8124        ));
8125        assert!(std::ptr::eq(
8126            politicas_via_const_fn(&c_full).unwrap(),
8127            c_full.politicas().unwrap()
8128        ));
8129        assert!(std::ptr::eq(
8130            placement_via_const_fn(&c_full).unwrap(),
8131            c_full.placement().unwrap()
8132        ));
8133        assert!(std::ptr::eq(
8134            entrada_via_const_fn(&c_full).unwrap(),
8135            c_full.entrada().unwrap()
8136        ));
8137        assert!(std::ptr::eq(
8138            ci_via_const_fn(&c_full).unwrap(),
8139            c_full.ci().unwrap()
8140        ));
8141    }
8142
8143    #[test]
8144    fn register_populates_registry() {
8145        Caixa::register().expect("first register call in this test process must succeed");
8146        let kws = tatara_lisp::domain::registered_keywords();
8147        assert!(kws.contains(&"defcaixa"));
8148    }
8149
8150    #[test]
8151    fn to_lisp_round_trips() {
8152        let src = Caixa::template("demo");
8153        let c1 = Caixa::from_lisp(&src).unwrap();
8154        let emitted = c1.to_lisp();
8155        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
8156        assert_eq!(c1, c2);
8157    }
8158
8159    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
8160    //
8161    // The compounding pin: the variant stores only the typed
8162    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
8163    // (canonical keyword, description, consumer) routes through the enum's
8164    // own accessors at Display time. Prior to that closure the variant
8165    // carried each accessor's return value as a stored `&'static str`
8166    // snapshot alongside `dialeto`; a caller could construct the variant
8167    // with a snapshot that drifted from what `dialeto`'s accessors would
8168    // return, and every downstream user-facing projection would silently
8169    // disagree with the classification. Storing only the axis makes the
8170    // drift structurally impossible.
8171
8172    #[test]
8173    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
8174        // Single-field construction is the whole compounding shape — a
8175        // future re-introduction of a snapshot field (a `palavra_canonica:
8176        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
8177        // would re-open the drift surface and this construction would fail
8178        // to compile with "missing field" until every snapshot was seeded
8179        // at the call site again. The compile-time guarantee is the
8180        // invariant; the assertion below only witnesses that the
8181        // construction is well-formed after the closure.
8182        let err = LeituraError::DialetoEstrangeiro {
8183            dialeto: crate::dialeto::CaixaDialeto::Molde,
8184        };
8185        assert!(matches!(
8186            err,
8187            LeituraError::DialetoEstrangeiro {
8188                dialeto: crate::dialeto::CaixaDialeto::Molde,
8189            }
8190        ));
8191    }
8192
8193    #[test]
8194    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
8195        // For every foreign-dialect classification the variant surfaces —
8196        // [`crate::dialeto::CaixaDialeto::Molde`] and
8197        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
8198        // variants [`Caixa::from_lisp`] raises this error for — the
8199        // rendered [`std::fmt::Display`] byte-string must interpolate each
8200        // typed accessor's return verbatim. A future re-introduction of a
8201        // stored `&'static str` snapshot alongside `dialeto` that Display
8202        // read instead of the accessor would fail this pin as soon as the
8203        // two disagreed; a future accessor rebrand (a per-dialect
8204        // consumer rename, a canonical-keyword shift once the substrate
8205        // migration named in [`crate::dialeto`] completes) reaches every
8206        // consumer through one typed dispatch and this pin verifies the
8207        // display path is one of them.
8208        for d in [
8209            crate::dialeto::CaixaDialeto::Molde,
8210            crate::dialeto::CaixaDialeto::MoldePosicional,
8211        ] {
8212            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
8213            assert!(
8214                rendered.contains(d.palavra_canonica()),
8215                "Display must interpolate `dialeto.palavra_canonica()` \
8216                 verbatim — a stored snapshot would silently drift from \
8217                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
8218            );
8219            assert!(
8220                rendered.contains(d.descricao()),
8221                "Display must interpolate `dialeto.descricao()` verbatim. \
8222                 dialect: {d}, rendered: {rendered:?}"
8223            );
8224            assert!(
8225                rendered.contains(d.consumidor()),
8226                "Display must interpolate `dialeto.consumidor()` verbatim. \
8227                 dialect: {d}, rendered: {rendered:?}"
8228            );
8229        }
8230    }
8231
8232    #[test]
8233    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
8234        // The end-to-end pin the compounding closure defends: a
8235        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
8236        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
8237        // rendered Display byte-string names the Molde accessors'
8238        // returns verbatim. Any future path that constructed the variant
8239        // with a mismatched snapshot (a stored `palavra_canonica:
8240        // "defcaixa"` on a `Molde` classification) would land Display
8241        // pointing at `defcaixa` while the typed axis said `Molde` — the
8242        // exact drift the closure removes.
8243        let src = r#"
8244          (defcaixa
8245            :name "x"
8246            :kind :Biblioteca
8247            :ecosystem :rust-single-crate
8248            :package {:name "x" :version "0.1.0"})
8249        "#;
8250        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
8251        match err {
8252            LeituraError::DialetoEstrangeiro { dialeto } => {
8253                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
8254                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
8255                assert!(rendered.contains(dialeto.palavra_canonica()));
8256                assert!(rendered.contains(dialeto.consumidor()));
8257                assert!(rendered.contains(dialeto.descricao()));
8258            }
8259            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
8260        }
8261    }
8262
8263    #[test]
8264    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
8265        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
8266        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
8267        // positional-arity `defmolde` form written under a `(defcaixa …)`
8268        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
8269        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
8270        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
8271        // so no test exercised the positional-arity path through
8272        // `Caixa::from_lisp` specifically; the sibling
8273        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
8274        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
8275        // two arms route through the lifted
8276        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
8277        // typed predicate — the same predicate the pre-lift `foreign =>`
8278        // wildcard resolved to today — and this pin makes the
8279        // positional-arity arm's byte-shape at the gate explicit rather
8280        // than implied by wildcard-absorption. A future regression that
8281        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
8282        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
8283        // from the two-arity closure) would fail this pin at caixa-core
8284        // test time rather than surfacing far from the change as a
8285        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
8286        // …)` silently parsing past the derive.
8287        let src = r#"
8288          (defcaixa todoku-go
8289            :kind :Biblioteca
8290            :ecosystem :go
8291            :package {:name "todoku-go" :version "0.3.0"})
8292        "#;
8293        let err =
8294            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
8295        match err {
8296            LeituraError::DialetoEstrangeiro { dialeto } => {
8297                assert_eq!(
8298                    dialeto,
8299                    crate::dialeto::CaixaDialeto::MoldePosicional,
8300                    "DialetoEstrangeiro must carry the MoldePosicional \
8301                     variant verbatim — the positional-arity `defmolde` \
8302                     form under a `(defcaixa …)` head is the \
8303                     `MoldePosicional` arm's canonical byte-shape"
8304                );
8305                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
8306                assert!(
8307                    rendered.contains(dialeto.palavra_canonica()),
8308                    "Display must interpolate `dialeto.palavra_canonica()` \
8309                     verbatim on the MoldePosicional arm; rendered: \
8310                     {rendered:?}"
8311                );
8312                assert!(
8313                    rendered.contains(dialeto.consumidor()),
8314                    "Display must interpolate `dialeto.consumidor()` \
8315                     verbatim on the MoldePosicional arm; rendered: \
8316                     {rendered:?}"
8317                );
8318                assert!(
8319                    rendered.contains(dialeto.descricao()),
8320                    "Display must interpolate `dialeto.descricao()` \
8321                     verbatim on the MoldePosicional arm; rendered: \
8322                     {rendered:?}"
8323                );
8324            }
8325            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
8326        }
8327    }
8328
8329    #[test]
8330    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
8331        // Load-bearing byte-parity pin: for every arm in
8332        // [`crate::dialeto::CaixaDialeto::ALL`], the
8333        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
8334        // partition must agree with the lifted
8335        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
8336        // typed predicate — i.e. from_lisp raises
8337        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
8338        // `d.is_molde_family()` returns `true`, and does NOT raise
8339        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
8340        // predicate returns `false` (the arm's source falls through to
8341        // the derive — parses cleanly on
8342        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
8343        // [`LeituraError::Leitura`] on
8344        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
8345        //
8346        // Pre-lift the gate hand-rolled a three-arm match
8347        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
8348        // whose `foreign =>` wildcard expressed no compile-time link
8349        // back to the substrate primitive's arm-family; a future fifth
8350        // dialect the [`crate::dialeto`] module doc's "third dialect"
8351        // hazard actualises would fall silently onto the wildcard
8352        // regardless of whether it belonged to the `defmolde` family or
8353        // to a distinct `defcaixa`-family. Post-lift the partition
8354        // resolves through
8355        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
8356        // typed dispatch, and this pin refuses any future regression
8357        // that silently split the from_lisp partition from the typed
8358        // predicate — the two paths now migrate as one on any future
8359        // arm addition.
8360        //
8361        // Sibling in shape to the peer
8362        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
8363        // (e9d2315) that pins the same byte-parity between
8364        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
8365        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
8366        // `== "defmolde"` classifier — extends the discipline from the
8367        // two paths within the [`crate::dialeto`] primitive onto the
8368        // third external consumer of the `defmolde`-family partition
8369        // (the [`Caixa::from_lisp`] gate that raises
8370        // [`LeituraError::DialetoEstrangeiro`]).
8371        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
8372            (
8373                crate::dialeto::CaixaDialeto::Pacote,
8374                r#"
8375                  (defcaixa
8376                    :nome   "checkout"
8377                    :versao "0.1.0"
8378                    :kind   Biblioteca
8379                    :edicao "2026"
8380                    :descricao "canonical Pacote source"
8381                    :autores ()
8382                    :etiquetas ()
8383                    :deps ()
8384                    :deps-dev ()
8385                    :bibliotecas ("lib/checkout.lisp"))
8386                "#,
8387            ),
8388            (
8389                crate::dialeto::CaixaDialeto::Molde,
8390                r#"
8391                  (defcaixa
8392                    :name "base64"
8393                    :kind :Biblioteca
8394                    :ecosystem :rust-single-crate
8395                    :package {:name "base64" :version "0.22.1"}
8396                    :workflows [:auto-release])
8397                "#,
8398            ),
8399            (
8400                crate::dialeto::CaixaDialeto::MoldePosicional,
8401                r#"
8402                  (defcaixa todoku-go
8403                    :kind :Biblioteca
8404                    :ecosystem :go
8405                    :package {:name "todoku-go" :version "0.3.0"})
8406                "#,
8407            ),
8408            (
8409                crate::dialeto::CaixaDialeto::Desconhecido,
8410                r#"(defcaixa :licenca "MIT")"#,
8411            ),
8412        ];
8413
8414        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
8415        // must appear in the fixture table so the pin's arm-set stays
8416        // synchronised with the enum's arm-set. Fails at test time if a
8417        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
8418        // (with a corresponding `is_molde_family` return) forgot to
8419        // extend this fixture table with a canonical source for the new
8420        // arm — the pin cannot cover an arm it has no source for.
8421        for &expected in crate::dialeto::CaixaDialeto::ALL {
8422            assert!(
8423                fixtures.iter().any(|(d, _)| *d == expected),
8424                "fixture table must carry a canonical source for every \
8425                 CaixaDialeto arm; missing: {expected:?}"
8426            );
8427        }
8428
8429        for &(expected_dialect, src) in fixtures {
8430            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
8431                panic!(
8432                    "fixture source for {expected_dialect:?} must classify \
8433                     cleanly, got err: {err:?}"
8434                )
8435            });
8436            assert_eq!(
8437                classified, expected_dialect,
8438                "fixture source for {expected_dialect:?} must classify as \
8439                 {expected_dialect:?} (drift here defeats the byte-parity \
8440                 pin below — a source labelled for one arm but classifying \
8441                 as another would silently satisfy or violate the pin for \
8442                 the wrong reason)"
8443            );
8444
8445            let outcome = Caixa::from_lisp(src);
8446            match (expected_dialect.is_molde_family(), &outcome) {
8447                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
8448                    assert_eq!(
8449                        *dialeto, expected_dialect,
8450                        "DialetoEstrangeiro must carry the same typed arm \
8451                         the classifier returned — a drift here would let \
8452                         from_lisp raise the error while pointing at the \
8453                         wrong dialect (e.g. rejecting a \
8454                         MoldePosicional source as Molde). arm: \
8455                         {expected_dialect:?}"
8456                    );
8457                }
8458                (true, other) => panic!(
8459                    "arm {expected_dialect:?} has is_molde_family() = true \
8460                     so from_lisp must raise DialetoEstrangeiro carrying \
8461                     {expected_dialect:?}; got: {other:?}"
8462                ),
8463                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
8464                    "arm {expected_dialect:?} has is_molde_family() = false \
8465                     so from_lisp must NOT raise DialetoEstrangeiro; got \
8466                     one carrying: {dialeto:?}. This means the typed \
8467                     predicate and the from_lisp partition disagree on \
8468                     this arm — exactly the drift this pin refuses."
8469                ),
8470                (false, _) => {
8471                    // A non-molde arm's source falls through to the
8472                    // derive: Pacote sources parse to Ok(_); Desconhecido
8473                    // sources surface as LeituraError::Leitura from the
8474                    // derive's own unknown-keyword rejection. Either
8475                    // shape is acceptable here — the pin's promise is
8476                    // narrower: "no DialetoEstrangeiro on
8477                    // is_molde_family() == false".
8478                }
8479            }
8480        }
8481    }
8482
8483    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
8484
8485    #[test]
8486    fn limits_round_trip_via_json() {
8487        use crate::LimitsSpec;
8488        use std::time::Duration;
8489        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8490        c.limits = Some(LimitsSpec {
8491            memory: Some(64 * 1024 * 1024),
8492            fuel: Some(1_000_000),
8493            wall_clock: Some(Duration::from_secs(30)),
8494            cpu: Some(500),
8495        });
8496        let json = serde_json::to_string(&c).unwrap();
8497        assert!(json.contains("\"limits\""));
8498        assert!(json.contains("\"64MiB\""));
8499        assert!(json.contains("\"30s\""));
8500        assert!(json.contains("\"500m\""));
8501        let back: Caixa = serde_json::from_str(&json).unwrap();
8502        assert_eq!(c.limits, back.limits);
8503    }
8504
8505    #[test]
8506    fn behavior_round_trip_via_json() {
8507        use crate::BehaviorSpec;
8508        use std::path::PathBuf;
8509        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8510        c.behavior = Some(BehaviorSpec {
8511            on_init: Some(PathBuf::from("lib/init.lisp")),
8512            on_call: Some(PathBuf::from("lib/handlers.lisp")),
8513            ..Default::default()
8514        });
8515        let json = serde_json::to_string(&c).unwrap();
8516        let back: Caixa = serde_json::from_str(&json).unwrap();
8517        assert_eq!(c.behavior, back.behavior);
8518    }
8519
8520    #[test]
8521    fn upgrade_from_round_trip_via_json() {
8522        use crate::{UpgradeFromEntry, UpgradeInstruction};
8523        use std::path::PathBuf;
8524        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8525        c.upgrade_from = vec![UpgradeFromEntry {
8526            from: "0.1.0".into(),
8527            instructions: vec![
8528                UpgradeInstruction::LoadModule {
8529                    module: "demo".into(),
8530                },
8531                UpgradeInstruction::StateChange {
8532                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
8533                },
8534                UpgradeInstruction::SoftPurge {
8535                    module: "demo-old".into(),
8536                },
8537            ],
8538        }];
8539        let json = serde_json::to_string(&c).unwrap();
8540        let back: Caixa = serde_json::from_str(&json).unwrap();
8541        assert_eq!(c.upgrade_from, back.upgrade_from);
8542    }
8543
8544    #[test]
8545    fn supervisor_view_returns_typed_shape() {
8546        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8547        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8548        c.kind = CaixaKind::Supervisor;
8549        c.bibliotecas.clear();
8550        c.estrategia = Some(RestartStrategy::OneForOne);
8551        c.max_restarts = Some(5);
8552        c.restart_window = Some("60s".into());
8553        c.children = vec![ChildSpec {
8554            caixa: "worker".into(),
8555            versao: "^0.1".into(),
8556            restart: RestartPolicy::Permanent,
8557        }];
8558        let view = c.supervisor_view().expect("Supervisor kind has a view");
8559        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
8560        assert_eq!(view.max_restarts, 5);
8561        assert_eq!(
8562            view.restart_window,
8563            Some(std::time::Duration::from_secs(60))
8564        );
8565        assert_eq!(view.children.len(), 1);
8566        view.validate().unwrap();
8567    }
8568
8569    #[test]
8570    fn supervisor_view_none_for_non_supervisor_kinds() {
8571        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8572        assert!(c.supervisor_view().is_none());
8573    }
8574
8575    #[test]
8576    fn declared_mesh_slots_empty_for_bare_caixa() {
8577        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8578        assert!(c.declared_mesh_slots().is_empty());
8579    }
8580
8581    #[test]
8582    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
8583        use crate::{Entrada, Membro};
8584        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8585        // Set a non-adjacent pair (:membros + :entrada) to pin that the
8586        // canonical declaration order is preserved regardless of which
8587        // subset is populated.
8588        c.membros = vec![Membro {
8589            caixa: "a".into(),
8590            versao: "^0.1".into(),
8591        }];
8592        c.entrada = Some(Entrada {
8593            host: "x.example.com".into(),
8594            para: "a".into(),
8595            paths: vec![],
8596            port: 8080,
8597        });
8598        assert_eq!(
8599            c.declared_mesh_slots(),
8600            vec![
8601                crate::render::M3_AUTHOR_KEY_MEMBROS,
8602                crate::render::M3_AUTHOR_KEY_ENTRADA,
8603            ]
8604        );
8605    }
8606
8607    #[test]
8608    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8609        // Scalar-value pin: the five author-facing kebab-case labels the
8610        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
8611        // mesh slot axis, one arm per typed slot. Mirrors the peer
8612        // scalar-value pin the sibling
8613        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8614        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8615        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
8616        // carry (f49c8b0), so both altitudes of the typed-slot algebra
8617        // (per-Servico M2 + per-Aplicacao M3) share the same
8618        // "one canonical byte-string per arm" discipline. A future
8619        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
8620        // `:politicas` → `:policies`, `:placement` → `:distribution`,
8621        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
8622        // and every consumer that reaches for the label picks it up at
8623        // build time rather than at runtime as a downstream mismatch.
8624        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
8625        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
8626        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
8627        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
8628        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
8629    }
8630
8631    #[test]
8632    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
8633        // Production-through-const pin: the five per-arm labels the
8634        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
8635        // `Vec` route through the lifted
8636        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
8637        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
8638        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
8639        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
8640        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
8641        // declaration order. A future re-order or drift at the tagger
8642        // (a rename that reaches the tagger but not the const, or vice
8643        // versa) surfaces here at build time rather than at runtime as
8644        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
8645        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8646        // commit. Mirror of the peer
8647        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8648        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
8649        // axis.
8650        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
8651        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8652        c.membros = vec![Membro {
8653            caixa: "a".into(),
8654            versao: "^0.1".into(),
8655        }];
8656        c.contratos = vec![WitContract {
8657            de: "a".into(),
8658            para: "a".into(),
8659            wit: "wasi:http/proxy".into(),
8660            endpoint: Some("/x".into()),
8661            subject: None,
8662            slot: None,
8663        }];
8664        c.politicas = Some(MeshPolicy::default());
8665        c.placement = Some(Placement {
8666            estrategia: PlacementStrategy::Replicated,
8667            clusters: vec!["rio".into()],
8668            affinity: None,
8669            shard_key: None,
8670        });
8671        c.entrada = Some(Entrada {
8672            host: "x.example.com".into(),
8673            para: "a".into(),
8674            paths: vec![],
8675            port: 8080,
8676        });
8677        assert_eq!(
8678            c.declared_mesh_slots(),
8679            vec![
8680                crate::render::M3_AUTHOR_KEY_MEMBROS,
8681                crate::render::M3_AUTHOR_KEY_CONTRATOS,
8682                crate::render::M3_AUTHOR_KEY_POLITICAS,
8683                crate::render::M3_AUTHOR_KEY_PLACEMENT,
8684                crate::render::M3_AUTHOR_KEY_ENTRADA,
8685            ]
8686        );
8687    }
8688
8689    #[test]
8690    fn declared_supervisor_slots_empty_for_bare_caixa() {
8691        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8692        assert!(c.declared_supervisor_slots().is_empty());
8693    }
8694
8695    #[test]
8696    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
8697        use crate::RestartStrategy;
8698        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8699        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
8700        // that the canonical declaration order is preserved regardless
8701        // of which subset is populated.
8702        c.estrategia = Some(RestartStrategy::OneForOne);
8703        c.restart_window = Some("60s".into());
8704        assert_eq!(
8705            c.declared_supervisor_slots(),
8706            vec![
8707                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8708                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8709            ]
8710        );
8711    }
8712
8713    #[test]
8714    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8715        // Scalar-value pin: the four author-facing kebab-case labels the
8716        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
8717        // supervision-tree slot axis, one arm per typed slot. Mirrors the
8718        // peer scalar-value pins the sibling
8719        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
8720        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
8721        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
8722        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
8723        // top-level M3 slot consts carry, so all three kind-scoped
8724        // typed-slot-family author-facing-label axes route through one
8725        // canonical per-arm declaration. A future rebrand
8726        // (`:estrategia` → `:strategy` for English uniformity,
8727        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
8728        // `MaxIntensity` name, `:restart-window` → `:period` matching
8729        // OTP's `Period` name, `:children` → `:workers` matching Elixir
8730        // idiom) lands as an edit to exactly one const, and every
8731        // consumer that reaches for the label picks it up at build time
8732        // rather than at runtime as a downstream mismatch.
8733        assert_eq!(
8734            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8735            ":estrategia"
8736        );
8737        assert_eq!(
8738            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8739            ":max-restarts"
8740        );
8741        assert_eq!(
8742            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8743            ":restart-window"
8744        );
8745        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
8746    }
8747
8748    #[test]
8749    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
8750        // Production-through-const pin: the four per-arm labels the
8751        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
8752        // return `Vec` route through the lifted
8753        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
8754        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
8755        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
8756        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
8757        // canonical declaration order. A future re-order or drift at the
8758        // tagger (a rename that reaches the tagger but not the const, or
8759        // vice versa) surfaces here at build time rather than at runtime
8760        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
8761        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8762        // commit. Mirror of the peer
8763        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8764        // (f49c8b0) and
8765        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
8766        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
8767        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8768        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8769        c.estrategia = Some(RestartStrategy::OneForOne);
8770        c.max_restarts = Some(5);
8771        c.restart_window = Some("60s".into());
8772        c.children = vec![ChildSpec {
8773            caixa: "worker".into(),
8774            versao: "^0.1".into(),
8775            restart: RestartPolicy::Permanent,
8776        }];
8777        assert_eq!(
8778            c.declared_supervisor_slots(),
8779            vec![
8780                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8781                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8782                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8783                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
8784            ]
8785        );
8786    }
8787
8788    #[test]
8789    fn declared_servico_slots_empty_for_bare_caixa() {
8790        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8791        assert!(c.declared_servico_slots().is_empty());
8792    }
8793
8794    #[test]
8795    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
8796        use crate::{UpgradeFromEntry, UpgradeInstruction};
8797        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8798        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
8799        // the canonical declaration order is preserved regardless of
8800        // which subset is populated.
8801        c.limits = Some(crate::LimitsSpec {
8802            fuel: Some(1_000_000),
8803            ..Default::default()
8804        });
8805        c.upgrade_from = vec![UpgradeFromEntry {
8806            from: "0.1.0".into(),
8807            instructions: vec![UpgradeInstruction::Restart],
8808        }];
8809        assert_eq!(
8810            c.declared_servico_slots(),
8811            vec![
8812                crate::render::M2_AUTHOR_KEY_LIMITS,
8813                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8814            ]
8815        );
8816    }
8817
8818    #[test]
8819    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8820        // Scalar-value pin: the three author-facing kebab-case labels
8821        // the `(defcaixa … :<slot> (…))` surface admits on the M2
8822        // top-level slot axis, one arm per typed slot. Mirrors the peer
8823        // scalar-value pin the sibling renderer-side
8824        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
8825        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
8826        // consts carry, so both halves of the M2 top-level slot dual
8827        // axis (author-facing kebab-case label + renderer-side
8828        // camelCase overlay-container wire key) route through one
8829        // canonical per-arm declaration. A future rebrand
8830        // (`:limits` → `:sandbox` matching Lunatic per-process
8831        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
8832        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
8833        // matching Erlang's verbatim appup name) lands as an edit to
8834        // exactly one const, and every consumer that reaches for the
8835        // label picks it up at build time rather than at runtime as a
8836        // downstream mismatch.
8837        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
8838        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
8839        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
8840    }
8841
8842    #[test]
8843    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
8844        // Production-through-const pin: the three per-arm labels the
8845        // [`Caixa::declared_servico_slots`] tagger pushes onto its
8846        // return `Vec` route through the lifted
8847        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8848        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8849        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
8850        // declaration order. A future re-order or drift at the tagger
8851        // (a rename that reaches the tagger but not the const, or vice
8852        // versa) surfaces here at build time rather than at runtime as
8853        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
8854        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8855        // commit. Mirror of the peer
8856        // [`crate::behavior::BehaviorSpec::declared_slots`] production
8857        // tagger pin (889dc18) on the sibling per-callback axis.
8858        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
8859        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8860        c.limits = Some(crate::LimitsSpec {
8861            fuel: Some(1_000_000),
8862            ..Default::default()
8863        });
8864        c.behavior = Some(BehaviorSpec {
8865            on_init: Some(PathBuf::from("lib/init.lisp")),
8866            ..Default::default()
8867        });
8868        c.upgrade_from = vec![UpgradeFromEntry {
8869            from: "0.1.0".into(),
8870            instructions: vec![UpgradeInstruction::Restart],
8871        }];
8872        assert_eq!(
8873            c.declared_servico_slots(),
8874            vec![
8875                crate::render::M2_AUTHOR_KEY_LIMITS,
8876                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
8877                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8878            ]
8879        );
8880    }
8881
8882    #[test]
8883    fn existing_manifests_unaffected_by_new_optional_slots() {
8884        // Regression test: a caixa.lisp authored before M2 typed slots
8885        // should still parse + serialize cleanly. The bare `defcaixa`
8886        // emitted by `Caixa::template` has none of the new fields.
8887        let src = Caixa::template("legacy");
8888        let c = Caixa::from_lisp(&src).unwrap();
8889        assert!(c.limits.is_none());
8890        assert!(c.behavior.is_none());
8891        assert!(c.upgrade_from.is_empty());
8892        assert!(c.estrategia.is_none());
8893        assert!(c.children.is_empty());
8894
8895        // And to_lisp emits a manifest with the new slots in the
8896        // empty/default state — round-trippable.
8897        let emitted = c.to_lisp();
8898        let back = Caixa::from_lisp(&emitted).unwrap();
8899        assert_eq!(c, back);
8900    }
8901
8902    #[test]
8903    fn validate_deps_accepts_canonical_caixa() {
8904        // Positive control: the bare template — zero deps, zero
8905        // deps_dev — passes the gate trivially. A future axis added to
8906        // `Dep::validate` mustn't regress an empty-deps caixa to a
8907        // build error.
8908        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8909        c.validate_deps().unwrap();
8910    }
8911
8912    #[test]
8913    fn validate_deps_rejects_invalid_versao_in_deps() {
8914        // Fail-before-pass-after pin: a malformed `:deps :versao`
8915        // surfaces at validate_deps() time, not at lacre-resolve time.
8916        // Mirrors `rejects_invalid_membro_versao_requirement` and
8917        // `validate_rejects_invalid_child_versao_requirement` on the
8918        // other two `:versao` axes.
8919        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8920        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
8921        let err = c.validate_deps().unwrap_err();
8922        assert!(
8923            matches!(
8924                err,
8925                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8926                    if nome == "caixa-teia" && versao == "^bad-version"
8927            ),
8928            "got {err:?}"
8929        );
8930    }
8931
8932    #[test]
8933    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
8934        // Parity pin: `:deps-dev` must run through the same per-entry
8935        // validator as `:deps` — a typo in either axis surfaces the
8936        // same diagnostic. Without this leg, `:deps-dev` would be a
8937        // second-class citizen of the typed surface and an author
8938        // could land a build that passes validate_deps but fails at
8939        // `feira lock`-time when the dev-dep is resolved for a test
8940        // build.
8941        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8942        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
8943        let err = c.validate_deps().unwrap_err();
8944        assert!(
8945            matches!(
8946                err,
8947                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8948                    if nome == "tatara-check" && versao == "^^0.1"
8949            ),
8950            "got {err:?}"
8951        );
8952    }
8953
8954    #[test]
8955    fn validate_deps_runs_deps_before_deps_dev() {
8956        // Order pin: when both lists carry typos, the `:deps`
8957        // diagnostic surfaces first. The author's mental model is
8958        // "runtime deps are load-bearing; dev deps are scaffolding";
8959        // surfacing the runtime axis first matches that hierarchy.
8960        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8961        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
8962        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
8963        let err = c.validate_deps().unwrap_err();
8964        assert!(
8965            matches!(
8966                err,
8967                crate::dep::DepError::VersaoInvalid { ref nome, .. }
8968                    if nome == "runtime-dep"
8969            ),
8970            "expected `:deps` typo to surface first, got {err:?}"
8971        );
8972    }
8973
8974    #[test]
8975    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
8976        // Positive control sweep across both lists. Pin every
8977        // canonical Cargo-shaped form so a future tightening of the
8978        // accepted set surfaces here as a test failure (parity with
8979        // `accepts_canonical_membro_versao_forms` and
8980        // `validate_accepts_canonical_child_versao_forms`).
8981        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8982        c.deps = vec![
8983            Dep::simple("caret", "^0.1"),
8984            Dep::simple("tilde", "~0.1.2"),
8985            Dep::simple("exact", "0.1.0"),
8986            Dep::simple("wildcard", "*"),
8987            Dep::simple("multi-range", ">=0.1, <2"),
8988        ];
8989        c.deps_dev = vec![
8990            Dep::simple("dev-caret", "^0.1"),
8991            Dep::simple("dev-wildcard", "*"),
8992        ];
8993        c.validate_deps().unwrap();
8994    }
8995
8996    #[test]
8997    fn validate_deps_diagnostic_carries_offending_dep() {
8998        // Diagnostic-shape pin: the error names the offending entry's
8999        // `:nome` + `:versao` verbatim and carries a non-empty
9000        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
9001        // run can render the diagnostic without re-parsing.
9002        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9003        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
9004        let err = c.validate_deps().unwrap_err();
9005        let crate::dep::DepError::VersaoInvalid {
9006            nome,
9007            versao,
9008            reason,
9009        } = err
9010        else {
9011            panic!("expected VersaoInvalid, got other variant");
9012        };
9013        assert_eq!(nome, "caixa-teia");
9014        assert_eq!(versao, "not-a-req");
9015        assert!(
9016            !reason.is_empty(),
9017            "VersaoInvalid `reason` must carry the parser's wording verbatim"
9018        );
9019    }
9020
9021    #[test]
9022    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
9023        // Cross-axis pin: `validate_deps` walks both :deps and
9024        // :deps-dev through `Dep::validate`, and the new fonte gate
9025        // (`:tag` + `:branch` both set — the canonical "pin drift"
9026        // footgun) must surface from the :deps-dev arm with the
9027        // offending entry's :nome named. Pin the :deps-dev arm
9028        // explicitly so a future shortcut that only walks :deps
9029        // surfaces here as a regression.
9030        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9031        c.deps_dev = vec![Dep {
9032            nome: "dev-only".into(),
9033            versao: "^0.1".into(),
9034            fonte: Some(crate::DepSource::Git {
9035                repo: "github:p/x".into(),
9036                tag: Some("v1".into()),
9037                rev: None,
9038                branch: Some("main".into()),
9039            }),
9040            opcional: false,
9041            caracteristicas: vec![],
9042        }];
9043        let err = c.validate_deps().unwrap_err();
9044        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
9045            panic!("expected FontePinAmbiguous from :deps-dev walk");
9046        };
9047        assert_eq!(nome, "dev-only");
9048        assert!(pins.contains(":tag") && pins.contains(":branch"));
9049    }
9050
9051    #[test]
9052    fn validate_deps_rejects_empty_repo_in_deps() {
9053        // Parity pin on the :deps arm: an empty :repo on the runtime
9054        // deps list surfaces the same FonteRepoEmpty diagnostic the
9055        // dep.rs per-entry tests pin, naming the offending entry.
9056        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9057        c.deps = vec![Dep {
9058            nome: "runtime".into(),
9059            versao: "^0.1".into(),
9060            fonte: Some(crate::DepSource::Git {
9061                repo: String::new(),
9062                tag: Some("v1".into()),
9063                rev: None,
9064                branch: None,
9065            }),
9066            opcional: false,
9067            caracteristicas: vec![],
9068        }];
9069        let err = c.validate_deps().unwrap_err();
9070        assert!(
9071            matches!(
9072                err,
9073                crate::dep::DepError::FonteRepoEmpty { ref nome }
9074                    if nome == "runtime"
9075            ),
9076            "got {err:?}"
9077        );
9078    }
9079
9080    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
9081
9082    #[test]
9083    fn validate_deps_rejects_duplicate_nome_in_deps() {
9084        // Fail-before-pass-after pin: two `:deps` entries naming the same
9085        // caixa carry two `:versao` / `:fonte` / feature triples that the
9086        // caixa-resolver's lacre pipeline collapses (the second silently
9087        // overwrites the first at `concrete_versao`-resolve time). The
9088        // gate surfaces the duplicate at validate-time, naming the
9089        // offending caixa + the list, before the resolver-side silent
9090        // drop. Mirrors the peer typed-graph duplicate gates
9091        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
9092        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9093        c.deps = vec![
9094            Dep::simple("caixa-teia", "^0.1"),
9095            Dep::simple("caixa-teia", "^0.2"),
9096        ];
9097        let err = c.validate_deps().unwrap_err();
9098        assert!(
9099            matches!(
9100                err,
9101                crate::dep::DepError::DuplicateNome { ref nome, list }
9102                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9103            ),
9104            "got {err:?}"
9105        );
9106    }
9107
9108    #[test]
9109    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
9110        // Parity pin: `:deps-dev` runs through the same per-list
9111        // duplicate check as `:deps` — neither axis is a second-class
9112        // citizen of the set-not-multiset discipline.
9113        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9114        c.deps_dev = vec![
9115            Dep::simple("tatara-check", "*"),
9116            Dep::simple("tatara-check", "^0.1"),
9117        ];
9118        let err = c.validate_deps().unwrap_err();
9119        assert!(
9120            matches!(
9121                err,
9122                crate::dep::DepError::DuplicateNome { ref nome, list }
9123                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
9124            ),
9125            "got {err:?}"
9126        );
9127    }
9128
9129    #[test]
9130    fn validate_deps_accepts_cross_list_same_nome() {
9131        // The Cargo `[dependencies]` + `[dev-dependencies]` override
9132        // convention is preserved: a name appearing in *both* lists is
9133        // valid (the dev-pin overrides at test/dev time). Only
9134        // within-list duplicates are structurally incoherent — pin the
9135        // permissive cross-list semantics so a future shortcut that
9136        // collapses the two seen-sets into one surfaces here as a test
9137        // failure.
9138        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9139        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
9140        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
9141        c.validate_deps().unwrap();
9142    }
9143
9144    #[test]
9145    fn validate_deps_accepts_distinct_nome_in_both_lists() {
9146        // Positive control: distinct names within each list pass — the
9147        // gate's identity element on the canonical authoring shape.
9148        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9149        c.deps = vec![
9150            Dep::simple("caixa-teia", "^0.1"),
9151            Dep::simple("pleme-mesh", "*"),
9152        ];
9153        c.deps_dev = vec![
9154            Dep::simple("tatara-check", "*"),
9155            Dep::simple("dev-shim", "^0.1"),
9156        ];
9157        c.validate_deps().unwrap();
9158    }
9159
9160    #[test]
9161    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
9162        // Diagnostic-precedence pin: a malformed `:versao` on the
9163        // duplicating entry surfaces its narrower `VersaoInvalid`
9164        // diagnostic first, before the cross-entry duplicate gate fires
9165        // — the canonical "per-entry shape before cross-entry uniqueness"
9166        // precedence every peer set-not-multiset gate establishes
9167        // (`*_invalid_fires_before_duplicate_check` pins on
9168        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
9169        // `validate_upgrade_from`).
9170        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9171        c.deps = vec![
9172            Dep::simple("caixa-teia", "^0.1"),
9173            Dep::simple("caixa-teia", "^bad-version"),
9174        ];
9175        let err = c.validate_deps().unwrap_err();
9176        assert!(
9177            matches!(
9178                err,
9179                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
9180                    if nome == "caixa-teia" && versao == "^bad-version"
9181            ),
9182            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
9183        );
9184    }
9185
9186    #[test]
9187    fn validate_deps_duplicate_diagnostic_names_first_collision() {
9188        // First-collision determinism pin: with three entries naming the
9189        // same caixa, the first colliding pair surfaces — not the last.
9190        // Mirrors the peer first-collision posture on every
9191        // duplicate-target gate
9192        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
9193        // — the second entry is the first collision; this gate uses the
9194        // same shape: the second entry's `:nome` lands in the diagnostic
9195        // because `seen.insert(first.nome)` already populated the set).
9196        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9197        c.deps = vec![
9198            Dep::simple("caixa-teia", "^0.1"),
9199            Dep::simple("caixa-teia", "^0.2"),
9200            Dep::simple("caixa-teia", "^0.3"),
9201        ];
9202        let err = c.validate_deps().unwrap_err();
9203        // The diagnostic carries the offending caixa name; the
9204        // implementation surfaces on the *second* entry (the first
9205        // collision), so the test pins the `:nome` value.
9206        assert!(
9207            matches!(
9208                err,
9209                crate::dep::DepError::DuplicateNome { ref nome, list }
9210                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9211            ),
9212            "got {err:?}"
9213        );
9214    }
9215
9216    #[test]
9217    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
9218        // Cross-list precedence pin: when both lists carry duplicates,
9219        // the `:deps` diagnostic surfaces first — same author-mental-
9220        // model ordering the `validate_deps_runs_deps_before_deps_dev`
9221        // pin establishes for malformed `:versao` (runtime axis before
9222        // dev axis).
9223        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9224        c.deps = vec![
9225            Dep::simple("runtime-dep", "^0.1"),
9226            Dep::simple("runtime-dep", "^0.2"),
9227        ];
9228        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
9229        let err = c.validate_deps().unwrap_err();
9230        assert!(
9231            matches!(
9232                err,
9233                crate::dep::DepError::DuplicateNome { ref nome, list }
9234                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9235            ),
9236            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
9237        );
9238    }
9239
9240    #[test]
9241    fn validate_deps_empty_lists_pass_duplicate_gate() {
9242        // Empty-set identity pin: the bare template (zero deps, zero
9243        // deps_dev) passes the duplicate gate as the gate's identity
9244        // element. A future tighten that conflates "empty" with
9245        // "missing" would regress this baseline.
9246        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9247        c.validate_deps().unwrap();
9248    }
9249
9250    #[test]
9251    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
9252        // Diagnostic-shape pin: the `list:` field tags which list the
9253        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
9254        // `feira lint` run can route the author to the right block in
9255        // their caixa.lisp without re-deriving the list from context.
9256        // Same self-locating shape every peer per-axis diagnostic
9257        // already exposes.
9258        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9259        c.deps_dev = vec![
9260            Dep::simple("dev-thing", "*"),
9261            Dep::simple("dev-thing", "^0.1"),
9262        ];
9263        let err = c.validate_deps().unwrap_err();
9264        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
9265            panic!("expected DuplicateNome from :deps-dev walk");
9266        };
9267        assert_eq!(nome, "dev-thing");
9268        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
9269    }
9270
9271    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
9272
9273    #[test]
9274    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
9275        // Thread-through pin on `:deps`: the per-entry
9276        // `Dep::validate_caracteristicas` gate fires inside
9277        // `Caixa::validate_deps`'s linear walk, so a malformed feature
9278        // list on any `:deps` entry surfaces as a `DepError` from
9279        // `validate_deps` — the same reachability shape every per-entry
9280        // `Dep::validate` arm threads through. Without this pin a future
9281        // shortcut that skips the per-entry `Dep::validate` call on the
9282        // cross-entry-uniqueness path would mask the within-entry
9283        // `:caracteristicas` gates.
9284        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9285        c.deps = vec![Dep {
9286            nome: "caixa-teia".into(),
9287            versao: "^0.1".into(),
9288            fonte: None,
9289            opcional: false,
9290            caracteristicas: vec!["http".into(), "http".into()],
9291        }];
9292        let err = c.validate_deps().unwrap_err();
9293        let crate::dep::DepError::CaracteristicaDuplicate {
9294            nome,
9295            caracteristica,
9296        } = err
9297        else {
9298            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
9299        };
9300        assert_eq!(nome, "caixa-teia");
9301        assert_eq!(caracteristica, "http");
9302    }
9303
9304    #[test]
9305    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
9306        // Peer thread-through pin on `:deps-dev`: same reachability as
9307        // the `:deps` arm above, on the dev-only authoring axis. Pins
9308        // that the `validate_deps` walk visits both lists' per-entry
9309        // gates uniformly. The empty-feature arm carries here so both
9310        // new `:caracteristicas` arms are surfaced via at least one
9311        // `validate_deps` thread-through.
9312        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9313        c.deps_dev = vec![Dep {
9314            nome: "caixa-teia".into(),
9315            versao: "^0.1".into(),
9316            fonte: None,
9317            opcional: false,
9318            caracteristicas: vec![String::new()],
9319        }];
9320        let err = c.validate_deps().unwrap_err();
9321        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
9322            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
9323        };
9324        assert_eq!(nome, "caixa-teia");
9325    }
9326
9327    #[test]
9328    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
9329        // Thread-through pin on `:deps`: the per-entry
9330        // `Dep::validate_caracteristicas` value-shape gate (lifted via
9331        // `crate::render::is_cargo_feature_name`) fires inside
9332        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
9333        // a structurally invalid feature name on any `:deps` entry
9334        // surfaces as `DepError::CaracteristicaInvalid` from
9335        // `validate_deps` — the same reachability shape every per-entry
9336        // `Dep::validate` arm threads through. Without this pin a
9337        // future shortcut that skips the per-entry `Dep::validate` call
9338        // on the cross-entry-uniqueness path would mask the within-
9339        // entry `:caracteristicas` value-shape gate.
9340        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9341        c.deps = vec![Dep {
9342            nome: "caixa-teia".into(),
9343            versao: "^0.1".into(),
9344            fonte: None,
9345            opcional: false,
9346            caracteristicas: vec!["+http".into()],
9347        }];
9348        let err = c.validate_deps().unwrap_err();
9349        let crate::dep::DepError::CaracteristicaInvalid {
9350            nome,
9351            caracteristica,
9352            ..
9353        } = err
9354        else {
9355            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
9356        };
9357        assert_eq!(nome, "caixa-teia");
9358        assert_eq!(caracteristica, "+http");
9359    }
9360
9361    #[test]
9362    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
9363        // Peer thread-through pin on `:deps-dev`: same reachability as
9364        // the `:deps` arm above, on the dev-only authoring axis. The
9365        // `http/json` shape carries here so the segment-separator
9366        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
9367        // confusion footgun) is surfaced via the cross-entry walk too —
9368        // pinning that the `:deps-dev` list visits the same per-entry
9369        // value-shape gate as the `:deps` list.
9370        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9371        c.deps_dev = vec![Dep {
9372            nome: "caixa-teia".into(),
9373            versao: "^0.1".into(),
9374            fonte: None,
9375            opcional: false,
9376            caracteristicas: vec!["http/json".into()],
9377        }];
9378        let err = c.validate_deps().unwrap_err();
9379        let crate::dep::DepError::CaracteristicaInvalid {
9380            nome,
9381            caracteristica,
9382            ..
9383        } = err
9384        else {
9385            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
9386        };
9387        assert_eq!(nome, "caixa-teia");
9388        assert_eq!(caracteristica, "http/json");
9389    }
9390
9391    #[test]
9392    fn to_lisp_preserves_deps() {
9393        let src = r#"
9394(defcaixa
9395  :nome "x"
9396  :versao "0.1.0"
9397  :kind Biblioteca
9398  :deps ((:nome "a" :versao "^0.1")
9399         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
9400"#;
9401        let c1 = Caixa::from_lisp(src).unwrap();
9402        let emitted = c1.to_lisp();
9403        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
9404        assert_eq!(c1.deps, c2.deps);
9405    }
9406
9407    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
9408
9409    fn caixa_with_nome(nome: &str) -> Caixa {
9410        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
9411        c.nome = nome.to_string();
9412        c
9413    }
9414
9415    #[test]
9416    fn validate_nome_accepts_canonical_template() {
9417        // Positive control: the bare `feira init`-style template's
9418        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
9419        // not regress this baseline shape. A future tightening of the
9420        // accepted set surfaces here as a test failure first.
9421        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9422        c.validate_nome().unwrap();
9423    }
9424
9425    #[test]
9426    fn validate_nome_accepts_canonical_forms() {
9427        // Positive-set sweep: each realistic caixa-name shape the K8s
9428        // apiserver accepts as a `metadata.name` label must pass —
9429        // single-word, hyphen-joined, version-suffixed, single-char,
9430        // two-char, digit-start (DNS-1123 allows this; the stricter
9431        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
9432        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
9433        // the peer member-name axis.
9434        for nome in [
9435            "checkout",
9436            "cart-v2",
9437            "a",
9438            "db",
9439            "3rd-party-shim",
9440            "payment-retry",
9441            "0",
9442        ] {
9443            caixa_with_nome(nome)
9444                .validate_nome()
9445                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
9446        }
9447    }
9448
9449    #[test]
9450    fn validate_nome_rejects_empty() {
9451        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9452        // an empty `:nome` (the derive macro stores the raw String);
9453        // the gate's empty arm names the offending axis with a narrower
9454        // diagnostic than the `NomeInvalid` parse arm would emit.
9455        let c = caixa_with_nome("");
9456        let err = c.validate_nome().unwrap_err();
9457        assert_eq!(err, ManifestError::NomeEmpty);
9458    }
9459
9460    #[test]
9461    fn validate_nome_rejects_uppercase() {
9462        // The canonical "I copied the TitleCase display name verbatim"
9463        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
9464        // admission on every derived artifact (Helm chart, ComputeUnit,
9465        // CNP, HTTPRoute, label values); the gate moves the diagnostic
9466        // to the source `caixa.lisp` and the reason suggests the
9467        // lowercased fix verbatim.
9468        let c = caixa_with_nome("MyApp");
9469        let err = c.validate_nome().unwrap_err();
9470        let ManifestError::NomeInvalid { nome, reason } = err else {
9471            panic!("expected NomeInvalid for uppercase :nome");
9472        };
9473        assert_eq!(nome, "MyApp");
9474        assert!(
9475            reason.contains("uppercase") && reason.contains("myapp"),
9476            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
9477        );
9478    }
9479
9480    #[test]
9481    fn validate_nome_rejects_underscore() {
9482        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
9483        // `_`; the apiserver rejects on admission across every derived
9484        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
9485        // and `:children :caixa` (31bfa43).
9486        let c = caixa_with_nome("my_app");
9487        let err = c.validate_nome().unwrap_err();
9488        assert!(
9489            matches!(
9490                err,
9491                ManifestError::NomeInvalid { ref nome, ref reason }
9492                    if nome == "my_app" && reason.contains('_')
9493            ),
9494            "got {err:?}"
9495        );
9496    }
9497
9498    #[test]
9499    fn validate_nome_rejects_dot() {
9500        // A `:nome` is a single DNS-1123 label, not a subdomain. The
9501        // "I want to namespace with `.`" footgun the gate redirects to
9502        // `-` via the shared predicate's reason wording.
9503        let c = caixa_with_nome("team.app");
9504        let err = c.validate_nome().unwrap_err();
9505        assert!(
9506            matches!(
9507                err,
9508                ManifestError::NomeInvalid { ref nome, ref reason }
9509                    if nome == "team.app" && reason.contains('.')
9510            ),
9511            "got {err:?}"
9512        );
9513    }
9514
9515    #[test]
9516    fn validate_nome_rejects_leading_hyphen() {
9517        // DNS-1123 boundary rule: the label must start with an ASCII
9518        // alphanumeric. Pin the leading-`-` arm explicitly.
9519        let c = caixa_with_nome("-app");
9520        let err = c.validate_nome().unwrap_err();
9521        assert!(
9522            matches!(
9523                err,
9524                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
9525            ),
9526            "got {err:?}"
9527        );
9528    }
9529
9530    #[test]
9531    fn validate_nome_rejects_trailing_hyphen() {
9532        // Symmetric arm of the boundary rule, pinned separately so a
9533        // future relaxation that only checks the leading position
9534        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
9535        // and `_with_trailing_hyphen` on the supervisor / aplicacao
9536        // axes.
9537        let c = caixa_with_nome("app-");
9538        let err = c.validate_nome().unwrap_err();
9539        assert!(
9540            matches!(
9541                err,
9542                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
9543            ),
9544            "got {err:?}"
9545        );
9546    }
9547
9548    #[test]
9549    fn validate_nome_rejects_unicode() {
9550        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
9551        // bytes are rejected by the K8s apiserver on every name axis.
9552        let c = caixa_with_nome("café");
9553        let err = c.validate_nome().unwrap_err();
9554        assert!(
9555            matches!(
9556                err,
9557                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
9558            ),
9559            "got {err:?}"
9560        );
9561    }
9562
9563    #[test]
9564    fn validate_nome_rejects_whitespace() {
9565        // The paste-from-sketch / paste-from-spec footgun. Internal
9566        // whitespace is rejected by every K8s name axis.
9567        let c = caixa_with_nome("my app");
9568        let err = c.validate_nome().unwrap_err();
9569        assert!(
9570            matches!(
9571                err,
9572                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
9573            ),
9574            "got {err:?}"
9575        );
9576    }
9577
9578    #[test]
9579    fn validate_nome_rejects_too_long() {
9580        // 64-byte boundary pin: the K8s apiserver rejects any
9581        // `metadata.name` over 63 bytes at admission; the diagnostic
9582        // names both the 63-byte cap and the actual length so the
9583        // author can shorten in one edit. Mirrors `_too_long` on the
9584        // peer member-/cluster-/child-name axes.
9585        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
9586        let c = caixa_with_nome(&over);
9587        let err = c.validate_nome().unwrap_err();
9588        let ManifestError::NomeInvalid { nome, reason } = err else {
9589            panic!("expected NomeInvalid for over-cap :nome");
9590        };
9591        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
9592        assert!(
9593            reason.contains("63") && reason.contains("64"),
9594            "diagnostic must name the cap + actual length, got {reason:?}"
9595        );
9596    }
9597
9598    #[test]
9599    fn nome_max_length_validates() {
9600        // The 63-byte cap exactly — the boundary-accepting case pinned
9601        // alongside `validate_nome_rejects_too_long` so a future cap
9602        // shift surfaces both arms simultaneously. Mirrors
9603        // `membro_caixa_max_length_validates`,
9604        // `placement_cluster_max_length_validates`,
9605        // `child_caixa_max_length_validates`.
9606        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9607        caixa_with_nome(&at_cap).validate_nome().unwrap();
9608    }
9609
9610    #[test]
9611    fn nome_empty_takes_precedence_over_invalid() {
9612        // Order pin: the empty arm fires before the predicate is
9613        // consulted. Empty < invalid in self-locating-ness — the
9614        // narrower `NomeEmpty` diagnostic doesn't carry a useless
9615        // `nome: ""` reference into the parser-shaped reason. Mirrors
9616        // `membro_caixa_empty_takes_precedence_over_invalid` on the
9617        // peer axis (3f9d7a0).
9618        let c = caixa_with_nome("");
9619        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
9620    }
9621
9622    #[test]
9623    fn nome_invalid_diagnostic_carries_offending_nome() {
9624        // Diagnostic-shape pin: the error names the offending `:nome`
9625        // verbatim with a non-empty parser-shaped reason, so a `feira
9626        // lint` run can render the diagnostic without re-parsing.
9627        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
9628        let c = caixa_with_nome("MyApp");
9629        let err = c.validate_nome().unwrap_err();
9630        let ManifestError::NomeInvalid { nome, reason } = err else {
9631            panic!("expected NomeInvalid variant");
9632        };
9633        assert_eq!(nome, "MyApp");
9634        assert!(
9635            !reason.is_empty(),
9636            "NomeInvalid `reason` must carry the predicate's wording verbatim"
9637        );
9638    }
9639
9640    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
9641    //
9642    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
9643    // via DNS-1123; this second-axis gate caps the joint
9644    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
9645    // canonical [`crate::lareira_chart_name`] helper's doc comment
9646    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
9647    // "the M4 admission webhook will pin the joint-length invariant
9648    // when it lands". These tests pin it at the manifest-validate
9649    // layer instead, fail-before-pass-after on the 56-byte boundary.
9650
9651    #[test]
9652    fn validate_nome_chart_name_budget_accepts_canonical_template() {
9653        // Positive control: the bare `feira init`-style template's
9654        // `:nome` ("demo") sits far below the cap; the gate must not
9655        // regress this baseline. Same shape every peer
9656        // value-shape-gate baseline pin uses.
9657        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9658        c.validate_nome_chart_name_budget().unwrap();
9659    }
9660
9661    #[test]
9662    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
9663        // Positive-set sweep across the canonical author surface every
9664        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
9665        // `worker`, the `checkout-aplicacao` example members, the
9666        // `example-attest` caixa-tatara fixture). Every value sits
9667        // far below the 55-byte per-`:nome` budget. Same shape every
9668        // peer per-axis baseline pin uses.
9669        for nome in [
9670            "hello-rio",
9671            "cart",
9672            "checkout",
9673            "worker",
9674            "example-attest",
9675            "demo",
9676            "a",
9677        ] {
9678            caixa_with_nome(nome)
9679                .validate_nome_chart_name_budget()
9680                .unwrap_or_else(|e| {
9681                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
9682                });
9683        }
9684    }
9685
9686    #[test]
9687    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
9688        // Boundary-accepting case at the 55-byte per-`:nome` budget —
9689        // the joint chart name is exactly 63 bytes, the DNS-1123 label
9690        // cap. Pinned alongside the rejecting-arm test so a future cap
9691        // shift surfaces both arms simultaneously. Mirrors
9692        // `nome_max_length_validates` on the peer bare-`:nome` axis.
9693        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
9694        caixa_with_nome(&at_cap)
9695            .validate_nome_chart_name_budget()
9696            .unwrap();
9697    }
9698
9699    #[test]
9700    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
9701        // Fail-before-pass-after pin on the 56-byte boundary: the
9702        // smallest `:nome` length that overflows the joint chart-name
9703        // cap. The inner [`is_dns_1123_label`] gate
9704        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
9705        // this gate it silently passed the manifest-validate cascade
9706        // and surfaced as a `helm lint` / apiserver rejection on the
9707        // rendered chart name far from the source `caixa.lisp`, with
9708        // no field naming the overflow. With this gate the diagnostic
9709        // names the offending `:nome` verbatim alongside the rendered
9710        // chart name and the budget, so the author can shorten in one
9711        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
9712        // bare-`:nome` axis.
9713        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9714        let c = caixa_with_nome(&over);
9715        let err = c.validate_nome_chart_name_budget().unwrap_err();
9716        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9717            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
9718        };
9719        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9720        assert_eq!(nome, over);
9721        assert!(
9722            reason.contains("63") && reason.contains("64") && reason.contains("55"),
9723            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
9724             and the per-`:nome` budget (55), got {reason:?}"
9725        );
9726    }
9727
9728    #[test]
9729    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
9730        // The 63-byte `:nome` boundary — passes the bare-`:nome`
9731        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
9732        // joint chart name that overflows the DNS-1123 label cap
9733        // structurally. The most stringent fail-before-pass-after
9734        // surface: every `:nome` in the 56..=63-byte range passed the
9735        // prior cascade and broke at admission.
9736        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9737        let c = caixa_with_nome(&bare_max);
9738        // The bare-`:nome` gate accepts the 63-byte length.
9739        c.validate_nome().unwrap();
9740        // The new joint-length gate rejects it.
9741        let err = c.validate_nome_chart_name_budget().unwrap_err();
9742        assert!(
9743            matches!(
9744                err,
9745                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
9746                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
9747            ),
9748            "got {err:?}"
9749        );
9750    }
9751
9752    #[test]
9753    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
9754        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
9755        // name appears verbatim in the diagnostic so the author sees
9756        // exactly the string the apiserver / `helm lint` would have
9757        // rejected — no re-derivation required to grep the source.
9758        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
9759        // on the bare-`:nome` axis.
9760        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
9761        let c = caixa_with_nome(&over);
9762        let err = c.validate_nome_chart_name_budget().unwrap_err();
9763        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9764            panic!("expected NomeChartNameBudgetExceeded variant");
9765        };
9766        assert_eq!(nome, over);
9767        let expected_chart = crate::lareira_chart_name(&over);
9768        assert!(
9769            reason.contains(&expected_chart),
9770            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
9771             got {reason:?}"
9772        );
9773        assert!(
9774            reason.contains("lareira-"),
9775            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
9776        );
9777    }
9778
9779    #[test]
9780    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
9781        // Order pin on the layout cascade: the narrower
9782        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
9783        // joint-length budget. A structurally-malformed `:nome` (here:
9784        // uppercase) surfaces its specific shape error rather than
9785        // the chart-name-budget error, even when the joint length
9786        // would also overflow — the narrower diagnostic is more
9787        // self-locating. Mirrors the cascade-precedence pins peer
9788        // gates already use (e.g. `EntradaParaEmpty` before
9789        // `EntradaParaInvalid`).
9790        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9791        let c = caixa_with_nome(&over);
9792        // The bare-shape gate fires first.
9793        let err = c.validate_nome().unwrap_err();
9794        assert!(
9795            matches!(err, ManifestError::NomeInvalid { .. }),
9796            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
9797        );
9798        // And the layout verify cascade surfaces that diagnostic, not
9799        // the budget arm. Inject a path-exists oracle so the cascade
9800        // gets past the manifest-presence check and into the
9801        // value-shape gates.
9802        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9803        let err = crate::LayoutInvariants::verify(
9804            &layout,
9805            &c,
9806            std::path::Path::new("/tmp/caixa-test-fake-root"),
9807        )
9808        .unwrap_err();
9809        let issue = err.to_string();
9810        assert!(
9811            issue.contains("DNS-1123") || issue.contains("uppercase"),
9812            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
9813             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
9814        );
9815    }
9816
9817    #[test]
9818    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
9819        // Cross-axis envelope pin: the layout cascade wraps both
9820        // bare-`:nome` and joint-length-`:nome` failures through the
9821        // same [`LayoutError::NomeViolation`] envelope, since both
9822        // arms are on the `:nome` axis. The user's diagnostic stays
9823        // self-locating ("which axis"), and a future consumer that
9824        // dispatches on the layout-error variant (e.g. a `feira lint`
9825        // exit-code mapping) sees a single per-axis envelope. The
9826        // wrapped `issue:` carries the full inner diagnostic.
9827        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9828        let c = caixa_with_nome(&over);
9829        // The bare-shape gate accepts.
9830        c.validate_nome().unwrap();
9831        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9832        let err = crate::LayoutInvariants::verify(
9833            &layout,
9834            &c,
9835            std::path::Path::new("/tmp/caixa-test-fake-root"),
9836        )
9837        .unwrap_err();
9838        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
9839            panic!("expected LayoutError::NomeViolation, got {err:?}");
9840        };
9841        assert_eq!(caixa, over);
9842        assert!(
9843            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
9844            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
9845        );
9846    }
9847
9848    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
9849
9850    fn caixa_with_versao(versao: &str) -> Caixa {
9851        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9852        c.versao = versao.to_string();
9853        c
9854    }
9855
9856    #[test]
9857    fn validate_versao_accepts_canonical_template() {
9858        // Positive control: the bare `feira init`-style template's
9859        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
9860        // must not regress this baseline shape. A future tightening of
9861        // the accepted set surfaces here as a test failure first.
9862        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9863        c.validate_versao().unwrap();
9864    }
9865
9866    #[test]
9867    fn validate_versao_accepts_canonical_forms() {
9868        // Positive-set sweep: each realistic SemVer-2 shape the
9869        // substrate's downstream consumers accept must pass — bare
9870        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
9871        // build metadata (`+build.42`), the combined form, and the
9872        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
9873        // the peer `:nome` axis (6c992f8).
9874        for versao in [
9875            "0.1.0",
9876            "0.0.0",
9877            "1.0.0",
9878            "0.2.0-rc.1",
9879            "1.0.0-alpha.0",
9880            "1.0.0+build.42",
9881            "1.0.0-rc.1+build.42",
9882            "10.20.30",
9883        ] {
9884            caixa_with_versao(versao)
9885                .validate_versao()
9886                .unwrap_or_else(|e| {
9887                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
9888                });
9889        }
9890    }
9891
9892    #[test]
9893    fn validate_versao_rejects_empty() {
9894        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9895        // an empty `:versao` (the derive macro stores the raw String);
9896        // the gate's empty arm names the offending axis with a narrower
9897        // diagnostic than the `VersaoInvalid` parse arm would emit.
9898        // Mirrors `validate_nome_rejects_empty` (6c992f8).
9899        let c = caixa_with_versao("");
9900        let err = c.validate_versao().unwrap_err();
9901        assert_eq!(err, ManifestError::VersaoEmpty);
9902    }
9903
9904    #[test]
9905    fn validate_versao_rejects_git_tag_shape() {
9906        // The canonical "I copied the git tag verbatim" footgun —
9907        // `feira publish` *emits* `v<versao>` git tags, so a leaked
9908        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
9909        // shift every downstream consumer's version axis. `semver`
9910        // rejects the leading `v` at parse time; the gate moves the
9911        // diagnostic to the source `caixa.lisp`.
9912        let c = caixa_with_versao("v0.1.0");
9913        let err = c.validate_versao().unwrap_err();
9914        let ManifestError::VersaoInvalid { versao, reason } = err else {
9915            panic!("expected VersaoInvalid for git-tag-shape :versao");
9916        };
9917        assert_eq!(versao, "v0.1.0");
9918        assert!(
9919            !reason.is_empty(),
9920            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
9921        );
9922    }
9923
9924    #[test]
9925    fn validate_versao_rejects_missing_patch() {
9926        // The canonical "I shortened it" footgun — SemVer-2 requires
9927        // three parts. Cargo's `version =` field accepts the shortened
9928        // form as a requirement, conflating the two leaks across the
9929        // typed `:deps :versao` vs top-level `:versao` axes; the gate
9930        // pins the top-level axis to the strict three-part shape.
9931        let c = caixa_with_versao("0.1");
9932        let err = c.validate_versao().unwrap_err();
9933        assert!(
9934            matches!(
9935                err,
9936                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
9937            ),
9938            "got {err:?}"
9939        );
9940    }
9941
9942    #[test]
9943    fn validate_versao_rejects_requirement_shape() {
9944        // The canonical "I leaked a requirement into a version" footgun —
9945        // the typed `:deps :versao` / `:membros :versao` axes accept
9946        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
9947        // concrete `Version`. Without this gate the two typed surfaces
9948        // would silently overlap, and a top-level `^0.1` would surface
9949        // at `helm install` time as a Chart.yaml version rejection far
9950        // from the source `caixa.lisp`.
9951        let c = caixa_with_versao("^0.1");
9952        let err = c.validate_versao().unwrap_err();
9953        assert!(
9954            matches!(
9955                err,
9956                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
9957            ),
9958            "got {err:?}"
9959        );
9960    }
9961
9962    #[test]
9963    fn validate_versao_rejects_docker_tag_shape() {
9964        // The "I confused it with a docker tag" footgun — `latest`,
9965        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
9966        // SemVer rejects at parse time; the gate moves the diagnostic
9967        // to the source `caixa.lisp`.
9968        for bad in ["latest", "main", "stable"] {
9969            let c = caixa_with_versao(bad);
9970            let err = c.validate_versao().unwrap_err();
9971            assert!(
9972                matches!(
9973                    err,
9974                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
9975                ),
9976                "got {err:?} for {bad:?}"
9977            );
9978        }
9979    }
9980
9981    #[test]
9982    fn validate_versao_rejects_four_part_form() {
9983        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
9984        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
9985        // semver crate rejects the extra `.0` at parse time.
9986        let c = caixa_with_versao("0.1.0.0");
9987        let err = c.validate_versao().unwrap_err();
9988        assert!(
9989            matches!(
9990                err,
9991                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
9992            ),
9993            "got {err:?}"
9994        );
9995    }
9996
9997    #[test]
9998    fn versao_empty_takes_precedence_over_invalid() {
9999        // Order pin: the empty arm fires before the parser is consulted.
10000        // Empty < invalid in self-locating-ness — the narrower
10001        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
10002        // reference into the parser-shaped reason. Mirrors
10003        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
10004        // peer axis.
10005        let c = caixa_with_versao("");
10006        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
10007    }
10008
10009    #[test]
10010    fn versao_invalid_diagnostic_carries_offending_versao() {
10011        // Diagnostic-shape pin: the error names the offending `:versao`
10012        // verbatim with a non-empty parser-shaped reason, so a `feira
10013        // lint` run can render the diagnostic without re-parsing.
10014        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
10015        let c = caixa_with_versao("v0.1.0");
10016        let err = c.validate_versao().unwrap_err();
10017        let ManifestError::VersaoInvalid { versao, reason } = err else {
10018            panic!("expected VersaoInvalid variant");
10019        };
10020        assert_eq!(versao, "v0.1.0");
10021        assert!(
10022            !reason.is_empty(),
10023            "VersaoInvalid `reason` must carry the parser's wording verbatim"
10024        );
10025    }
10026
10027    #[test]
10028    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
10029        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
10030        // for `:upgrade-from :from` must also pass `validate_versao` —
10031        // the two `:versao`-typed surfaces (top-level `:versao`,
10032        // `:upgrade-from :from`) consume the *same* `semver::Version`
10033        // parser, so they must agree on the accepted set. Without this
10034        // pin, a future tightening of one axis could silently diverge
10035        // from the other. Mirrors the `:versao` requirement-axis
10036        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
10037        // commits established.
10038        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
10039            // From the canonical UpgradeFromEntry round-trip fixture
10040            // (`upgrade::tests::round_trip_load_module` peers).
10041            let entry = crate::UpgradeFromEntry {
10042                from: versao.to_string(),
10043                instructions: Vec::new(),
10044            };
10045            entry
10046                .validate()
10047                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
10048            caixa_with_versao(versao)
10049                .validate_versao()
10050                .unwrap_or_else(|e| {
10051                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
10052                });
10053        }
10054    }
10055
10056    // ── Caixa::validate_restart_window — supervisor restart-window
10057    //    folds through the shared `supervisor::duration_codec` ────────
10058
10059    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
10060        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
10061        c.kind = CaixaKind::Supervisor;
10062        c.restart_window = window.map(str::to_string);
10063        c
10064    }
10065
10066    #[test]
10067    fn validate_restart_window_accepts_none() {
10068        // The canonical "omit the slot to express no reset" shape — a
10069        // `None` raw string is the absence of the typed
10070        // `:restart-window` slot, which is exactly the SupervisorSpec
10071        // "never reset" semantics. The gate must be a no-op here; a
10072        // future tightening that rejected `None` would force every
10073        // supervisor caixa to authoring-time pin a window even when
10074        // the OTP semantics call for none.
10075        caixa_with_restart_window(None)
10076            .validate_restart_window()
10077            .unwrap();
10078    }
10079
10080    #[test]
10081    fn validate_restart_window_accepts_canonical_forms() {
10082        // Positive-set sweep across the canonical authoring units the
10083        // shared `supervisor::duration_codec::parse` accepts —
10084        // matches the codec-side `parse_accepts_integer_canonical_units`
10085        // pin in supervisor::tests so a future codec-side tightening
10086        // surfaces simultaneously on both axes.
10087        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
10088            caixa_with_restart_window(Some(window))
10089                .validate_restart_window()
10090                .unwrap_or_else(|e| {
10091                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
10092                });
10093        }
10094    }
10095
10096    #[test]
10097    fn validate_restart_window_rejects_fractional_seconds() {
10098        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
10099        // as f64 to 1.5 → renders back as `"1500ms"` on first
10100        // serialize). Prior to the fold + this gate, the inline
10101        // `parse_window_inline` accepted f64 magnitudes and silently
10102        // produced a `Duration::from_secs_f64(1.5)`, divergent from
10103        // the shared codec's integer-magnitude discipline on the
10104        // serde-routed siblings. The gate now surfaces a self-locating
10105        // diagnostic at the manifest layer.
10106        let err = caixa_with_restart_window(Some("1.5s"))
10107            .validate_restart_window()
10108            .unwrap_err();
10109        let ManifestError::RestartWindowMalformed {
10110            restart_window,
10111            reason,
10112        } = err
10113        else {
10114            panic!("expected RestartWindowMalformed for fractional seconds");
10115        };
10116        assert_eq!(restart_window, "1.5s");
10117        assert!(
10118            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
10119            "diagnostic must carry shared-codec wording, got {reason:?}"
10120        );
10121    }
10122
10123    #[test]
10124    fn validate_restart_window_rejects_decimal_shaped_integer() {
10125        // The `"1.0s"` class — numerically `1s` exactly, but the
10126        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
10127        // gets the same canonical-form diagnostic.
10128        let err = caixa_with_restart_window(Some("1.0s"))
10129            .validate_restart_window()
10130            .unwrap_err();
10131        assert!(
10132            matches!(
10133                err,
10134                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10135                    if restart_window == "1.0s"
10136            ),
10137            "got {err:?}"
10138        );
10139    }
10140
10141    #[test]
10142    fn validate_restart_window_rejects_half_unit_minute() {
10143        // `"0.5m"` is the unit-fraction footgun — author writes a
10144        // human-readable half-minute, the prior inline parser silently
10145        // produced `Duration::from_secs_f64(30.0)` and serde
10146        // re-emitted as `"30s"`, rewriting author intent. The gate
10147        // closes the loop at the manifest layer.
10148        let err = caixa_with_restart_window(Some("0.5m"))
10149            .validate_restart_window()
10150            .unwrap_err();
10151        let ManifestError::RestartWindowMalformed {
10152            restart_window,
10153            reason,
10154        } = err
10155        else {
10156            panic!("expected RestartWindowMalformed");
10157        };
10158        assert_eq!(restart_window, "0.5m");
10159        assert!(
10160            reason.contains("\"30s\""),
10161            "diagnostic must point at the canonical-form remediation, got {reason:?}"
10162        );
10163    }
10164
10165    #[test]
10166    fn validate_restart_window_rejects_leading_sign() {
10167        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
10168        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
10169        // and was caught by the `num < 0.0` arm which silently
10170        // returned `None`, dropping the author-supplied window). The
10171        // shared codec's digit-only gate rejects both with a unified
10172        // canonical-form diagnostic; the manifest-layer wrapper names
10173        // the offending value.
10174        for bad in ["+30s", "-30s"] {
10175            let err = caixa_with_restart_window(Some(bad))
10176                .validate_restart_window()
10177                .unwrap_err();
10178            assert!(
10179                matches!(
10180                    err,
10181                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
10182                        if restart_window == bad
10183                ),
10184                "got {err:?} for {bad:?}"
10185            );
10186        }
10187    }
10188
10189    #[test]
10190    fn validate_restart_window_rejects_unknown_unit() {
10191        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
10192        // unit dispatch surfaces an `unknown duration unit` reason;
10193        // the manifest-layer wrapper names the offending value.
10194        let err = caixa_with_restart_window(Some("30x"))
10195            .validate_restart_window()
10196            .unwrap_err();
10197        let ManifestError::RestartWindowMalformed {
10198            restart_window,
10199            reason,
10200        } = err
10201        else {
10202            panic!("expected RestartWindowMalformed for unknown unit");
10203        };
10204        assert_eq!(restart_window, "30x");
10205        assert!(
10206            reason.contains("unknown duration unit"),
10207            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
10208        );
10209    }
10210
10211    #[test]
10212    fn validate_restart_window_rejects_garbage() {
10213        // Pure non-numeric magnitude (`"abc"`) falls through to the
10214        // shared codec's narrower `"bad duration magnitude"` arm. Same
10215        // diagnostic shape as the codec-side
10216        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
10217        let err = caixa_with_restart_window(Some("abc"))
10218            .validate_restart_window()
10219            .unwrap_err();
10220        let ManifestError::RestartWindowMalformed {
10221            restart_window,
10222            reason,
10223        } = err
10224        else {
10225            panic!("expected RestartWindowMalformed for garbage");
10226        };
10227        assert_eq!(restart_window, "abc");
10228        assert!(
10229            reason.contains("bad duration magnitude"),
10230            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
10231        );
10232    }
10233
10234    #[test]
10235    fn validate_restart_window_rejects_empty_string() {
10236        // The empty-after-trim edge case — distinct from the `None`
10237        // canonical "omit the slot" shape. The shared codec's
10238        // digit-only gate refuses an empty magnitude; the manifest
10239        // layer names the offending `""` so the author can grep for
10240        // the literal empty value in their `caixa.lisp` and either
10241        // remove the slot (the canonical "no reset" shape) or pin a
10242        // positive duration.
10243        let err = caixa_with_restart_window(Some(""))
10244            .validate_restart_window()
10245            .unwrap_err();
10246        assert!(
10247            matches!(
10248                err,
10249                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10250                    if restart_window.is_empty()
10251            ),
10252            "got {err:?}"
10253        );
10254    }
10255
10256    #[test]
10257    fn validate_restart_window_diagnostic_carries_offending_value() {
10258        // Diagnostic-shape pin (peer with
10259        // `nome_invalid_diagnostic_carries_offending_nome` /
10260        // `versao_invalid_diagnostic_carries_offending_versao`): the
10261        // error names the offending raw `:restart-window` verbatim
10262        // with a non-empty shared-codec-shaped reason, so a `feira
10263        // lint` run can render the diagnostic without re-parsing.
10264        let err = caixa_with_restart_window(Some("1.5s"))
10265            .validate_restart_window()
10266            .unwrap_err();
10267        let ManifestError::RestartWindowMalformed {
10268            restart_window,
10269            reason,
10270        } = err
10271        else {
10272            panic!("expected RestartWindowMalformed variant");
10273        };
10274        assert_eq!(restart_window, "1.5s");
10275        assert!(
10276            !reason.is_empty(),
10277            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
10278        );
10279    }
10280
10281    #[test]
10282    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
10283        // Behavioral parity pin after the fold (`parse_window_inline`
10284        // deletion): the canonical `"60s"` still produces
10285        // `Duration::from_secs(60)` on the typed view — the fold is
10286        // semantically equivalent to the prior inline parser on the
10287        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
10288        // pin, narrowed to the parser-side contract.
10289        let c = caixa_with_restart_window(Some("60s"));
10290        let view = c.supervisor_view().expect("Supervisor kind has a view");
10291        assert_eq!(
10292            view.restart_window,
10293            Some(std::time::Duration::from_secs(60))
10294        );
10295    }
10296
10297    #[test]
10298    fn supervisor_view_soft_swallows_what_validate_rejects() {
10299        // Parity pin between the view-construction path and the
10300        // manifest-level validator: the same `"1.5s"` that surfaces
10301        // `RestartWindowMalformed` at `validate_restart_window` time
10302        // becomes `restart_window: None` on the typed view (the fold
10303        // preserves the existing best-effort shape of `supervisor_view`).
10304        // The contract is: a layout-verifier / `feira lint` flow that
10305        // cares about the malformed-window axis MUST consult
10306        // `validate_restart_window` — relying solely on the view's
10307        // `None` swallows the diagnostic silently. This pin makes the
10308        // expectation a typed invariant.
10309        let c = caixa_with_restart_window(Some("1.5s"));
10310        let view = c.supervisor_view().expect("Supervisor kind has a view");
10311        assert_eq!(
10312            view.restart_window, None,
10313            "view-construction path soft-swallows the parse error to None"
10314        );
10315        // And the manifest-level validator does NOT soft-swallow:
10316        assert!(
10317            matches!(
10318                c.validate_restart_window().unwrap_err(),
10319                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10320                    if restart_window == "1.5s"
10321            ),
10322            "validator must surface the offending value",
10323        );
10324    }
10325
10326    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
10327
10328    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
10329        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10330        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
10331        c.exe = exe.into_iter().map(String::from).collect();
10332        c.servicos = servicos.into_iter().map(String::from).collect();
10333        c
10334    }
10335
10336    #[test]
10337    fn validate_code_paths_accepts_canonical_template() {
10338        // The bare `Caixa::template` shape is the gate's identity element
10339        // on the canonical authoring shape — `:bibliotecas
10340        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
10341        // that the gate is non-disruptive against every existing caixa.
10342        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10343        c.validate_code_paths().unwrap();
10344    }
10345
10346    #[test]
10347    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
10348        // Positive control sweep: a canonical-shaped path on every slot
10349        // passes. Mirrors the peer
10350        // `behavior::validate_every_slot_relative_is_ok` pin.
10351        let c = caixa_with_code_paths(
10352            vec!["lib/demo.lisp", "lib/helpers.lisp"],
10353            vec!["exe/demo", "exe/tool"],
10354            vec!["servicos/demo.computeunit.yaml"],
10355        );
10356        c.validate_code_paths().unwrap();
10357    }
10358
10359    #[test]
10360    fn validate_code_paths_accepts_all_empty_lists() {
10361        // The empty-list identity element: every Caixa with no declared
10362        // code paths trivially passes (Supervisor / Aplicacao kinds rely
10363        // on this — the OwnCode gate already rejected them before the
10364        // path-shape gate runs in the layout, but the validator itself
10365        // must accept the empty shape).
10366        let c = caixa_with_code_paths(vec![], vec![], vec![]);
10367        c.validate_code_paths().unwrap();
10368    }
10369
10370    #[test]
10371    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
10372        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10373        let err = c.validate_code_paths().unwrap_err();
10374        assert!(
10375            matches!(
10376                err,
10377                ManifestError::CodePathEmpty {
10378                    slot: ":bibliotecas"
10379                }
10380            ),
10381            "got {err:?}",
10382        );
10383    }
10384
10385    #[test]
10386    fn validate_code_paths_rejects_empty_exe_entry() {
10387        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
10388        let err = c.validate_code_paths().unwrap_err();
10389        assert!(
10390            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
10391            "got {err:?}",
10392        );
10393    }
10394
10395    #[test]
10396    fn validate_code_paths_rejects_empty_servicos_entry() {
10397        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
10398        let err = c.validate_code_paths().unwrap_err();
10399        assert!(
10400            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
10401            "got {err:?}",
10402        );
10403    }
10404
10405    #[test]
10406    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
10407        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
10408        // so an absolute path that resolves on disk silently passes the
10409        // layout's existence check — the canonical sandbox-escape on
10410        // the biblioteca axis.
10411        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10412        let err = c.validate_code_paths().unwrap_err();
10413        let ManifestError::CodePathAbsolute { slot, path } = err else {
10414            panic!("expected CodePathAbsolute, got {err:?}");
10415        };
10416        assert_eq!(slot, ":bibliotecas");
10417        assert_eq!(path, PathBuf::from("/etc/passwd"));
10418    }
10419
10420    #[test]
10421    fn validate_code_paths_rejects_absolute_exe_entry() {
10422        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
10423        let err = c.validate_code_paths().unwrap_err();
10424        let ManifestError::CodePathAbsolute { slot, path } = err else {
10425            panic!("expected CodePathAbsolute, got {err:?}");
10426        };
10427        assert_eq!(slot, ":exe");
10428        assert_eq!(path, PathBuf::from("/usr/bin/env"));
10429    }
10430
10431    #[test]
10432    fn validate_code_paths_rejects_absolute_servicos_entry() {
10433        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
10434        let err = c.validate_code_paths().unwrap_err();
10435        let ManifestError::CodePathAbsolute { slot, path } = err else {
10436            panic!("expected CodePathAbsolute, got {err:?}");
10437        };
10438        assert_eq!(slot, ":servicos");
10439        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
10440    }
10441
10442    #[test]
10443    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
10444        // Canonical "I want a lib from a sibling caixa" footgun on the
10445        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
10446        // downstream, so a leading `..` traverses to the parent of the
10447        // caixa root with no diagnostic at layout time if the resolved
10448        // target exists.
10449        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
10450        let err = c.validate_code_paths().unwrap_err();
10451        let ManifestError::CodePathParentEscape { slot, path } = err else {
10452            panic!("expected CodePathParentEscape, got {err:?}");
10453        };
10454        assert_eq!(slot, ":bibliotecas");
10455        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
10456    }
10457
10458    #[test]
10459    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
10460        // Mid-path `..` defeats the layout's component-aware
10461        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
10462        // `starts_with(<root>/exe)` is true, but the canonical resolution
10463        // lives outside the caixa root. Caught regardless of where the
10464        // `..` sits — mirrors the peer
10465        // `behavior::validate_rejects_parent_escape_mid_path` pin.
10466        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
10467        let err = c.validate_code_paths().unwrap_err();
10468        let ManifestError::CodePathParentEscape { slot, path } = err else {
10469            panic!("expected CodePathParentEscape, got {err:?}");
10470        };
10471        assert_eq!(slot, ":exe");
10472        assert_eq!(path, PathBuf::from("exe/../../escape"));
10473    }
10474
10475    #[test]
10476    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
10477        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
10478        let err = c.validate_code_paths().unwrap_err();
10479        let ManifestError::CodePathParentEscape { slot, path } = err else {
10480            panic!("expected CodePathParentEscape, got {err:?}");
10481        };
10482        assert_eq!(slot, ":servicos");
10483        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
10484    }
10485
10486    #[test]
10487    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
10488        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
10489        // `:servicos`. A manifest with malformed entries on all three
10490        // surfaces surfaces the `:bibliotecas` defect first, mirroring
10491        // the canonical declaration order
10492        // `Caixa::declared_foreign_code_slots` already establishes for
10493        // the foreign-code-slot diagnostic.
10494        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
10495        let err = c.validate_code_paths().unwrap_err();
10496        assert!(
10497            matches!(
10498                err,
10499                ManifestError::CodePathEmpty {
10500                    slot: ":bibliotecas"
10501                }
10502            ),
10503            "got {err:?}",
10504        );
10505    }
10506
10507    #[test]
10508    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
10509        // Within-slot precedence pin: empty → absolute → parent-escape,
10510        // matching the [`PathShapeViolation`] arm-ordering every peer
10511        // `is_sandboxed_relative_path` caller follows (b0c8389
10512        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
10513        // `:bibliotecas` list whose first entry is empty *and* whose
10514        // later entries are absolute/parent-escape surfaces the empty
10515        // arm first, on the lexicographically-earliest offending entry.
10516        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
10517        let err = c.validate_code_paths().unwrap_err();
10518        assert!(
10519            matches!(
10520                err,
10521                ManifestError::CodePathEmpty {
10522                    slot: ":bibliotecas"
10523                }
10524            ),
10525            "got {err:?}",
10526        );
10527    }
10528
10529    #[test]
10530    fn validate_code_paths_first_offender_per_slot_wins() {
10531        // Within a single slot, the first declaration-order offender
10532        // surfaces — pins that the gate is left-to-right deterministic
10533        // (peer of every `*_first_collision_*` pin on duplicate gates).
10534        let c = caixa_with_code_paths(
10535            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
10536            vec![],
10537            vec![],
10538        );
10539        let err = c.validate_code_paths().unwrap_err();
10540        let ManifestError::CodePathAbsolute { slot, path } = err else {
10541            panic!("expected CodePathAbsolute, got {err:?}");
10542        };
10543        assert_eq!(slot, ":bibliotecas");
10544        assert_eq!(path, PathBuf::from("/etc/escape"));
10545    }
10546
10547    #[test]
10548    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
10549        // Diagnostic-shape pin (peer with
10550        // `nome_invalid_diagnostic_carries_offending_nome` /
10551        // `versao_invalid_diagnostic_carries_offending_versao`): the
10552        // error's Display surfaces both the offending `:slot` tag and
10553        // the offending path verbatim, so a `feira lint` run can render
10554        // the diagnostic without re-parsing.
10555        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10556        let rendered = c.validate_code_paths().unwrap_err().to_string();
10557        assert!(
10558            rendered.contains(":bibliotecas"),
10559            "diagnostic must name the offending slot: {rendered}",
10560        );
10561        assert!(
10562            rendered.contains("/etc/passwd"),
10563            "diagnostic must quote the offending path: {rendered}",
10564        );
10565    }
10566
10567    #[test]
10568    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
10569        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
10570        // axis. Without the gate `feira build` re-parses the same lib
10571        // twice, wasting work and silently masking the author's intent
10572        // to declare a *second* biblioteca.
10573        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
10574        let err = c.validate_code_paths().unwrap_err();
10575        let ManifestError::CodePathDuplicate { slot, path } = err else {
10576            panic!("expected CodePathDuplicate, got {err:?}");
10577        };
10578        assert_eq!(slot, ":bibliotecas");
10579        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
10580    }
10581
10582    #[test]
10583    fn validate_code_paths_rejects_duplicate_exe_entry() {
10584        // Same footgun on the Binario surface. The future `caixa-flake`
10585        // emitter that materializes each `:exe` entry as a flake
10586        // `packages.<name>` derivation would collide on the duplicate
10587        // package key — surfaced here at the typed-validate layer with a
10588        // self-locating diagnostic instead.
10589        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
10590        let err = c.validate_code_paths().unwrap_err();
10591        let ManifestError::CodePathDuplicate { slot, path } = err else {
10592            panic!("expected CodePathDuplicate, got {err:?}");
10593        };
10594        assert_eq!(slot, ":exe");
10595        assert_eq!(path, PathBuf::from("exe/cli"));
10596    }
10597
10598    #[test]
10599    fn validate_code_paths_rejects_duplicate_servicos_entry() {
10600        // Same footgun on the Servico surface. The peer caixa-helm /
10601        // caixa-flux renderers refuse `:servicos.len() != 1` with the
10602        // narrower `UnsupportedServicoCount` diagnostic, but that
10603        // diagnostic surfaces "too many servicos" without naming
10604        // "duplicate entry" — the typed self-locating framing only lands
10605        // at this gate.
10606        let c = caixa_with_code_paths(
10607            vec![],
10608            vec![],
10609            vec![
10610                "servicos/demo.computeunit.yaml",
10611                "servicos/demo.computeunit.yaml",
10612            ],
10613        );
10614        let err = c.validate_code_paths().unwrap_err();
10615        let ManifestError::CodePathDuplicate { slot, path } = err else {
10616            panic!("expected CodePathDuplicate, got {err:?}");
10617        };
10618        assert_eq!(slot, ":servicos");
10619        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
10620    }
10621
10622    #[test]
10623    fn validate_code_paths_accepts_same_path_across_slots() {
10624        // Per-list scope pin: a `:bibliotecas` entry that happens to
10625        // collide with an `:exe` or `:servicos` entry as a *string* is
10626        // not a duplicate by this gate (each list gets its own HashSet),
10627        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
10628        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
10629        // shape on the dep axis). The structural `starts_with(<exe |
10630        // servicos>_dir)` fence at layout time prevents the realistic
10631        // cross-slot collision case from existing on disk, but the gate's
10632        // per-list scope is correct independent of that downstream fence.
10633        let c = caixa_with_code_paths(
10634            vec!["lib/x.lisp"],
10635            vec!["exe/x"],
10636            vec!["servicos/x.computeunit.yaml"],
10637        );
10638        c.validate_code_paths().unwrap();
10639    }
10640
10641    #[test]
10642    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
10643        // Within-slot ordering pin: structural defects (empty / absolute
10644        // / parent-escape) fire before the duplicate gate on the same
10645        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
10646        // surfaces the narrower `CodePathEmpty` for the empty entry
10647        // first, not the duplicate on the later pair — same arm-ordering
10648        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
10649        // `:autores` 86c769b, `:deps` 359fba5).
10650        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
10651        let err = c.validate_code_paths().unwrap_err();
10652        assert!(
10653            matches!(
10654                err,
10655                ManifestError::CodePathEmpty {
10656                    slot: ":bibliotecas"
10657                }
10658            ),
10659            "got {err:?}",
10660        );
10661    }
10662
10663    #[test]
10664    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
10665        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
10666        // duplicates surface before `:exe` duplicates, matching the
10667        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
10668        // order every peer per-slot diagnostic on this surface follows.
10669        let c = caixa_with_code_paths(
10670            vec!["lib/x.lisp", "lib/x.lisp"],
10671            vec!["exe/y", "exe/y"],
10672            vec![],
10673        );
10674        let err = c.validate_code_paths().unwrap_err();
10675        let ManifestError::CodePathDuplicate { slot, path } = err else {
10676            panic!("expected CodePathDuplicate, got {err:?}");
10677        };
10678        assert_eq!(slot, ":bibliotecas");
10679        assert_eq!(path, PathBuf::from("lib/x.lisp"));
10680    }
10681
10682    #[test]
10683    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
10684        // Diagnostic-shape pin (peer with
10685        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10686        // on the structural arm): the duplicate-arm Display surfaces both
10687        // the offending `:slot` tag and the offending path verbatim, so a
10688        // `feira lint` run can render the diagnostic without re-parsing.
10689        let c = caixa_with_code_paths(
10690            vec![],
10691            vec![],
10692            vec![
10693                "servicos/demo.computeunit.yaml",
10694                "servicos/demo.computeunit.yaml",
10695            ],
10696        );
10697        let rendered = c.validate_code_paths().unwrap_err().to_string();
10698        assert!(
10699            rendered.contains(":servicos"),
10700            "diagnostic must name the offending slot: {rendered}",
10701        );
10702        assert!(
10703            rendered.contains("servicos/demo.computeunit.yaml"),
10704            "diagnostic must quote the offending path: {rendered}",
10705        );
10706    }
10707
10708    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
10709    //
10710    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
10711    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
10712    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
10713    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
10714    // at parse time — the same downstream consumer the peer `:behavior
10715    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
10716    // `:upgrade-from :state-change :script` (33cc830,
10717    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
10718    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
10719    // nix-built executable surface (`"exe/<name>"` shape per the canonical
10720    // [`crate::LayoutError::ExeOutsideDir`] error message and every
10721    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
10722    // is the `.computeunit.yaml` ComputeUnit-CR axis.
10723
10724    #[test]
10725    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
10726        // Canonical "I dragged the wrong file from the workspace tree"
10727        // footgun on the biblioteca axis. Without the gate `feira build`
10728        // hands the extensionless path to `tatara_lisp::read` and fails
10729        // with a parser-shaped diagnostic far from the source caixa.lisp,
10730        // with no field naming the offending `:bibliotecas` entry.
10731        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
10732            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10733            let err = c.validate_code_paths().unwrap_err();
10734            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10735                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10736            };
10737            assert_eq!(slot, ":bibliotecas");
10738            assert_eq!(path, PathBuf::from(relpath));
10739        }
10740    }
10741
10742    #[test]
10743    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
10744        // Wrong-extension sweep across common authoring footguns. Same
10745        // sweep posture as the peer
10746        // `behavior::validate_rejects_wrong_extension` (c97815a) and
10747        // `upgrade::tests::state_change_rejects_wrong_extension_script`
10748        // (33cc830) cases.
10749        for relpath in [
10750            "lib/demo.rs",
10751            "lib/demo.txt",
10752            "lib/demo.md",
10753            "lib/demo.json",
10754            "lib/demo.yaml",
10755            "lib/demo.toml",
10756            "lib/demo.lisp.bak",
10757            "lib/demo.lispx",
10758            "lib/demo.lis",
10759        ] {
10760            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10761            let err = c.validate_code_paths().unwrap_err();
10762            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10763                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10764            };
10765            assert_eq!(slot, ":bibliotecas");
10766            assert_eq!(path, PathBuf::from(relpath));
10767        }
10768    }
10769
10770    #[test]
10771    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
10772        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
10773        // contract. An uppercase `.LISP` shape that the layout's existence
10774        // check would (case-insensitively, on case-insensitive volumes)
10775        // match the on-disk file still mismatches the canonical form the
10776        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
10777        // contract. Mirrors the peer
10778        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
10779        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
10780        // (33cc830) sweeps.
10781        for relpath in [
10782            "lib/demo.LISP",
10783            "lib/demo.Lisp",
10784            "lib/demo.LiSp",
10785            "lib/demo.lISP",
10786        ] {
10787            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10788            let err = c.validate_code_paths().unwrap_err();
10789            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10790                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10791            };
10792            assert_eq!(slot, ":bibliotecas");
10793            assert_eq!(path, PathBuf::from(relpath));
10794        }
10795    }
10796
10797    #[test]
10798    fn validate_code_paths_accepts_canonical_lisp_shapes() {
10799        // Positive-control sweep through every canonical authoring shape
10800        // every in-tree fixture and the `Caixa::template` scaffold use.
10801        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
10802        // (c97815a) and the lifted predicate's own
10803        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
10804        // (33cc830).
10805        for relpath in [
10806            "lib/demo.lisp",
10807            "lib/handlers.lisp",
10808            "lib/migrations/v01-to-v02.lisp",
10809            "demo.lisp",
10810            "a.lisp",
10811            "./lib/demo.lisp",
10812            "lib/./handlers.lisp",
10813            "lib/migrations/v.0.1.lisp",
10814        ] {
10815            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10816            c.validate_code_paths()
10817                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10818        }
10819    }
10820
10821    #[test]
10822    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
10823        // The file-type gate is per-slot — only `:bibliotecas` carries the
10824        // tatara-lisp-source contract. An extensionless `:exe` entry
10825        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
10826        // canonical shapes every in-tree fixture uses, and must continue
10827        // to pass validate. Pins that a future tightening that broadens
10828        // the `.lisp` gate to either axis surfaces as a test failure
10829        // rather than as a silent breaking change to existing valid
10830        // manifests.
10831        let c = caixa_with_code_paths(
10832            vec![],
10833            vec!["exe/demo", "exe/tool"],
10834            vec!["servicos/demo.computeunit.yaml"],
10835        );
10836        c.validate_code_paths().unwrap();
10837    }
10838
10839    #[test]
10840    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
10841        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
10842        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
10843        // sandbox-shape diagnostic first (the `.lisp` remediation would
10844        // be misleading when the offending path can never resolve under
10845        // the caixa root anyway). Mirrors the peer
10846        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
10847        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
10848        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
10849        // on `:upgrade-from :state-change :script` (33cc830).
10850        //
10851        // Empty wins (the strictly-smaller-scope structural arm).
10852        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10853        assert!(
10854            matches!(
10855                c.validate_code_paths().unwrap_err(),
10856                ManifestError::CodePathEmpty {
10857                    slot: ":bibliotecas"
10858                }
10859            ),
10860            "empty must win over non-lisp-extension",
10861        );
10862        // Absolute wins (the path can't resolve under the caixa root).
10863        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10864        let err = c.validate_code_paths().unwrap_err();
10865        let ManifestError::CodePathAbsolute { slot, .. } = err else {
10866            panic!("absolute must win over non-lisp-extension, got {err:?}");
10867        };
10868        assert_eq!(slot, ":bibliotecas");
10869        // ParentEscape wins (the path escapes the caixa root).
10870        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
10871        let err = c.validate_code_paths().unwrap_err();
10872        let ManifestError::CodePathParentEscape { slot, .. } = err else {
10873            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
10874        };
10875        assert_eq!(slot, ":bibliotecas");
10876    }
10877
10878    #[test]
10879    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
10880        // Within-slot precedence pin: the per-entry file-type shape gate
10881        // fires before the cross-entry duplicate gate, so the narrower
10882        // structural defect dominates the uniqueness diagnostic. A
10883        // `("lib/x.txt" "lib/x.txt")` shape surfaces
10884        // `CodePathNonLispExtension` on the first entry rather than
10885        // `CodePathDuplicate` on the pair — same posture every per-entry
10886        // shape-gate-precedes-duplicate cascade follows on this surface
10887        // (the empty / absolute / parent-escape arms already precede the
10888        // duplicate arm; the lifted file-type arm joins that set).
10889        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
10890        let err = c.validate_code_paths().unwrap_err();
10891        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10892            panic!("expected CodePathNonLispExtension, got {err:?}");
10893        };
10894        assert_eq!(slot, ":bibliotecas");
10895        assert_eq!(path, PathBuf::from("lib/x.txt"));
10896    }
10897
10898    #[test]
10899    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
10900        // Diagnostic-shape pin (peer with
10901        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10902        // on the sandbox-shape arms and
10903        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
10904        // on the duplicate arm): the file-type-arm Display surfaces both
10905        // the offending `:slot` tag, the offending path verbatim, and the
10906        // expected `.lisp` extension named in the remediation text, so a
10907        // `feira lint` run can render the diagnostic without re-parsing.
10908        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
10909        let rendered = c.validate_code_paths().unwrap_err().to_string();
10910        assert!(
10911            rendered.contains(":bibliotecas"),
10912            "diagnostic must name the offending slot: {rendered}",
10913        );
10914        assert!(
10915            rendered.contains("lib/demo.rs"),
10916            "diagnostic must quote the offending path: {rendered}",
10917        );
10918        assert!(
10919            rendered.contains(".lisp"),
10920            "diagnostic must name the expected extension: {rendered}",
10921        );
10922    }
10923
10924    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
10925    //
10926    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
10927    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
10928    // contract. The peer caixa-helm / caixa-flux renderers consume each
10929    // `:servicos` entry through `serde_yaml::from_str` as a typed
10930    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
10931    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
10932    // axis `Path::extension` can't express on its own.
10933
10934    #[test]
10935    fn validate_code_paths_rejects_no_extension_servicos_entry() {
10936        // Canonical "I dragged the wrong file from the workspace tree"
10937        // footgun on the Servico axis. Without the gate the peer
10938        // caixa-helm / caixa-flux renderers hand the extensionless path
10939        // to `serde_yaml::from_str` and fail with a parser-shaped
10940        // diagnostic far from the source caixa.lisp, with no field
10941        // naming the offending `:servicos` entry.
10942        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
10943            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10944            let err = c.validate_code_paths().unwrap_err();
10945            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10946                panic!(
10947                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10948                     got {err:?}"
10949                );
10950            };
10951            assert_eq!(slot, ":servicos");
10952            assert_eq!(path, PathBuf::from(relpath));
10953        }
10954    }
10955
10956    #[test]
10957    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
10958        // Wrong-extension sweep across common authoring footguns on the
10959        // Servico axis. Bare `.yaml` is the canonical "I forgot the
10960        // `.computeunit` segment" typo; the off-by-one-segment shapes
10961        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
10962        // bare `Path::extension` view but mismatch the typed compound
10963        // suffix the renderers' `serde_yaml::from_str` consumer demands.
10964        // Same sweep-posture as the peer
10965        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
10966        // (64772a9) on the sibling tatara-lisp-source axis.
10967        for relpath in [
10968            "servicos/demo.yaml",
10969            "servicos/demo.yml",
10970            "servicos/demo.json",
10971            "servicos/demo.toml",
10972            "servicos/demo.txt",
10973            "servicos/demo.computeunit.yaml.bak",
10974            "servicos/demo.computeunit.yam",
10975            "servicos/demo.computeunit",
10976            "servicos/demo-computeunit.yaml",
10977            "servicos/demo_computeunit.yaml",
10978        ] {
10979            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10980            let err = c.validate_code_paths().unwrap_err();
10981            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10982                panic!(
10983                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10984                     got {err:?}"
10985                );
10986            };
10987            assert_eq!(slot, ":servicos");
10988            assert_eq!(path, PathBuf::from(relpath));
10989        }
10990    }
10991
10992    #[test]
10993    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
10994        // Case-sensitivity sweep — pins the strict lowercase
10995        // `.computeunit.yaml` contract. A case-folded shape that the
10996        // layout's existence check would (case-insensitively, on
10997        // case-insensitive volumes) match the on-disk file still
10998        // mismatches the canonical form the codec emits, breaking the
10999        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
11000        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
11001        // (64772a9) sweep on the sibling tatara-lisp-source axis.
11002        for relpath in [
11003            "servicos/demo.ComputeUnit.yaml",
11004            "servicos/demo.COMPUTEUNIT.yaml",
11005            "servicos/demo.computeunit.YAML",
11006            "servicos/demo.computeunit.Yaml",
11007            "servicos/demo.COMPUTEUNIT.YAML",
11008        ] {
11009            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
11010            let err = c.validate_code_paths().unwrap_err();
11011            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
11012                panic!(
11013                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
11014                     got {err:?}"
11015                );
11016            };
11017            assert_eq!(slot, ":servicos");
11018            assert_eq!(path, PathBuf::from(relpath));
11019        }
11020    }
11021
11022    #[test]
11023    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
11024        // Degenerate hidden-file shape: a file name exactly equal to the
11025        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
11026        // the structural "Servico declared with no identity" footgun.
11027        // The substrate identifies each ComputeUnit by the file-stem
11028        // segment that precedes `.computeunit.yaml` (the rendered
11029        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
11030        // the M3 `:contratos` membership lookup), so an empty stem
11031        // leaves the Servico unidentifiable. Pinned at the typed-axis
11032        // level so a future regression that drops the `name.len() >
11033        // SUFFIX.len()` bound at the predicate surfaces here, not
11034        // piecemeal as a `lareira-` chart-name collision at render time.
11035        for relpath in ["servicos/.computeunit.yaml"] {
11036            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
11037            let err = c.validate_code_paths().unwrap_err();
11038            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
11039                panic!(
11040                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
11041                     got {err:?}"
11042                );
11043            };
11044            assert_eq!(slot, ":servicos");
11045            assert_eq!(path, PathBuf::from(relpath));
11046        }
11047    }
11048
11049    #[test]
11050    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
11051        // Positive-control sweep through every canonical authoring shape
11052        // every in-tree fixture and the `Caixa::template` scaffold use.
11053        // Mirrors the peer
11054        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
11055        // and the lifted predicate's own
11056        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
11057        // render.rs.
11058        for relpath in [
11059            "servicos/demo.computeunit.yaml",
11060            "servicos/hello-rio.computeunit.yaml",
11061            "servicos/my-service.computeunit.yaml",
11062            "servicos/a.computeunit.yaml",
11063            "./servicos/demo.computeunit.yaml",
11064            "servicos/./demo.computeunit.yaml",
11065            "servicos/sub/nested.computeunit.yaml",
11066            "servicos/v0.1.computeunit.yaml",
11067        ] {
11068            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
11069            c.validate_code_paths()
11070                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
11071        }
11072    }
11073
11074    #[test]
11075    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
11076        // The file-type gate is per-slot — only `:servicos` carries the
11077        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
11078        // entry and an extensionless `:exe` entry are the canonical
11079        // shapes every in-tree fixture uses, and must continue to pass
11080        // validate. Peer of
11081        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
11082        // (64772a9) — together pin that the typed
11083        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
11084        // cross-axis leakage in either direction.
11085        let c = caixa_with_code_paths(
11086            vec!["lib/demo.lisp"],
11087            vec!["exe/demo", "exe/tool"],
11088            vec!["servicos/demo.computeunit.yaml"],
11089        );
11090        c.validate_code_paths().unwrap();
11091    }
11092
11093    #[test]
11094    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
11095        // Cross-arm precedence pin: a `:servicos` entry that is *both*
11096        // sandbox-escaping and wrong-extension surfaces the more
11097        // fundamental sandbox-shape diagnostic first (the
11098        // `.computeunit.yaml` remediation would be misleading when the
11099        // offending path can never resolve under the caixa root
11100        // anyway). Mirrors the peer
11101        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
11102        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
11103        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
11104        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
11105        // table establishes.
11106        //
11107        // Empty wins (the strictly-smaller-scope structural arm).
11108        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
11109        assert!(
11110            matches!(
11111                c.validate_code_paths().unwrap_err(),
11112                ManifestError::CodePathEmpty { slot: ":servicos" }
11113            ),
11114            "empty must win over non-computeunit-yaml-extension",
11115        );
11116        // Absolute wins (the path can't resolve under the caixa root).
11117        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
11118        let err = c.validate_code_paths().unwrap_err();
11119        let ManifestError::CodePathAbsolute { slot, .. } = err else {
11120            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
11121        };
11122        assert_eq!(slot, ":servicos");
11123        // ParentEscape wins (the path escapes the caixa root).
11124        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
11125        let err = c.validate_code_paths().unwrap_err();
11126        let ManifestError::CodePathParentEscape { slot, .. } = err else {
11127            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
11128        };
11129        assert_eq!(slot, ":servicos");
11130    }
11131
11132    #[test]
11133    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
11134        // Within-slot precedence pin: the per-entry file-type shape gate
11135        // fires before the cross-entry duplicate gate, so the narrower
11136        // structural defect dominates the uniqueness diagnostic. A
11137        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
11138        // `CodePathNonComputeUnitYamlExtension` on the first entry
11139        // rather than `CodePathDuplicate` on the pair — same posture
11140        // every per-entry shape-gate-precedes-duplicate cascade follows
11141        // on this surface, peer of the 64772a9 `:bibliotecas`
11142        // `("lib/x.txt" "lib/x.txt")` ordering.
11143        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
11144        let err = c.validate_code_paths().unwrap_err();
11145        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
11146            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
11147        };
11148        assert_eq!(slot, ":servicos");
11149        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
11150    }
11151
11152    #[test]
11153    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
11154     {
11155        // Diagnostic-shape pin (peer with
11156        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
11157        // on the sibling tatara-lisp-source axis): the file-type-arm
11158        // Display surfaces both the offending `:slot` tag, the
11159        // offending path verbatim, and the expected
11160        // `.computeunit.yaml` compound suffix named in the remediation
11161        // text, so a `feira lint` run can render the diagnostic without
11162        // re-parsing.
11163        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
11164        let rendered = c.validate_code_paths().unwrap_err().to_string();
11165        assert!(
11166            rendered.contains(":servicos"),
11167            "diagnostic must name the offending slot: {rendered}",
11168        );
11169        assert!(
11170            rendered.contains("servicos/demo.yaml"),
11171            "diagnostic must quote the offending path: {rendered}",
11172        );
11173        assert!(
11174            rendered.contains(".computeunit.yaml"),
11175            "diagnostic must name the expected compound suffix: {rendered}",
11176        );
11177    }
11178
11179    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
11180
11181    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
11182        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11183        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
11184        c
11185    }
11186
11187    #[test]
11188    fn validate_etiquetas_accepts_empty_list() {
11189        // The empty-list identity: every caixa with no declared tags
11190        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
11191        // so the gate is non-disruptive against every existing manifest.
11192        let c = caixa_with_etiquetas(vec![]);
11193        c.validate_etiquetas().unwrap();
11194    }
11195
11196    #[test]
11197    fn validate_etiquetas_accepts_canonical_forms() {
11198        // Positive control sweep: a canonical-shaped non-empty distinct
11199        // tag list passes, mirroring the example checkout-aplicacao
11200        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
11201        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
11202        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
11203        c.validate_etiquetas().unwrap();
11204    }
11205
11206    #[test]
11207    fn validate_etiquetas_rejects_empty_entry() {
11208        // Canonical paste-from-blank-doc footgun. Without the gate the
11209        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
11210        // no-op tag indexing nothing in the future caixa-registry.
11211        let c = caixa_with_etiquetas(vec![""]);
11212        let err = c.validate_etiquetas().unwrap_err();
11213        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11214    }
11215
11216    #[test]
11217    fn validate_etiquetas_rejects_duplicate_entry() {
11218        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
11219        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
11220        // collect at chart render — a "second wins / one silently
11221        // disappears" shape divergent from every peer typed-graph set
11222        // gate. The duplicate-arm names the offending tag verbatim.
11223        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
11224        let err = c.validate_etiquetas().unwrap_err();
11225        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
11226            panic!("expected EtiquetaDuplicate, got {err:?}");
11227        };
11228        assert_eq!(etiqueta, "demo");
11229    }
11230
11231    #[test]
11232    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
11233        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
11234        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
11235        // structural "this entry has no value" defect dominates the
11236        // cross-entry uniqueness diagnostic. Mirrors the peer
11237        // empty-before-duplicate cascades on `:caracteristicas`
11238        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11239        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
11240        // `MembroDuplicate`).
11241        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
11242        let err = c.validate_etiquetas().unwrap_err();
11243        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11244    }
11245
11246    #[test]
11247    fn validate_etiquetas_duplicate_reports_first_collision() {
11248        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11249        // duplicate (the lexicographically-earliest offending position
11250        // — the second `"a"` at index 2 collides with the first `"a"`
11251        // at index 0), not the later `"b"` collision at index 3,
11252        // peer with every other first-collision diagnostic posture on
11253        // this surface (`validate_load_singularity_reports_first_collision`,
11254        // `validate_cleanup_singularity_reports_first_collision`).
11255        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
11256        let err = c.validate_etiquetas().unwrap_err();
11257        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
11258            panic!("expected EtiquetaDuplicate, got {err:?}");
11259        };
11260        assert_eq!(etiqueta, "a");
11261    }
11262
11263    #[test]
11264    fn validate_etiquetas_case_sensitive() {
11265        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
11266        // mirroring the peer `:membros :caixa` / `:children :caixa`
11267        // exact-string-match discipline. The shape gate this routine
11268        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
11269        // grammar) accepts mixed case — crates.io's keyword rule is
11270        // "case-insensitive" at the index layer but admits mixed case
11271        // at the entry layer (the canonical Helm chart `keywords:`
11272        // shape is lowercase by convention, but the grammar admits
11273        // uppercase). Case-sensitivity at the duplicate-set layer
11274        // remains structural — two distinct strings are two distinct
11275        // entries.
11276        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
11277        c.validate_etiquetas().unwrap();
11278    }
11279
11280    #[test]
11281    fn validate_etiquetas_diagnostic_carries_offending_tag() {
11282        // Diagnostic-shape pin (peer with
11283        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
11284        // the error's Display surfaces the offending tag verbatim, so a
11285        // `feira lint` run can render the diagnostic without re-parsing
11286        // and the author can grep their caixa.lisp for the offending
11287        // value.
11288        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
11289        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11290        assert!(
11291            rendered.contains(":etiquetas"),
11292            "diagnostic must name the offending slot: {rendered}",
11293        );
11294        assert!(
11295            rendered.contains("demo"),
11296            "diagnostic must quote the offending tag: {rendered}",
11297        );
11298    }
11299
11300    #[test]
11301    fn validate_etiquetas_rejects_leading_whitespace_entry() {
11302        // Canonical paste-from-aligned-doc footgun. Without the shape
11303        // gate `" mesh"` silently passed validate and landed as a
11304        // YAML plain-style scalar with leading whitespace in the
11305        // rendered Chart.yaml `keywords:` array — every YAML 1.2
11306        // dumper trims leading whitespace from plain-style scalars,
11307        // so the authored space round-tripped inconsistently back
11308        // through `caixa.lisp`. Mirrors the peer
11309        // `validate_autores_rejects_leading_whitespace_entry`.
11310        let c = caixa_with_etiquetas(vec![" mesh"]);
11311        let err = c.validate_etiquetas().unwrap_err();
11312        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11313            panic!("expected EtiquetaInvalid, got {err:?}");
11314        };
11315        assert_eq!(etiqueta, " mesh");
11316        assert!(reason.contains("whitespace"), "got: {reason}");
11317    }
11318
11319    #[test]
11320    fn validate_etiquetas_rejects_embedded_newline_entry() {
11321        // Canonical paste-from-multiline-doc footgun — the author
11322        // pasted a multi-tag block into one `:etiquetas` entry
11323        // instead of splitting into one entry per tag. Without the
11324        // shape gate `"mesh\nhttp"` silently passed validate and
11325        // landed as a YAML-illegal multi-line scalar in the rendered
11326        // Chart.yaml `keywords:` array.
11327        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11328        let err = c.validate_etiquetas().unwrap_err();
11329        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11330            panic!("expected EtiquetaInvalid, got {err:?}");
11331        };
11332        assert_eq!(etiqueta, "mesh\nhttp");
11333        assert!(reason.contains("newline"), "got: {reason}");
11334    }
11335
11336    #[test]
11337    fn validate_etiquetas_rejects_embedded_comma_entry() {
11338        // Canonical CSV-list-separator-confusion footgun: the author
11339        // confused the CSV-style separator convention with the
11340        // `:etiquetas` list grammar. Without the shape gate
11341        // `"mesh,http,grpc"` silently passed validate and landed as a
11342        // single malformed search tag in the rendered Chart.yaml
11343        // `keywords:` array — Artifact Hub's keyword index would
11344        // either silently drop the tag or index it as
11345        // `mesh,http,grpc` instead of three separate tags.
11346        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
11347        let err = c.validate_etiquetas().unwrap_err();
11348        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11349            panic!("expected EtiquetaInvalid, got {err:?}");
11350        };
11351        assert_eq!(etiqueta, "mesh,http,grpc");
11352        assert!(reason.contains('`'), "got: {reason}");
11353        assert!(reason.contains(','), "got: {reason}");
11354    }
11355
11356    #[test]
11357    fn validate_etiquetas_rejects_embedded_slash_entry() {
11358        // Canonical path-separator-confusion footgun: the author
11359        // confused namespace-path notation with the keyword grammar.
11360        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
11361        let err = c.validate_etiquetas().unwrap_err();
11362        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11363            panic!("expected EtiquetaInvalid, got {err:?}");
11364        };
11365        assert_eq!(etiqueta, "caixa/servico");
11366        assert!(reason.contains('/'), "got: {reason}");
11367    }
11368
11369    #[test]
11370    fn validate_etiquetas_rejects_leading_digit_entry() {
11371        // Canonical paste-from-numbered-list footgun: the author
11372        // copied `1. mesh` from a numbered doc and the `1` leaked
11373        // into the tag.
11374        let c = caixa_with_etiquetas(vec!["1mesh"]);
11375        let err = c.validate_etiquetas().unwrap_err();
11376        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11377            panic!("expected EtiquetaInvalid, got {err:?}");
11378        };
11379        assert_eq!(etiqueta, "1mesh");
11380        assert!(reason.contains("digit"), "got: {reason}");
11381    }
11382
11383    #[test]
11384    fn validate_etiquetas_rejects_leading_hyphen_entry() {
11385        // Canonical kebab-leak footgun.
11386        let c = caixa_with_etiquetas(vec!["-foo"]);
11387        let err = c.validate_etiquetas().unwrap_err();
11388        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11389            panic!("expected EtiquetaInvalid, got {err:?}");
11390        };
11391        assert_eq!(etiqueta, "-foo");
11392        assert!(reason.contains('-'), "got: {reason}");
11393    }
11394
11395    #[test]
11396    fn validate_etiquetas_rejects_non_ascii_entry() {
11397        // Canonical paste-from-Unicode-doc footgun. Every legitimate
11398        // search tag is strict ASCII; raw non-ASCII silently
11399        // round-trips inconsistently across NFC/NFD normalization on
11400        // APFS / case-folding filesystems and breaks the Artifact Hub
11401        // keyword search index lookup.
11402        let c = caixa_with_etiquetas(vec!["café"]);
11403        let err = c.validate_etiquetas().unwrap_err();
11404        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11405            panic!("expected EtiquetaInvalid, got {err:?}");
11406        };
11407        assert_eq!(etiqueta, "café");
11408        assert!(reason.contains("non-ASCII"), "got: {reason}");
11409    }
11410
11411    #[test]
11412    fn validate_etiquetas_rejects_period_entry() {
11413        // Canonical namespace-confusion / version-suffix footgun
11414        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
11415        // excludes `.` from the continuation set even though the
11416        // sibling `:caracteristicas` axis (Cargo's feature-name
11417        // grammar) admits it. Tighter than the sibling axis, peer
11418        // with Cargo's own crates.io keyword shape.
11419        let c = caixa_with_etiquetas(vec!["http.1"]);
11420        let err = c.validate_etiquetas().unwrap_err();
11421        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11422            panic!("expected EtiquetaInvalid, got {err:?}");
11423        };
11424        assert_eq!(etiqueta, "http.1");
11425        assert!(reason.contains('.'), "got: {reason}");
11426    }
11427
11428    #[test]
11429    fn validate_etiquetas_empty_takes_precedence_over_shape() {
11430        // Per-entry empty-first cascade pin: an entry that is both
11431        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
11432        // narrower "this entry has no value" structural defect
11433        // dominates the broader shape-predicate diagnostic). The
11434        // empty arm fires before the shape predicate is consulted,
11435        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
11436        // cascade established on the sibling universal-axis Vec<String>
11437        // surface.
11438        let c = caixa_with_etiquetas(vec![""]);
11439        let err = c.validate_etiquetas().unwrap_err();
11440        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11441    }
11442
11443    #[test]
11444    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
11445        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11446        // entry that is malformed surfaces `EtiquetaInvalid` even when
11447        // a later entry would have collided on duplicate. The
11448        // per-entry shape arm fires inside the same loop iteration as
11449        // the empty arm, before the seen-set insert at end-of-iteration
11450        // — structural per-entry defects dominate the cross-entry
11451        // uniqueness diagnostic. Mirrors the peer
11452        // `validate_autores_shape_takes_precedence_over_duplicate`.
11453        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
11454        let err = c.validate_etiquetas().unwrap_err();
11455        assert!(
11456            matches!(err, ManifestError::EtiquetaInvalid { .. }),
11457            "got {err:?}",
11458        );
11459    }
11460
11461    #[test]
11462    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
11463        // Diagnostic-shape pin on the new shape arm (peer with
11464        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
11465        // the rendered Display surfaces both the offending slot name
11466        // and the offending value verbatim, so a `feira lint` run
11467        // points the author at the exact `:etiquetas` entry to fix.
11468        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11469        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11470        assert!(
11471            rendered.contains(":etiquetas"),
11472            "diagnostic must name the offending slot: {rendered}",
11473        );
11474        assert!(
11475            rendered.contains("mesh\\nhttp"),
11476            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11477        );
11478    }
11479
11480    #[test]
11481    fn validate_etiquetas_rejects_at_21_byte_boundary() {
11482        // The 20-byte cap pin — boundary-exceeding case rejected,
11483        // boundary-accepting case passes. Mirrors the peer
11484        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
11485        // side pin, surfaced at the per-axis caller so the cap
11486        // propagates through validate end-to-end. Constructed as a
11487        // single all-`a` token so only the cap arm fires.
11488        let max_ok = "a".repeat(20);
11489        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
11490        c.validate_etiquetas().unwrap();
11491        let too_long = "a".repeat(21);
11492        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
11493        let err = c.validate_etiquetas().unwrap_err();
11494        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
11495            panic!("expected EtiquetaInvalid, got {err:?}");
11496        };
11497        assert!(reason.contains("20"), "got: {reason}");
11498        assert!(reason.contains("21"), "got: {reason}");
11499    }
11500
11501    #[test]
11502    fn validate_etiquetas_accepts_canonical_shaped_forms() {
11503        // Positive control sweep: every canonical-shaped tag from the
11504        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
11505        // example fixtures plus the substrate-fixed tags caixa-helm
11506        // unions in at chart render. Drift between this list and the
11507        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
11508        // sweep surfaces here — one source of truth for the rule.
11509        let c = caixa_with_etiquetas(vec![
11510            "example",
11511            "aplicacao",
11512            "mesh",
11513            "ecommerce",
11514            "demo",
11515            "infrastructure",
11516            "aws",
11517            "akeyless",
11518            "pangea-native",
11519            "hello-world",
11520            "wasm",
11521            "rust",
11522            "tatara-lisp",
11523            "caixa-servico",
11524            "lareira",
11525        ]);
11526        c.validate_etiquetas().unwrap();
11527    }
11528
11529    // ── validate_autores — universal-axis maintainer shape ────────────
11530
11531    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
11532        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11533        c.autores = autores.into_iter().map(String::from).collect();
11534        c
11535    }
11536
11537    #[test]
11538    fn validate_autores_accepts_empty_list() {
11539        // The empty-list identity: `Caixa::template` emits `:autores ()`,
11540        // so the gate is non-disruptive against every existing manifest.
11541        let c = caixa_with_autores(vec![]);
11542        c.validate_autores().unwrap();
11543    }
11544
11545    #[test]
11546    fn validate_autores_accepts_canonical_forms() {
11547        // Positive control sweep: every canonical-shaped non-empty
11548        // distinct maintainer list passes — the hello-rio / checkout-
11549        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
11550        // multi-author shape downstream packaging surfaces emit.
11551        let c = caixa_with_autores(vec!["pleme-io"]);
11552        c.validate_autores().unwrap();
11553        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
11554        c.validate_autores().unwrap();
11555    }
11556
11557    #[test]
11558    fn validate_autores_rejects_empty_entry() {
11559        // Canonical paste-from-blank-doc footgun. Without the gate the
11560        // empty entry rendered as `maintainers: [{name: "", email: null}]`
11561        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
11562        // to.
11563        let c = caixa_with_autores(vec![""]);
11564        let err = c.validate_autores().unwrap_err();
11565        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11566    }
11567
11568    #[test]
11569    fn validate_autores_rejects_duplicate_entry() {
11570        // Canonical copy-paste-the-wrong-author footgun. Unlike the
11571        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
11572        // dedups the rendered `keywords:` array), the `maintainers:`
11573        // rendering has *no* dedup — duplicates stack verbatim. The
11574        // duplicate-arm names the offending author verbatim.
11575        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11576        let err = c.validate_autores().unwrap_err();
11577        let ManifestError::AutorDuplicate { autor } = err else {
11578            panic!("expected AutorDuplicate, got {err:?}");
11579        };
11580        assert_eq!(autor, "pleme-io");
11581    }
11582
11583    #[test]
11584    fn validate_autores_empty_takes_precedence_over_duplicate() {
11585        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
11586        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
11587        // "this entry has no value" defect dominates the cross-entry
11588        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
11589        // cascades on `:etiquetas` (`EtiquetaEmpty` before
11590        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
11591        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11592        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
11593        // `MembroDuplicate`).
11594        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
11595        let err = c.validate_autores().unwrap_err();
11596        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11597    }
11598
11599    #[test]
11600    fn validate_autores_duplicate_reports_first_collision() {
11601        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11602        // duplicate (the lexicographically-earliest offending position
11603        // — the second `"a"` at index 2 collides with the first `"a"`
11604        // at index 0), not the later `"b"` collision at index 3,
11605        // peer with every other first-collision diagnostic posture on
11606        // this surface.
11607        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
11608        let err = c.validate_autores().unwrap_err();
11609        let ManifestError::AutorDuplicate { autor } = err else {
11610            panic!("expected AutorDuplicate, got {err:?}");
11611        };
11612        assert_eq!(autor, "a");
11613    }
11614
11615    #[test]
11616    fn validate_autores_case_sensitive() {
11617        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
11618        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
11619        // / `:children :caixa` exact-string-match discipline.
11620        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
11621        c.validate_autores().unwrap();
11622    }
11623
11624    #[test]
11625    fn validate_autores_diagnostic_carries_offending_author() {
11626        // Diagnostic-shape pin (peer with
11627        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
11628        // error's Display surfaces the offending author verbatim, so a
11629        // `feira lint` run can render the diagnostic without re-parsing
11630        // and the author can grep their caixa.lisp for the offending
11631        // value.
11632        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11633        let rendered = c.validate_autores().unwrap_err().to_string();
11634        assert!(
11635            rendered.contains(":autores"),
11636            "diagnostic must name the offending slot: {rendered}",
11637        );
11638        assert!(
11639            rendered.contains("pleme-io"),
11640            "diagnostic must quote the offending author: {rendered}",
11641        );
11642    }
11643
11644    #[test]
11645    fn validate_autores_rejects_leading_whitespace_entry() {
11646        // Canonical paste-from-aligned-doc footgun. Without the shape
11647        // gate `" pleme-io"` silently passed validate and landed as a
11648        // YAML plain-style scalar with leading whitespace in the
11649        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
11650        // dumper trims leading whitespace from plain-style scalars, so
11651        // the authored space round-tripped inconsistently back through
11652        // `caixa.lisp`. Mirrors the peer
11653        // `validate_descricao_rejects_leading_whitespace`.
11654        let c = caixa_with_autores(vec![" pleme-io"]);
11655        let err = c.validate_autores().unwrap_err();
11656        let ManifestError::AutorInvalid { autor, reason } = err else {
11657            panic!("expected AutorInvalid, got {err:?}");
11658        };
11659        assert_eq!(autor, " pleme-io");
11660        assert!(reason.contains("whitespace"), "got: {reason}");
11661    }
11662
11663    #[test]
11664    fn validate_autores_rejects_trailing_whitespace_entry() {
11665        // Canonical paste-from-doc footgun.
11666        let c = caixa_with_autores(vec!["pleme-io "]);
11667        let err = c.validate_autores().unwrap_err();
11668        let ManifestError::AutorInvalid { autor, reason } = err else {
11669            panic!("expected AutorInvalid, got {err:?}");
11670        };
11671        assert_eq!(autor, "pleme-io ");
11672        assert!(reason.contains("whitespace"), "got: {reason}");
11673    }
11674
11675    #[test]
11676    fn validate_autores_rejects_embedded_newline_entry() {
11677        // Canonical paste-from-multiline-doc footgun — the author
11678        // pasted a multi-line block of author records into one
11679        // `:autores` entry instead of splitting into one entry per
11680        // author. Without the shape gate `"alice\nbob"` silently
11681        // passed validate and landed as a YAML-illegal multi-line
11682        // scalar in the rendered Chart.yaml `maintainers:` array.
11683        let c = caixa_with_autores(vec!["alice\nbob"]);
11684        let err = c.validate_autores().unwrap_err();
11685        let ManifestError::AutorInvalid { autor, reason } = err else {
11686            panic!("expected AutorInvalid, got {err:?}");
11687        };
11688        assert_eq!(autor, "alice\nbob");
11689        assert!(reason.contains("newline"), "got: {reason}");
11690    }
11691
11692    #[test]
11693    fn validate_autores_rejects_embedded_carriage_return_entry() {
11694        // Canonical paste-from-Windows-CRLF-doc footgun.
11695        let c = caixa_with_autores(vec!["alice\rbob"]);
11696        let err = c.validate_autores().unwrap_err();
11697        let ManifestError::AutorInvalid { autor, reason } = err else {
11698            panic!("expected AutorInvalid, got {err:?}");
11699        };
11700        assert_eq!(autor, "alice\rbob");
11701        assert!(reason.contains("carriage return"), "got: {reason}");
11702    }
11703
11704    #[test]
11705    fn validate_autores_rejects_embedded_tab_entry() {
11706        // Canonical tab-from-aligned-doc footgun.
11707        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
11708        let err = c.validate_autores().unwrap_err();
11709        let ManifestError::AutorInvalid { autor, reason } = err else {
11710            panic!("expected AutorInvalid, got {err:?}");
11711        };
11712        assert_eq!(autor, "Pleme\tContributors");
11713        assert!(reason.contains("tab"), "got: {reason}");
11714    }
11715
11716    #[test]
11717    fn validate_autores_rejects_embedded_control_bytes_entry() {
11718        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
11719        // surface the same control-byte arm.
11720        for entry in [
11721            "alice\x00bob",
11722            "alice\x07bob",
11723            "alice\x1bbob",
11724            "alice\x7fbob",
11725        ] {
11726            let c = caixa_with_autores(vec![entry]);
11727            let err = c.validate_autores().unwrap_err();
11728            let ManifestError::AutorInvalid { autor, reason } = err else {
11729                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
11730            };
11731            assert_eq!(autor, entry);
11732            assert!(
11733                reason.contains("control character"),
11734                "{entry:?} reason: {reason}",
11735            );
11736        }
11737    }
11738
11739    #[test]
11740    fn validate_autores_accepts_unicode_entry() {
11741        // Unicode positive control: realistic maintainer names carry
11742        // Unicode (`François`, `日本語`, `naïve`). The predicate must
11743        // round-trip Unicode losslessly, peer with the
11744        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
11745        // sweep.
11746        let c = caixa_with_autores(vec![
11747            "François Dupont",
11748            "日本語の名前",
11749            "naïve <naive@example.com>",
11750        ]);
11751        c.validate_autores().unwrap();
11752    }
11753
11754    #[test]
11755    fn validate_autores_empty_takes_precedence_over_shape() {
11756        // Per-entry empty-first cascade pin: an entry that is both
11757        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
11758        // "this entry has no value" structural defect dominates the
11759        // broader shape-predicate diagnostic). The empty arm fires
11760        // before the shape predicate is consulted, mirroring the peer
11761        // `validate_repositorio_empty_takes_precedence_over_shape`
11762        // cascade on the universal `Option<String>` siblings — and now
11763        // established on the Vec<String> per-entry surface.
11764        let c = caixa_with_autores(vec![""]);
11765        let err = c.validate_autores().unwrap_err();
11766        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11767    }
11768
11769    #[test]
11770    fn validate_autores_shape_takes_precedence_over_duplicate() {
11771        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11772        // entry that is malformed surfaces `AutorInvalid` even when a
11773        // later entry would have collided on duplicate. The per-entry
11774        // shape arm fires inside the same loop iteration as the empty
11775        // arm, before the seen-set insert at end-of-iteration —
11776        // structural per-entry defects dominate the cross-entry
11777        // uniqueness diagnostic.
11778        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
11779        let err = c.validate_autores().unwrap_err();
11780        assert!(
11781            matches!(err, ManifestError::AutorInvalid { .. }),
11782            "got {err:?}",
11783        );
11784    }
11785
11786    #[test]
11787    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
11788        // Diagnostic-shape pin on the new shape arm (peer with
11789        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
11790        // the rendered Display surfaces both the offending slot name
11791        // and the offending value verbatim, so a `feira lint` run
11792        // points the author at the exact `:autores` entry to fix.
11793        let c = caixa_with_autores(vec!["alice\nbob"]);
11794        let rendered = c.validate_autores().unwrap_err().to_string();
11795        assert!(
11796            rendered.contains(":autores"),
11797            "diagnostic must name the offending slot: {rendered}",
11798        );
11799        assert!(
11800            rendered.contains("alice\\nbob"),
11801            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11802        );
11803    }
11804
11805    #[test]
11806    fn validate_autores_rejects_at_129_byte_boundary() {
11807        // The 128-byte cap pin — boundary-exceeding case rejected,
11808        // boundary-accepting case passes. Mirrors the peer
11809        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
11810        // substrate-side pin, surfaced at the per-axis caller so the
11811        // cap propagates through validate end-to-end. Constructed as
11812        // a single all-`a` token so only the cap arm fires.
11813        let max_ok = "a".repeat(128);
11814        let c = caixa_with_autores(vec![max_ok.as_str()]);
11815        c.validate_autores().unwrap();
11816        let too_long = "a".repeat(129);
11817        let c = caixa_with_autores(vec![too_long.as_str()]);
11818        let err = c.validate_autores().unwrap_err();
11819        let ManifestError::AutorInvalid { reason, .. } = err else {
11820            panic!("expected AutorInvalid, got {err:?}");
11821        };
11822        assert!(reason.contains("128"), "got: {reason}");
11823        assert!(reason.contains("129"), "got: {reason}");
11824    }
11825
11826    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
11827
11828    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
11829        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11830        c.repositorio = repositorio.map(String::from);
11831        c
11832    }
11833
11834    #[test]
11835    fn validate_repositorio_accepts_none() {
11836        // The omit-the-slot identity: `:repositorio` is optional. The
11837        // gate is a no-op when the author didn't declare a value —
11838        // every caixa without a `:repositorio` line trivially passes,
11839        // and the substrate-side renderers fall back to their
11840        // documented placeholder (`caixa-helm`'s `home: None`,
11841        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
11842        // URL). Mirrors the peer `validate_restart_window_accepts_none`
11843        // posture on the other `Option<String>` Caixa slot.
11844        let c = caixa_with_repositorio(None);
11845        c.validate_repositorio().unwrap();
11846    }
11847
11848    #[test]
11849    fn validate_repositorio_accepts_canonical_forms() {
11850        // Positive control sweep across every documented `:repositorio`
11851        // authoring shape — the same union the shared
11852        // `crate::render::is_git_repo_url` predicate accepts and the
11853        // peer `:deps :fonte :repo` axis already routes through.
11854        // Covers the `github:` shorthand (the canonical pleme-io
11855        // convention used in the `:repositorio` field of every
11856        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
11857        // `examples/`), the `https://…` URL the README quickstart uses,
11858        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
11859        // `file://` URL schemes the shared predicate documents.
11860        for repo in [
11861            "github:pleme-io/hello-rio",
11862            "github:pleme-io/checkout",
11863            "https://github.com/pleme-io/hello-rio",
11864            "ssh://git@github.com/pleme-io/hello-rio.git",
11865            "git://github.com/pleme-io/hello-rio.git",
11866            "git@github.com:pleme-io/hello-rio.git",
11867            "file:///srv/pleme/hello-rio",
11868        ] {
11869            let c = caixa_with_repositorio(Some(repo));
11870            c.validate_repositorio()
11871                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
11872        }
11873    }
11874
11875    #[test]
11876    fn validate_repositorio_rejects_empty_some() {
11877        // Canonical paste-from-blank-doc footgun. The narrower
11878        // [`ManifestError::RepositorioEmpty`] arm fires before the
11879        // shape predicate is consulted, mirroring the empty-first
11880        // cascade every peer per-axis identity gate uses
11881        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
11882        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
11883        // the empty `Some("")` silently passed the renderer's
11884        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
11885        // on `None`) and landed as `home: ""` in `Chart.yaml` /
11886        // `url: ""` in the FluxCD `GitRepository`.
11887        let c = caixa_with_repositorio(Some(""));
11888        let err = c.validate_repositorio().unwrap_err();
11889        assert!(
11890            matches!(err, ManifestError::RepositorioEmpty),
11891            "got {err:?}",
11892        );
11893    }
11894
11895    #[test]
11896    fn validate_repositorio_rejects_whitespace() {
11897        // Paste-from-doc whitespace footgun. The shared
11898        // `is_git_repo_url` predicate refuses any whitespace byte; a
11899        // trailing space in a `:repositorio` value silently broke
11900        // `git clone '<value> '` at clone time. The diagnostic names
11901        // the offending value verbatim.
11902        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
11903        let err = c.validate_repositorio().unwrap_err();
11904        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
11905            panic!("expected RepositorioInvalid, got {err:?}");
11906        };
11907        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
11908    }
11909
11910    #[test]
11911    fn validate_repositorio_rejects_control_char() {
11912        // Paste-from-multiline-doc CRLF footgun — control characters
11913        // at the URL boundary are a class of subprocess-arg injection
11914        // and break git's URL parser at every porcelain entry point.
11915        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
11916        let err = c.validate_repositorio().unwrap_err();
11917        assert!(
11918            matches!(err, ManifestError::RepositorioInvalid { .. }),
11919            "got {err:?}",
11920        );
11921    }
11922
11923    #[test]
11924    fn validate_repositorio_rejects_leading_dash() {
11925        // Canonical CLI-argument-injection footgun: `git clone <repo>`
11926        // interprets a leading `-` as a CLI flag, so a
11927        // `-upload-pack=…` value escapes the subprocess argument
11928        // boundary. The shared predicate refuses every leading-`-`
11929        // shape at validate time.
11930        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
11931        let err = c.validate_repositorio().unwrap_err();
11932        assert!(
11933            matches!(err, ManifestError::RepositorioInvalid { .. }),
11934            "got {err:?}",
11935        );
11936    }
11937
11938    #[test]
11939    fn validate_repositorio_rejects_missing_colon_separator() {
11940        // The bare `org/repo` ambiguity footgun — `git clone` reads
11941        // a no-`:` form as a relative filesystem path rather than the
11942        // GitHub-shorthand expansion the author probably intended.
11943        // The shared predicate refuses every shape without a `:`
11944        // separator.
11945        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
11946        let err = c.validate_repositorio().unwrap_err();
11947        assert!(
11948            matches!(err, ManifestError::RepositorioInvalid { .. }),
11949            "got {err:?}",
11950        );
11951    }
11952
11953    #[test]
11954    fn validate_repositorio_rejects_fragment_anchor() {
11955        // Paste-from-browser-address-bar footgun on the
11956        // `:repositorio` axis — an author copies a GitHub permalink
11957        // to a README section / line-permalink and forgets to trim
11958        // the `#fragment` tail. The shared `is_git_repo_url`
11959        // predicate refuses the byte at the URL-grammar layer
11960        // (libcurl strips the fragment before opening the
11961        // transport, so the byte rides verbatim into the rendered
11962        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
11963        // fields but is silently dropped on the wire — two
11964        // manifest variants whose values differ only in their
11965        // fragment anchor lock to two distinct rendered artifacts
11966        // for the byte-identical clone, defeating the THEORY.md
11967        // §V.2 render-determinism contract on the `:repositorio`
11968        // axis the peer `:fonte :repo` axis already closes).
11969        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
11970        let err = c.validate_repositorio().unwrap_err();
11971        let ManifestError::RepositorioInvalid {
11972            repositorio,
11973            reason,
11974        } = err
11975        else {
11976            panic!("expected RepositorioInvalid, got {err:?}");
11977        };
11978        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
11979        assert!(
11980            reason.contains("must not contain `#`"),
11981            "reason must surface the fragment-`#` arm, got {reason:?}"
11982        );
11983    }
11984
11985    #[test]
11986    fn validate_repositorio_rejects_query_string() {
11987        // Paste-from-browser-address-bar footgun on the
11988        // `:repositorio` axis (peer with the a68f818 fragment-`#`
11989        // arm on the same axis). An author copies a GitHub tab
11990        // deep-link out of the address bar and forgets to trim
11991        // the `?tab=…` query tail. The shared `is_git_repo_url`
11992        // predicate refuses the byte at the URL-grammar layer
11993        // (GitHub / GitLab / Bitbucket silently ignore the
11994        // `?query` tail and serve the same repo regardless, so
11995        // the byte rides verbatim into the rendered `Chart.yaml`
11996        // `home:` and FluxCD `GitRepository` `url:` fields but
11997        // is silently masked at the wire — two manifest variants
11998        // whose values differ only in their query tail lock to
11999        // two distinct rendered artifacts for the byte-identical
12000        // clone, defeating the THEORY.md §V.2 render-determinism
12001        // contract on the `:repositorio` axis the peer `:fonte
12002        // :repo` axis already closes).
12003        let c = caixa_with_repositorio(Some(
12004            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
12005        ));
12006        let err = c.validate_repositorio().unwrap_err();
12007        let ManifestError::RepositorioInvalid {
12008            repositorio,
12009            reason,
12010        } = err
12011        else {
12012            panic!("expected RepositorioInvalid, got {err:?}");
12013        };
12014        assert_eq!(
12015            repositorio,
12016            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
12017        );
12018        assert!(
12019            reason.contains("must not contain `?`"),
12020            "reason must surface the query-`?` arm, got {reason:?}"
12021        );
12022    }
12023
12024    #[test]
12025    fn validate_repositorio_rejects_embedded_backslash() {
12026        // Windows-file-path-confusion footgun on the `:repositorio`
12027        // axis (peer with the prior fragment-`#` / query-`?` arms on
12028        // the same axis, and peer with the new dep-level `:fonte :repo`
12029        // backslash arm on the URL-grammar trajectory). An author
12030        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
12031        // hello-rio` into the `:repositorio` slot, expecting the
12032        // `lareira-<nome>` chart's `home:` field and the FluxCD
12033        // `GitRepository` `url:` field to render the canonical local
12034        // file-URI. The shared `is_git_repo_url` predicate refuses
12035        // the byte at the URL-grammar layer (libcurl silently
12036        // translates `\` → `/` on some platforms and refuses it on
12037        // others, so the byte rides verbatim into the rendered
12038        // artifacts but is silently rewritten or rejected at the wire
12039        // — two manifest variants whose values differ only in
12040        // backslash-vs-forward-slash lock to two distinct rendered
12041        // artifacts for the byte-identical clone, defeating the
12042        // THEORY.md §V.2 render-determinism contract on the
12043        // `:repositorio` axis the peer `:fonte :repo` axis already
12044        // closes).
12045        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
12046        let err = c.validate_repositorio().unwrap_err();
12047        let ManifestError::RepositorioInvalid {
12048            repositorio,
12049            reason,
12050        } = err
12051        else {
12052            panic!("expected RepositorioInvalid, got {err:?}");
12053        };
12054        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
12055        assert!(
12056            reason.contains("must not contain `\\`"),
12057            "reason must surface the backslash-`\\` arm, got {reason:?}"
12058        );
12059    }
12060
12061    #[test]
12062    fn validate_repositorio_rejects_uri_template_placeholder() {
12063        // URI Template (RFC 6570) placeholder footgun on the
12064        // `:repositorio` axis (peer with the prior fragment-`#` /
12065        // query-`?` / backslash-`\` arms on the same axis, and peer
12066        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
12067        // URL-grammar trajectory). An author pastes a quick-start
12068        // README snippet / OpenAPI `servers:` URL / Helm chart
12069        // `home:` template carrying unresolved `{org}` / `{repo}`
12070        // placeholders into the `:repositorio` slot, expecting the
12071        // substrate to resolve the placeholder downstream. The
12072        // shared `is_git_repo_url` predicate refuses the byte at the
12073        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
12074        // `%7B` / `%7D` on the wire, so the byte round-trips
12075        // inconsistently between the rendered `Chart.yaml home:` /
12076        // FluxCD `GitRepository url:` and the resolver's `git clone`
12077        // invocation, defeating the THEORY.md §V.2 render-
12078        // determinism contract on the `:repositorio` axis the peer
12079        // `:fonte :repo` axis already closes; every git porcelain
12080        // entry-point additionally fetches a nonexistent literal-
12081        // `{placeholder}`-named path far from the source caixa.lisp).
12082        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
12083        let err = c.validate_repositorio().unwrap_err();
12084        let ManifestError::RepositorioInvalid {
12085            repositorio,
12086            reason,
12087        } = err
12088        else {
12089            panic!("expected RepositorioInvalid, got {err:?}");
12090        };
12091        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
12092        assert!(
12093            reason.contains("must not contain `{`"),
12094            "reason must surface the open-brace `{{` arm, got {reason:?}"
12095        );
12096        assert!(
12097            reason.contains("URI Template") || reason.contains("RFC 6570"),
12098            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
12099        );
12100    }
12101
12102    #[test]
12103    fn validate_repositorio_empty_takes_precedence_over_shape() {
12104        // Empty-first cascade pin: the empty `Some("")` surfaces the
12105        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
12106        // `RepositorioInvalid`, mirroring the peer
12107        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
12108        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
12109        // `is_git_repo_url` predicate also rejects the empty input
12110        // (defensively, with its own `"must not be empty"` reason),
12111        // but the manifest-layer empty arm runs first to surface the
12112        // narrower diagnostic verbatim.
12113        let c = caixa_with_repositorio(Some(""));
12114        let err = c.validate_repositorio().unwrap_err();
12115        assert!(
12116            matches!(err, ManifestError::RepositorioEmpty),
12117            "got {err:?}",
12118        );
12119    }
12120
12121    #[test]
12122    fn validate_repositorio_diagnostic_carries_offending_value() {
12123        // Diagnostic-shape pin (peer with
12124        // `validate_autores_diagnostic_carries_offending_author`): the
12125        // error's Display surfaces the offending value + slot name
12126        // verbatim, so a `feira lint` run can render the diagnostic
12127        // without re-parsing and the author can grep their caixa.lisp
12128        // for the offending `:repositorio` value.
12129        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
12130        let rendered = c.validate_repositorio().unwrap_err().to_string();
12131        assert!(
12132            rendered.contains(":repositorio"),
12133            "diagnostic must name the offending slot: {rendered}",
12134        );
12135        assert!(
12136            rendered.contains("pleme-io/hello-rio"),
12137            "diagnostic must quote the offending value: {rendered}",
12138        );
12139    }
12140
12141    // ── validate_descricao — universal-axis Chart.yaml description shape ──
12142
12143    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
12144        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12145        c.descricao = descricao.map(String::from);
12146        c
12147    }
12148
12149    #[test]
12150    fn validate_descricao_accepts_none() {
12151        // The omit-the-slot identity: `:descricao` is optional. The
12152        // gate is a no-op when the author didn't declare a value —
12153        // every caixa without a `:descricao` line trivially passes,
12154        // and the substrate-side renderers fall back to their
12155        // documented `caixa.nome`-derived placeholder. Mirrors the
12156        // peer `validate_repositorio_accepts_none` posture on the
12157        // sibling `Option<String>` Caixa slot.
12158        let c = caixa_with_descricao(None);
12159        c.validate_descricao().unwrap();
12160    }
12161
12162    #[test]
12163    fn validate_descricao_accepts_canonical_summary() {
12164        // Positive control: the canonical pleme-io descricao shape —
12165        // a short free-form prose summary — passes the gate. Covers
12166        // the fixture shapes the `caixa-helm` / `caixa-flux` /
12167        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
12168        // wasip2 caixa Servico."`, `"Checkout flow."`).
12169        for desc in [
12170            "Canonical Rust→wasm32-wasip2 caixa Servico.",
12171            "Checkout flow.",
12172            "AWS provider caixa for tatara-lisp",
12173            "FIXME — describe this caixa",
12174            "x",
12175        ] {
12176            let c = caixa_with_descricao(Some(desc));
12177            c.validate_descricao()
12178                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
12179        }
12180    }
12181
12182    #[test]
12183    fn validate_descricao_rejects_empty_some() {
12184        // Canonical paste-from-blank-doc footgun. Without this gate
12185        // the empty `Some("")` silently passed the renderer's
12186        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
12187        // on `None`) and landed as `description: ""` in `Chart.yaml`
12188        // and a blank `README.md` header. Mirrors the peer
12189        // [`ManifestError::RepositorioEmpty`] empty-arm on the
12190        // sibling `Option<String>` Caixa slot.
12191        let c = caixa_with_descricao(Some(""));
12192        let err = c.validate_descricao().unwrap_err();
12193        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
12194    }
12195
12196    #[test]
12197    fn validate_descricao_rejects_leading_whitespace() {
12198        // Paste-from-aligned-doc footgun: a leading ASCII space the
12199        // bare empty-arm gate accepted, the shape predicate now
12200        // refuses. The diagnostic carries the offending value
12201        // verbatim (with the leading space preserved) so the author
12202        // can grep their caixa.lisp for the exact `:descricao` line
12203        // and fix the round-trip-inconsistent leading whitespace.
12204        // Mirrors the peer
12205        // `validate_licenca_rejects_leading_whitespace` arm on the
12206        // sibling `:licenca` axis.
12207        let c = caixa_with_descricao(Some(" Checkout flow."));
12208        let err = c.validate_descricao().unwrap_err();
12209        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
12210            panic!("expected DescricaoInvalid, got {err:?}");
12211        };
12212        assert_eq!(descricao, " Checkout flow.");
12213        assert!(reason.contains("whitespace"), "got: {reason:?}");
12214    }
12215
12216    #[test]
12217    fn validate_descricao_rejects_trailing_whitespace() {
12218        // Paste-from-doc footgun: a trailing ASCII space the bare
12219        // empty-arm gate accepted, the shape predicate now refuses.
12220        let c = caixa_with_descricao(Some("Checkout flow. "));
12221        let err = c.validate_descricao().unwrap_err();
12222        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
12223            panic!("expected DescricaoInvalid, got {err:?}");
12224        };
12225        assert_eq!(descricao, "Checkout flow. ");
12226        assert!(reason.contains("whitespace"), "got: {reason:?}");
12227    }
12228
12229    #[test]
12230    fn validate_descricao_rejects_embedded_newline() {
12231        // Paste-from-multiline-doc footgun: an embedded LF the bare
12232        // empty-arm gate accepted, the shape predicate now refuses.
12233        // Without this gate the embedded newline silently landed in
12234        // the rendered Chart.yaml as a multi-line YAML block scalar,
12235        // and every chart-aware UI (`helm list`, `helm search`,
12236        // Artifact Hub) renders the description in a single-line
12237        // column so the embedded newline is silently dropped at
12238        // every downstream consumer.
12239        let c = caixa_with_descricao(Some("Checkout\nflow."));
12240        let err = c.validate_descricao().unwrap_err();
12241        assert!(
12242            matches!(err, ManifestError::DescricaoInvalid { .. }),
12243            "got {err:?}",
12244        );
12245        assert!(err.to_string().contains("newline"), "got {err}");
12246    }
12247
12248    #[test]
12249    fn validate_descricao_rejects_embedded_carriage_return() {
12250        // Paste-from-Windows-CRLF-doc footgun.
12251        let c = caixa_with_descricao(Some("Checkout\rflow."));
12252        let err = c.validate_descricao().unwrap_err();
12253        assert!(
12254            matches!(err, ManifestError::DescricaoInvalid { .. }),
12255            "got {err:?}",
12256        );
12257        assert!(err.to_string().contains("carriage return"), "got {err}");
12258    }
12259
12260    #[test]
12261    fn validate_descricao_rejects_embedded_tab() {
12262        // Tab-from-aligned-doc footgun.
12263        let c = caixa_with_descricao(Some("Checkout\tflow."));
12264        let err = c.validate_descricao().unwrap_err();
12265        assert!(
12266            matches!(err, ManifestError::DescricaoInvalid { .. }),
12267            "got {err:?}",
12268        );
12269        assert!(err.to_string().contains("tab"), "got {err}");
12270    }
12271
12272    #[test]
12273    fn validate_descricao_rejects_embedded_control_bytes() {
12274        // Paste-from-binary-blob footgun: every other control byte
12275        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
12276        // the peer SPDX-expression control-byte arm.
12277        for s in [
12278            "Checkout\x00flow.",
12279            "Checkout\x07flow.",
12280            "Checkout\x1bflow.",
12281            "Checkout\x7fflow.",
12282        ] {
12283            let c = caixa_with_descricao(Some(s));
12284            let err = c.validate_descricao().unwrap_err();
12285            assert!(
12286                matches!(err, ManifestError::DescricaoInvalid { .. }),
12287                "{s:?} got {err:?}",
12288            );
12289            assert!(
12290                err.to_string().contains("control character"),
12291                "{s:?} got {err}",
12292            );
12293        }
12294    }
12295
12296    #[test]
12297    fn validate_descricao_accepts_unicode_prose() {
12298        // Positive control: Unicode prose is accepted — the
12299        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
12300        // and `Caixa::template`'s `"FIXME — describe this caixa"`
12301        // scaffold every `feira init` emits must continue to pass.
12302        for s in [
12303            "Canonical Rust→wasm32-wasip2 caixa Servico.",
12304            "FIXME — describe this caixa",
12305            "Caixa pour le projet tâche",
12306            "日本語の説明",
12307        ] {
12308            let c = caixa_with_descricao(Some(s));
12309            c.validate_descricao()
12310                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
12311        }
12312    }
12313
12314    #[test]
12315    fn validate_descricao_empty_takes_precedence_over_shape() {
12316        // Cascade pin: a `Some("")` surfaces the narrower
12317        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
12318        // shape-predicate arm. Mirrors the peer
12319        // `validate_licenca_empty_takes_precedence_over_shape` pin
12320        // on the sibling `:licenca` axis.
12321        let c = caixa_with_descricao(Some(""));
12322        let err = c.validate_descricao().unwrap_err();
12323        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
12324    }
12325
12326    #[test]
12327    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
12328        // Diagnostic-shape pin: the error's Display surfaces both
12329        // the `:descricao` slot name and the offending value
12330        // verbatim, so a `feira lint` run can render the diagnostic
12331        // without re-parsing and the author can grep their caixa.lisp
12332        // for the offending `:descricao` line. Mirrors the peer
12333        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
12334        // pin (ee2e888) on the sibling `:licenca` axis.
12335        // The `{descricao:?}` Debug format escapes embedded control
12336        // bytes; the quoted offending value surfaces as
12337        // `"Checkout\nflow."` (literal backslash-n) in the rendered
12338        // diagnostic. The author can grep their caixa.lisp for the
12339        // literal `Checkout` summary prefix.
12340        let c = caixa_with_descricao(Some("Checkout\nflow."));
12341        let rendered = c.validate_descricao().unwrap_err().to_string();
12342        assert!(
12343            rendered.contains(":descricao"),
12344            "diagnostic must name the offending slot: {rendered}",
12345        );
12346        assert!(
12347            rendered.contains("Checkout\\nflow."),
12348            "diagnostic must quote the offending value (debug-escaped): {rendered}",
12349        );
12350    }
12351
12352    #[test]
12353    fn validate_descricao_template_passes() {
12354        // Round-trip pin: the bare `Caixa::template` shape carries
12355        // `:descricao "FIXME — describe this caixa"` (a non-empty
12356        // sentinel), so the template-derived Caixa passes the gate by
12357        // construction. A future template-shape change that omits or
12358        // empties `:descricao` would surface here as a regression.
12359        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12360        c.validate_descricao().unwrap();
12361    }
12362
12363    #[test]
12364    fn validate_descricao_diagnostic_names_offending_slot() {
12365        // Diagnostic-shape pin (peer with
12366        // `validate_repositorio_diagnostic_carries_offending_value`):
12367        // the error's Display surfaces the `:descricao` slot name
12368        // verbatim, so a `feira lint` run can render the diagnostic
12369        // without re-parsing and the author can grep their caixa.lisp
12370        // for the offending `:descricao` line.
12371        let c = caixa_with_descricao(Some(""));
12372        let rendered = c.validate_descricao().unwrap_err().to_string();
12373        assert!(
12374            rendered.contains(":descricao"),
12375            "diagnostic must name the offending slot: {rendered}",
12376        );
12377    }
12378
12379    // ── validate_licenca — universal-axis chart README license shape ──
12380
12381    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
12382        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12383        c.licenca = licenca.map(String::from);
12384        c
12385    }
12386
12387    #[test]
12388    fn validate_licenca_accepts_none() {
12389        // The omit-the-slot identity: `:licenca` is optional. The
12390        // gate is a no-op when the author didn't declare a value —
12391        // every caixa without a `:licenca` line trivially passes,
12392        // and the substrate-side `caixa-helm` renderer falls back to
12393        // the documented `"MIT"` placeholder. Mirrors the peer
12394        // `validate_descricao_accepts_none` posture on the sibling
12395        // `Option<String>` Caixa slot.
12396        let c = caixa_with_licenca(None);
12397        c.validate_licenca().unwrap();
12398    }
12399
12400    #[test]
12401    fn validate_licenca_accepts_canonical_expressions() {
12402        // Positive control: every canonical SPDX expression shape
12403        // pleme-io carries in its existing fixtures + the canonical
12404        // SPDX dual-license / with-exception / `+`-suffix / grouped /
12405        // user-defined-reference shapes all pass the gate. Covers
12406        // the single-license, `OR`-compound, `AND`-compound,
12407        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
12408        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
12409        // production the SPDX 2.1 expression grammar admits that
12410        // sits within the alphabet floor the
12411        // `is_spdx_expression_shape` predicate enforces.
12412        for lic in [
12413            "MIT",
12414            "Apache-2.0",
12415            "Apache-2.0 OR MIT",
12416            "Apache-2.0 AND MIT",
12417            "BSD-3-Clause",
12418            "MPL-2.0",
12419            "GPL-3.0-or-later",
12420            "GPL-2.0+",
12421            "Apache-2.0 WITH LLVM-exception",
12422            "(MIT OR Apache-2.0) AND BSD-3-Clause",
12423            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
12424            "LicenseRef-MyLicense",
12425            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
12426            "x",
12427        ] {
12428            let c = caixa_with_licenca(Some(lic));
12429            c.validate_licenca()
12430                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
12431        }
12432    }
12433
12434    #[test]
12435    fn validate_licenca_rejects_trailing_whitespace() {
12436        // Paste-from-doc whitespace footgun. A trailing space in the
12437        // `:licenca` value would silently break a downstream SPDX
12438        // parser that splits on exact `AND` / `OR` / `WITH` keyword
12439        // boundaries. The shape predicate refuses every trailing
12440        // whitespace byte by construction. Peer with
12441        // `validate_repositorio_rejects_whitespace` and
12442        // `validate_edicao_rejects_trailing_whitespace`.
12443        let c = caixa_with_licenca(Some("MIT "));
12444        let err = c.validate_licenca().unwrap_err();
12445        let ManifestError::LicencaInvalid { licenca, .. } = err else {
12446            panic!("expected LicencaInvalid, got {err:?}");
12447        };
12448        assert_eq!(licenca, "MIT ");
12449    }
12450
12451    #[test]
12452    fn validate_licenca_rejects_leading_whitespace() {
12453        // Symmetric paste-from-doc whitespace footgun on the leading
12454        // boundary — the gate refuses every shape that starts with a
12455        // space byte by construction. Peer with
12456        // `validate_edicao_rejects_leading_whitespace`.
12457        let c = caixa_with_licenca(Some(" MIT"));
12458        let err = c.validate_licenca().unwrap_err();
12459        assert!(
12460            matches!(err, ManifestError::LicencaInvalid { .. }),
12461            "got {err:?}",
12462        );
12463    }
12464
12465    #[test]
12466    fn validate_licenca_rejects_control_char() {
12467        // Paste-from-multiline-doc CRLF footgun — control characters
12468        // at the value boundary land as a malformed line in the
12469        // rendered chart `README.md` `## License` section. Peer with
12470        // `validate_repositorio_rejects_control_char` and
12471        // `validate_edicao_rejects_control_char`.
12472        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
12473            let c = caixa_with_licenca(Some(lic));
12474            let err = c.validate_licenca().unwrap_err();
12475            assert!(
12476                matches!(err, ManifestError::LicencaInvalid { .. }),
12477                "expected LicencaInvalid on {lic:?}, got {err:?}",
12478            );
12479        }
12480    }
12481
12482    #[test]
12483    fn validate_licenca_rejects_tab() {
12484        // Tab-from-aligned-doc footgun — SPDX expressions use a
12485        // single ASCII space between tokens; a tab breaks every
12486        // downstream SPDX parser that splits on exact `" "`
12487        // boundaries.
12488        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
12489        let err = c.validate_licenca().unwrap_err();
12490        assert!(
12491            matches!(err, ManifestError::LicencaInvalid { .. }),
12492            "got {err:?}",
12493        );
12494    }
12495
12496    #[test]
12497    fn validate_licenca_rejects_non_ascii() {
12498        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
12499        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
12500        // ".")` production. The shape predicate refuses every
12501        // non-ASCII byte by construction; peer with
12502        // `validate_edicao_rejects_non_ascii_lookalike`.
12503        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
12504            let c = caixa_with_licenca(Some(lic));
12505            let err = c.validate_licenca().unwrap_err();
12506            assert!(
12507                matches!(err, ManifestError::LicencaInvalid { .. }),
12508                "expected LicencaInvalid on {lic:?}, got {err:?}",
12509            );
12510        }
12511    }
12512
12513    #[test]
12514    fn validate_licenca_rejects_underscore() {
12515        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
12516        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
12517        // snake-case identifier conventions that don't apply to the
12518        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
12519        // "-" / "."`). The shape predicate refuses every underscore
12520        // byte by construction.
12521        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
12522            let c = caixa_with_licenca(Some(lic));
12523            let err = c.validate_licenca().unwrap_err();
12524            assert!(
12525                matches!(err, ManifestError::LicencaInvalid { .. }),
12526                "expected LicencaInvalid on {lic:?}, got {err:?}",
12527            );
12528        }
12529    }
12530
12531    #[test]
12532    fn validate_licenca_rejects_comma_separator() {
12533        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
12534        // SPDX expressions compose multiple licenses via `AND` / `OR`
12535        // keywords, not the comma separator. The shape predicate
12536        // refuses every comma byte by construction.
12537        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
12538            let c = caixa_with_licenca(Some(lic));
12539            let err = c.validate_licenca().unwrap_err();
12540            assert!(
12541                matches!(err, ManifestError::LicencaInvalid { .. }),
12542                "expected LicencaInvalid on {lic:?}, got {err:?}",
12543            );
12544        }
12545    }
12546
12547    #[test]
12548    fn validate_licenca_rejects_slash_dual_license() {
12549        // Slash-dual-license colloquial idiom footgun — the
12550        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
12551        // `package.license` field but non-SPDX; the SPDX equivalent
12552        // is `MIT OR Apache-2.0`. The shape predicate refuses every
12553        // forward-slash byte by construction.
12554        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
12555            let c = caixa_with_licenca(Some(lic));
12556            let err = c.validate_licenca().unwrap_err();
12557            assert!(
12558                matches!(err, ManifestError::LicencaInvalid { .. }),
12559                "expected LicencaInvalid on {lic:?}, got {err:?}",
12560            );
12561        }
12562    }
12563
12564    #[test]
12565    fn validate_licenca_rejects_semicolon_separator() {
12566        // Semicolon-list-separator confusion footgun — adjacent to
12567        // the comma-separator idiom, every list-separator-belongs-
12568        // to-list-grammar confusion lands here.
12569        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
12570        let err = c.validate_licenca().unwrap_err();
12571        assert!(
12572            matches!(err, ManifestError::LicencaInvalid { .. }),
12573            "got {err:?}",
12574        );
12575    }
12576
12577    #[test]
12578    fn validate_licenca_empty_takes_precedence_over_shape() {
12579        // Empty-first cascade pin: the empty `Some("")` surfaces the
12580        // narrower `LicencaEmpty` not the shape-predicate-wrapped
12581        // `LicencaInvalid`, mirroring the peer
12582        // `validate_edicao_empty_takes_precedence_over_shape` and
12583        // `validate_repositorio_empty_takes_precedence_over_shape`
12584        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
12585        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
12586        // The shape predicate also refuses the empty input
12587        // (defensively — `"must not be empty"`), but the manifest-
12588        // layer empty arm runs first to surface the narrower
12589        // diagnostic verbatim.
12590        let c = caixa_with_licenca(Some(""));
12591        let err = c.validate_licenca().unwrap_err();
12592        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12593    }
12594
12595    #[test]
12596    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
12597        // Diagnostic-shape pin on the shape-predicate arm (peer with
12598        // `validate_edicao_invalid_diagnostic_carries_offending_value`
12599        // and `validate_repositorio_diagnostic_carries_offending_value`):
12600        // the error's Display surfaces the offending value + slot
12601        // name verbatim, so a `feira lint` run can render the
12602        // diagnostic without re-parsing and the author can grep
12603        // their caixa.lisp for the offending `:licenca` value.
12604        let c = caixa_with_licenca(Some("Apache_2.0"));
12605        let rendered = c.validate_licenca().unwrap_err().to_string();
12606        assert!(
12607            rendered.contains(":licenca"),
12608            "diagnostic must name the offending slot: {rendered}",
12609        );
12610        assert!(
12611            rendered.contains("Apache_2.0"),
12612            "diagnostic must quote the offending value: {rendered}",
12613        );
12614    }
12615
12616    #[test]
12617    fn validate_licenca_rejects_empty_some() {
12618        // Canonical paste-from-blank-doc footgun. Without this gate
12619        // the empty `Some("")` silently passed the renderer's
12620        // `Option::unwrap_or_else(|| "MIT".into())` (which only
12621        // fires on `None`) and landed as a bare trailing period in
12622        // the rendered chart `README.md` `## License` section.
12623        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
12624        // arm on the sibling `Option<String>` Caixa slot.
12625        let c = caixa_with_licenca(Some(""));
12626        let err = c.validate_licenca().unwrap_err();
12627        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12628    }
12629
12630    #[test]
12631    fn validate_licenca_template_passes() {
12632        // Round-trip pin: the bare `Caixa::template` shape (whether
12633        // it carries `:licenca` or omits it) passes the gate by
12634        // construction. A future template-shape change that
12635        // introduced `(:licenca "")` would surface here as a
12636        // regression. Mirrors the peer
12637        // `validate_descricao_template_passes` pin.
12638        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12639        c.validate_licenca().unwrap();
12640    }
12641
12642    #[test]
12643    fn validate_licenca_diagnostic_names_offending_slot() {
12644        // Diagnostic-shape pin (peer with
12645        // `validate_descricao_diagnostic_names_offending_slot`):
12646        // the error's Display surfaces the `:licenca` slot name
12647        // verbatim, so a `feira lint` run can render the diagnostic
12648        // without re-parsing and the author can grep their caixa.lisp
12649        // for the offending `:licenca` line.
12650        let c = caixa_with_licenca(Some(""));
12651        let rendered = c.validate_licenca().unwrap_err().to_string();
12652        assert!(
12653            rendered.contains(":licenca"),
12654            "diagnostic must name the offending slot: {rendered}",
12655        );
12656    }
12657
12658    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
12659
12660    #[test]
12661    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
12662        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
12663        // pin: [`Caixa::licenca`] must return the `:licenca` typed
12664        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12665        // raw `self.licenca.as_deref()` access across every
12666        // representative value in the accept-set — `None` (the "omit
12667        // the slot to defer to the caixa-helm renderer's `MIT`
12668        // fallback" arm every existing fixture without a `:licenca`
12669        // line carries), `Some("")` (a past-the-guard sentinel that
12670        // pins the accessor doesn't perform a silent
12671        // `Some("") → None` collapse on the empty arm — validate
12672        // rejects `Some("")` through `LicencaEmpty` but the accessor
12673        // must ship the raw slot verbatim so a validate-time gate
12674        // regression surfaces at the caixa-helm emit boundary rather
12675        // than being silently absorbed into the fallback), `Some("MIT")`
12676        // (the canonical single-license shape every `feira init`
12677        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
12678        // canonical `OR`-compound shape the peer
12679        // `validate_licenca_accepts_canonical_expressions` positive
12680        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
12681        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
12682        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
12683        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
12684        // guard sentinels — validate rejects each through
12685        // `LicencaInvalid` but the accessor must ship the raw slot
12686        // verbatim).
12687        //
12688        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
12689        // accessor pin on the substrate primitive — opens the "outer
12690        // [`Caixa`] `Option<&str>` scalar" projection pattern the
12691        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
12692        // future lifts fold on. Sibling in shape to the peer per-`:placement`
12693        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12694        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12695        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12696        // axes, extended onto the outer top-level [`Caixa`] universal-
12697        // axis surface. Pins against a future silent detour that
12698        // returned an owned `Option<String>` (which would type-check
12699        // but silently allocate on every accessor call, breaking the
12700        // zero-cost projection every peer sibling accessor carries), a
12701        // `Some("") → None` collapse (which would silently absorb the
12702        // `LicencaEmpty` refusal case at the accessor boundary and the
12703        // caixa-helm emit path would silently fall back to `"MIT"` on
12704        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
12705        // `None → Some("MIT")` collapse (which would silently reify
12706        // the caixa-helm renderer's `"MIT"` fallback at the accessor
12707        // boundary and every downstream consumer keying off the
12708        // `Option::is_none()` discriminator would lose the "author
12709        // omitted the slot" signal).
12710        for licenca in [
12711            None,
12712            Some(""),
12713            Some("MIT"),
12714            Some("Apache-2.0 OR MIT"),
12715            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
12716            Some("MIT "),
12717            Some(" MIT"),
12718            Some("MIT\n"),
12719            Some("Apache_2.0"),
12720            Some("MIT,Apache-2.0"),
12721        ] {
12722            let c = caixa_with_licenca(licenca);
12723            assert_eq!(
12724                c.licenca(),
12725                licenca,
12726                "Caixa::licenca must return :licenca verbatim (got {:?}, \
12727                 expected {licenca:?})",
12728                c.licenca(),
12729            );
12730            assert_eq!(
12731                c.licenca(),
12732                c.licenca.as_deref(),
12733                "Caixa::licenca must byte-equal the raw \
12734                 `self.licenca.as_deref()` field access across every \
12735                 value in the Option<&str> accept-set",
12736            );
12737        }
12738    }
12739
12740    #[test]
12741    fn validate_licenca_empty_arm_routes_through_accessor() {
12742        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
12743        // must key off [`Caixa::licenca`], not the raw
12744        // `self.licenca.as_deref()` field access. Structurally: a
12745        // `Caixa { licenca: Some(""), .. }` must surface the
12746        // `LicencaEmpty` refusal exactly, and a
12747        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
12748        // single-license form) must pass validate. The pair jointly
12749        // pins the accessor + validate-gate composition: any future
12750        // silent detour that had the accessor return `None` on the
12751        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
12752        // silently absorb the `LicencaEmpty` refusal at the accessor
12753        // boundary and the validate gate would accept a struct-literal
12754        // `Caixa { licenca: Some(""), .. }` — the composition pin
12755        // catches that at caixa-core build time.
12756        //
12757        // Peer of the per-`:politicas :circuit-breaker`
12758        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12759        // accessor-composition pin
12760        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12761        // on the sibling per-M3-mesh-slot required-`u32` axis — same
12762        // "the validate / shape-gate predicate must route through the
12763        // substrate-primitive typed dispatch" discipline extended onto
12764        // the outer top-level [`Caixa`] universal-axis
12765        // `Option<&str>`-composition surface.
12766        let c = caixa_with_licenca(Some(""));
12767        assert!(
12768            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
12769            "validate_licenca must reject licenca == Some(\"\") with \
12770             LicencaEmpty — the accessor and the validate gate must \
12771             route through the same substrate-primitive typed dispatch \
12772             on the :licenca empty arm",
12773        );
12774        let c = caixa_with_licenca(Some("MIT"));
12775        assert!(
12776            c.validate_licenca().is_ok(),
12777            "validate_licenca must accept licenca == Some(\"MIT\") \
12778             (the canonical single-license SPDX shape)",
12779        );
12780    }
12781
12782    #[test]
12783    fn licenca_projects_option_str_by_borrow() {
12784        // The by-borrow pin: [`Caixa::licenca`] returns
12785        // `Option<&str>` by borrow — the `&str` borrows the underlying
12786        // `String` storage of the `Option<String>` slot and the
12787        // accessor must not allocate a fresh `String` on every call.
12788        // Peer of the per-`:placement`
12789        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12790        // borrow pin on the peer per-M3-mesh-slot
12791        // `Option<&str>`-return axis, extended onto the outer top-
12792        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12793        // accessor's returned `&str` must borrow from `&self` (the
12794        // returned reference's lifetime is tied to `&self`), and
12795        // calling the accessor twice on the same [`Caixa`] must yield
12796        // the same `Option<&str>` verbatim (idempotent, no side
12797        // effects on `&self`).
12798        //
12799        // Pins against a future silent detour that returned an owned
12800        // `Option<String>` (which would type-check but silently
12801        // allocate on every call, breaking the zero-cost projection
12802        // every peer sibling accessor carries), or a one-arm-only
12803        // accessor that returned a saturating value on some sentinel
12804        // input (breaking the pass-through invariant the sibling
12805        // required-scalar accessors carry).
12806        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
12807            let c = caixa_with_licenca(licenca);
12808            let first = c.licenca();
12809            let second = c.licenca();
12810            assert_eq!(
12811                first, second,
12812                "Caixa::licenca must be idempotent — two successive \
12813                 calls on the same &self must return the same \
12814                 Option<&str>",
12815            );
12816            assert_eq!(
12817                first, licenca,
12818                "Caixa::licenca must return :licenca verbatim by \
12819                 borrow — got {first:?}, expected {licenca:?}",
12820            );
12821        }
12822    }
12823
12824    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
12825
12826    #[test]
12827    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
12828        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
12829        // pin: [`Caixa::repositorio`] must return the `:repositorio`
12830        // typed byte-string verbatim as an `Option<&str>`, byte-equal
12831        // to the raw `self.repositorio.as_deref()` access across every
12832        // representative value in the accept-set — `None` (the "omit
12833        // the slot to defer to the per-renderer placeholder" arm every
12834        // existing fixture without a `:repositorio` line carries),
12835        // `Some("")` (a past-the-guard sentinel that pins the accessor
12836        // doesn't perform a silent `Some("") → None` collapse on the
12837        // empty arm — validate rejects `Some("")` through
12838        // `RepositorioEmpty` but the accessor must ship the raw slot
12839        // verbatim so a validate-time gate regression surfaces at the
12840        // caixa-helm / caixa-flux emit boundary rather than being
12841        // silently absorbed into the per-renderer fallback),
12842        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
12843        // shorthand every existing manifest fixture across
12844        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
12845        // `Some("https://github.com/pleme-io/checkout")` (the canonical
12846        // `https://` URL the README quickstart uses),
12847        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
12848        // `Some("git://github.com/pleme-io/checkout.git")` /
12849        // `Some("git@github.com:pleme-io/checkout.git")` /
12850        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
12851        // github scheme the shared `is_git_repo_url` predicate
12852        // documents), and five past-the-guard sentinels for the
12853        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
12854        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
12855        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
12856        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
12857        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
12858        // sentinels pin the accessor doesn't silently absorb the
12859        // refusal cases into a fallback).
12860        //
12861        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
12862        // accessor pin on the substrate primitive — sibling of the peer
12863        // [`Caixa::licenca`] (6d5bc28) pin
12864        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
12865        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
12866        // projection pin pattern this pin folds on. Sibling in shape to
12867        // the peer per-`:placement`
12868        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12869        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12870        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12871        // axes, extended onto the outer top-level [`Caixa`] universal-
12872        // axis surface. Pins against a future silent detour that
12873        // returned an owned `Option<String>` (which would type-check
12874        // but silently allocate on every accessor call, breaking the
12875        // zero-cost projection every peer sibling accessor carries), a
12876        // `Some("") → None` collapse (which would silently absorb the
12877        // `RepositorioEmpty` refusal case at the accessor boundary and
12878        // the caixa-helm `Chart.yaml` `home:` fold would silently
12879        // render a `home: null` / omitted field on a struct-literal
12880        // `Caixa { repositorio: Some(""), .. }`), or a
12881        // `None → Some(<default>)` collapse (which would silently reify
12882        // the per-renderer fallback at the accessor boundary and every
12883        // downstream consumer keying off the `Option::is_none()`
12884        // discriminator would lose the "author omitted the slot"
12885        // signal).
12886        for repositorio in [
12887            None,
12888            Some(""),
12889            Some("github:pleme-io/hello-rio"),
12890            Some("https://github.com/pleme-io/checkout"),
12891            Some("ssh://git@github.com/pleme-io/checkout.git"),
12892            Some("git://github.com/pleme-io/checkout.git"),
12893            Some("git@github.com:pleme-io/checkout.git"),
12894            Some("file:///opt/mirrors/pleme-io/checkout"),
12895            Some("pleme-io/checkout"),
12896            Some("-upload-pack=evil"),
12897            Some("github:pleme-io/checkout?ref=main"),
12898            Some("github:pleme-io/checkout#main"),
12899            Some("github:pleme-io/{tpl}"),
12900        ] {
12901            let c = caixa_with_repositorio(repositorio);
12902            assert_eq!(
12903                c.repositorio(),
12904                repositorio,
12905                "Caixa::repositorio must return :repositorio verbatim \
12906                 (got {:?}, expected {repositorio:?})",
12907                c.repositorio(),
12908            );
12909            assert_eq!(
12910                c.repositorio(),
12911                c.repositorio.as_deref(),
12912                "Caixa::repositorio must byte-equal the raw \
12913                 `self.repositorio.as_deref()` field access across every \
12914                 value in the Option<&str> accept-set",
12915            );
12916        }
12917    }
12918
12919    #[test]
12920    fn validate_repositorio_empty_arm_routes_through_accessor() {
12921        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
12922        // gate must key off [`Caixa::repositorio`], not the raw
12923        // `self.repositorio.as_deref()` field access. Structurally: a
12924        // `Caixa { repositorio: Some(""), .. }` must surface the
12925        // `RepositorioEmpty` refusal exactly, and a
12926        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
12927        // (the canonical `github:` shorthand form) must pass validate.
12928        // The pair jointly pins the accessor + validate-gate
12929        // composition: any future silent detour that had the accessor
12930        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
12931        // collapse) would silently absorb the `RepositorioEmpty` refusal
12932        // at the accessor boundary and the validate gate would accept a
12933        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
12934        // composition pin catches that at caixa-core build time.
12935        //
12936        // Peer of the [`Caixa::licenca`] (6d5bc28)
12937        // `validate_licenca_empty_arm_routes_through_accessor`
12938        // composition pin on the sibling outer top-level [`Caixa`]
12939        // `Option<&str>` universal-axis surface — same "the validate /
12940        // shape-gate predicate must route through the substrate-
12941        // primitive typed dispatch" discipline extended onto the second
12942        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
12943        // composition surface.
12944        let c = caixa_with_repositorio(Some(""));
12945        assert!(
12946            matches!(
12947                c.validate_repositorio(),
12948                Err(ManifestError::RepositorioEmpty),
12949            ),
12950            "validate_repositorio must reject repositorio == Some(\"\") \
12951             with RepositorioEmpty — the accessor and the validate gate \
12952             must route through the same substrate-primitive typed \
12953             dispatch on the :repositorio empty arm",
12954        );
12955        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
12956        assert!(
12957            c.validate_repositorio().is_ok(),
12958            "validate_repositorio must accept repositorio == \
12959             Some(\"github:pleme-io/hello-rio\") (the canonical \
12960             `github:` shorthand git-repo-URL shape)",
12961        );
12962    }
12963
12964    #[test]
12965    fn repositorio_projects_option_str_by_borrow() {
12966        // The by-borrow pin: [`Caixa::repositorio`] returns
12967        // `Option<&str>` by borrow — the `&str` borrows the underlying
12968        // `String` storage of the `Option<String>` slot and the
12969        // accessor must not allocate a fresh `String` on every call.
12970        // Peer of the per-`:placement`
12971        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
12972        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
12973        // `Option<&str>`-return axes, extended onto the second outer
12974        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
12975        // the accessor's returned `&str` must borrow from `&self` (the
12976        // returned reference's lifetime is tied to `&self`), and
12977        // calling the accessor twice on the same [`Caixa`] must yield
12978        // the same `Option<&str>` verbatim (idempotent, no side effects
12979        // on `&self`).
12980        //
12981        // Pins against a future silent detour that returned an owned
12982        // `Option<String>` (which would type-check but silently
12983        // allocate on every call, breaking the zero-cost projection
12984        // every peer sibling accessor carries), or a one-arm-only
12985        // accessor that returned a saturating value on some sentinel
12986        // input (breaking the pass-through invariant the sibling
12987        // required-scalar accessors carry).
12988        for repositorio in [
12989            None,
12990            Some(""),
12991            Some("github:pleme-io/hello-rio"),
12992            Some("https://github.com/pleme-io/checkout"),
12993        ] {
12994            let c = caixa_with_repositorio(repositorio);
12995            let first = c.repositorio();
12996            let second = c.repositorio();
12997            assert_eq!(
12998                first, second,
12999                "Caixa::repositorio must be idempotent — two successive \
13000                 calls on the same &self must return the same \
13001                 Option<&str>",
13002            );
13003            assert_eq!(
13004                first, repositorio,
13005                "Caixa::repositorio must return :repositorio verbatim by \
13006                 borrow — got {first:?}, expected {repositorio:?}",
13007            );
13008        }
13009    }
13010
13011    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
13012
13013    #[test]
13014    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
13015        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
13016        // return the author-declared `:repositorio` byte-string verbatim
13017        // on the `Some` arm — no scheme rewrite, no trailing-slash
13018        // canonicalization, no `github:` → `https://github.com/`
13019        // desugaring. The resolved-URL composer is the projection of
13020        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
13021        // the `String`-return arity every substrate-side field-fill
13022        // consumer keys off; on the `Some` arm the projection is
13023        // `str::to_owned` verbatim, so every accept-set value the
13024        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
13025        // across_permutations` pin covers (`https://…`, `github:…`,
13026        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
13027        // guard sentinel `pleme-io/…`) must survive the accessor
13028        // byte-equal. Pins against a future silent detour that rewrote
13029        // the `github:` shorthand to the `https://github.com/` full URL
13030        // at the accessor boundary (which would silently split the
13031        // resolved-URL surface from the raw [`Caixa::repositorio`]
13032        // accessor's documented pass-through invariant), or a trailing-
13033        // slash normalization (which would silently break the
13034        // FluxCD `GitRepository` `spec.url` byte-exact match every
13035        // downstream consumer keys the source-controller reconcile off).
13036        for repositorio in [
13037            "github:pleme-io/hello-rio",
13038            "https://github.com/pleme-io/checkout",
13039            "ssh://git@github.com/pleme-io/checkout.git",
13040            "git://github.com/pleme-io/checkout.git",
13041            "git@github.com:pleme-io/checkout.git",
13042            "file:///opt/mirrors/pleme-io/checkout",
13043        ] {
13044            let c = caixa_with_repositorio(Some(repositorio));
13045            assert_eq!(
13046                c.canonical_git_url(),
13047                repositorio,
13048                "Caixa::canonical_git_url on the Some arm must return \
13049                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
13050                c.canonical_git_url(),
13051            );
13052        }
13053    }
13054
13055    #[test]
13056    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
13057        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
13058        // `None` arm must emit the substrate's canonical pleme-org github
13059        // URL derived from `caixa.nome()` — `https://github.com/<org>/
13060        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
13061        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
13062        // is the exact byte-image of the prior inline
13063        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
13064        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
13065        // re-derived open-coded. Pins against a future silent detour
13066        // that migrated the `<org>` segment to a different constant (a
13067        // fork rebranding that split off a new
13068        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
13069        // to migrate onto), a scheme change (`https://` → `git://` or
13070        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
13071        // override (which would break the substrate-wide single-source-
13072        // of-truth guarantee this method encodes).
13073        let c = caixa_with_repositorio(None);
13074        let expected = format!(
13075            "https://github.com/{org}/{nome}",
13076            org = crate::DEFAULT_PLEME_GIT_ORG,
13077            nome = c.nome(),
13078        );
13079        assert_eq!(
13080            c.canonical_git_url(),
13081            expected,
13082            "Caixa::canonical_git_url on the None arm must fold through \
13083             the substrate's canonical pleme-org github URL fallback \
13084             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
13085             {:?}, expected {expected:?}",
13086            c.canonical_git_url(),
13087        );
13088    }
13089
13090    #[test]
13091    fn canonical_git_url_byte_matches_manual_composition() {
13092        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
13093        // byte-identically to the manual open-coded
13094        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
13095        //  format!("https://github.com/{org}/{nome}", ...))` composition
13096        // every prior substrate-side caller re-derived. Guards the
13097        // paired-site convergence just applied at caixa-flux's
13098        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
13099        // now routes through this accessor): a future implementation of
13100        // this method that reordered the format arguments, swapped the
13101        // `<org>` constant for a different one, or interposed a
13102        // canonicalization pass on the `Some` arm surfaces here as a
13103        // caixa-core build-time test failure rather than as a downstream
13104        // FluxCD `GitRepository` reconcile mismatch far from this
13105        // method's source.
13106        for repositorio in [
13107            None,
13108            Some("github:pleme-io/hello-rio"),
13109            Some("https://github.com/pleme-io/checkout"),
13110            Some("ssh://git@github.com/pleme-io/checkout.git"),
13111        ] {
13112            let c = caixa_with_repositorio(repositorio);
13113            let manual = c.repositorio().map_or_else(
13114                || {
13115                    format!(
13116                        "https://github.com/{org}/{nome}",
13117                        org = crate::DEFAULT_PLEME_GIT_ORG,
13118                        nome = c.nome(),
13119                    )
13120                },
13121                str::to_owned,
13122            );
13123            assert_eq!(
13124                c.canonical_git_url(),
13125                manual,
13126                "Caixa::canonical_git_url must byte-equal the manual \
13127                 open-coded `repositorio().map(str::to_owned)\
13128                 .unwrap_or_else(|| format!(...))` composition across \
13129                 every representative :repositorio input — got {:?}, \
13130                 expected {manual:?}",
13131                c.canonical_git_url(),
13132            );
13133        }
13134    }
13135
13136    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
13137
13138    #[test]
13139    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
13140        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
13141        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
13142        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
13143        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
13144        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
13145        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
13146        // the `0.0.0` boundary case. Every accept-set value the peer
13147        // validate gate lets through must survive the resolved-tag
13148        // projection byte-equal.
13149        for versao in [
13150            "0.1.0",
13151            "0.0.0",
13152            "1.0.0",
13153            "1.2.3-rc.1",
13154            "1.2.3+build.42",
13155            "1.2.3-rc.1+build.42",
13156        ] {
13157            let c = caixa_with_versao(versao);
13158            let expected = format!(
13159                "{prefix}{versao}",
13160                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13161            );
13162            assert_eq!(
13163                c.publish_tag(),
13164                expected,
13165                "Caixa::publish_tag must compose \
13166                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
13167                 :versao ({versao:?}) verbatim — got {got:?}, \
13168                 expected {expected:?}",
13169                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13170                got = c.publish_tag(),
13171            );
13172        }
13173    }
13174
13175    #[test]
13176    fn publish_tag_starts_with_default_publish_tag_prefix() {
13177        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
13178        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
13179        // byte-string on every input, guarding a hypothetical future
13180        // implementation that migrated the prefix segment to an inline
13181        // literal (`"v"`) that would silently drift from any rebrand of
13182        // the lifted constant. Peer to the sibling caixa-flux
13183        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
13184        // test which pins the same prefix invariant at the reader-side
13185        // `GitRefSpec::Tag` emit site.
13186        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
13187            let c = caixa_with_versao(versao);
13188            let tag = c.publish_tag();
13189            assert!(
13190                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
13191                "Caixa::publish_tag emission {tag:?} must start with \
13192                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
13193                 ({prefix:?})",
13194                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13195            );
13196        }
13197    }
13198
13199    #[test]
13200    fn publish_tag_byte_matches_manual_composition() {
13201        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
13202        // identically to the manual open-coded
13203        // `format!("{prefix}{versao}", prefix =
13204        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
13205        //  caixa.versao())` composition every prior substrate-side
13206        // caller re-derived. Guards the paired-site convergence just
13207        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
13208        // `git_ref` composer (which now routes through this accessor):
13209        // a future implementation of this method that reordered the
13210        // format arguments, swapped the `<prefix>` constant for a
13211        // different one, or interposed a canonicalization pass on the
13212        // `:versao` axis surfaces here as a caixa-core build-time test
13213        // failure rather than as a downstream FluxCD `GitRepository`
13214        // reconcile mismatch far from this method's source.
13215        for versao in [
13216            "0.1.0",
13217            "0.0.0",
13218            "1.2.3-rc.1",
13219            "1.2.3+build.42",
13220            "1.2.3-rc.1+build.42",
13221        ] {
13222            let c = caixa_with_versao(versao);
13223            let manual = format!(
13224                "{prefix}{versao}",
13225                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13226                versao = c.versao(),
13227            );
13228            assert_eq!(
13229                c.publish_tag(),
13230                manual,
13231                "Caixa::publish_tag must byte-equal the manual \
13232                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
13233                 composition across every representative :versao input \
13234                 — got {got:?}, expected {manual:?}",
13235                got = c.publish_tag(),
13236            );
13237        }
13238    }
13239
13240    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
13241
13242    #[test]
13243    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
13244        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
13245        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
13246        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
13247        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
13248        // set sweep documents — single-word, hyphen-joined, version-
13249        // suffixed, single-char, two-char, digit-start, retry-suffixed.
13250        // Every accept-set value the peer validate gate lets through must
13251        // survive the resolved-chart-name projection byte-equal.
13252        for nome in [
13253            "checkout",
13254            "cart-v2",
13255            "a",
13256            "db",
13257            "3rd-party-shim",
13258            "payment-retry",
13259            "0",
13260        ] {
13261            let c = caixa_with_nome(nome);
13262            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
13263            assert_eq!(
13264                c.lareira_chart_name(),
13265                expected,
13266                "Caixa::lareira_chart_name must compose \
13267                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
13268                 :nome ({nome:?}) verbatim — got {got:?}, \
13269                 expected {expected:?}",
13270                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
13271                got = c.lareira_chart_name(),
13272            );
13273        }
13274    }
13275
13276    #[test]
13277    fn lareira_chart_name_starts_with_lifted_prefix() {
13278        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
13279        // must begin with the canonical
13280        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
13281        // input, guarding a hypothetical future implementation that
13282        // migrated the prefix segment to an inline literal (`"lareira-"`)
13283        // that would silently drift from any rebrand of the lifted
13284        // constant. Peer to the sibling
13285        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
13286        // the co-resident resolved-publish-tag composer's prefix axis.
13287        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13288            let c = caixa_with_nome(nome);
13289            let chart = c.lareira_chart_name();
13290            assert!(
13291                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
13292                "Caixa::lareira_chart_name emission {chart:?} must start \
13293                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
13294                 ({prefix:?})",
13295                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
13296            );
13297        }
13298    }
13299
13300    #[test]
13301    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
13302        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
13303        // byte-identically to the manual open-coded
13304        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
13305        // composition every prior substrate-side caller re-derived.
13306        // Guards the paired-site convergence just applied at caixa-helm's
13307        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
13308        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
13309        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
13310        // composer (all of which now route through this accessor): a
13311        // future implementation of this method that reordered the
13312        // composition arguments, swapped the `<prefix>` constant for a
13313        // different one, or interposed a canonicalization pass on the
13314        // `:nome` axis surfaces here as a caixa-core build-time test
13315        // failure rather than as a downstream Helm chart-render / FluxCD
13316        // reconcile / tatara Process-CR mismatch far from this method's
13317        // source.
13318        for nome in [
13319            "checkout",
13320            "cart-v2",
13321            "a",
13322            "db",
13323            "3rd-party-shim",
13324            "payment-retry",
13325        ] {
13326            let c = caixa_with_nome(nome);
13327            let manual = crate::lareira_chart_name(c.nome());
13328            assert_eq!(
13329                c.lareira_chart_name(),
13330                manual,
13331                "Caixa::lareira_chart_name must byte-equal the manual \
13332                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
13333                 composition across every representative :nome input — \
13334                 got {got:?}, expected {manual:?}",
13335                got = c.lareira_chart_name(),
13336            );
13337        }
13338    }
13339
13340    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
13341
13342    #[test]
13343    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
13344        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
13345        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
13346        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
13347        // across the full paired `(registry, :nome)` accept-set — every
13348        // representative registry the substrate-side emitters carry
13349        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
13350        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
13351        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
13352        // inline_format` render-side pin exercises; `registry.example.
13353        // com`, an off-org shape; `localhost:5000`, the local-dev shape
13354        // every `feira chart` iteration path lands under) × every DNS-
13355        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
13356        // forms` positive-set sweep documents (single-word, hyphen-
13357        // joined, single-char, two-char, digit-start, retry-suffixed).
13358        // Every accept-set pair the peer validate gates let through must
13359        // survive the resolved-OCI-ref projection byte-equal.
13360        for registry in [
13361            "ghcr.io/pleme-io/charts",
13362            "ghcr.io/pleme-io",
13363            "registry.example.com",
13364            "localhost:5000",
13365        ] {
13366            for nome in [
13367                "checkout",
13368                "cart-v2",
13369                "a",
13370                "db",
13371                "3rd-party-shim",
13372                "payment-retry",
13373                "0",
13374            ] {
13375                let c = caixa_with_nome(nome);
13376                let expected = format!(
13377                    "{scheme}{registry}/{chart}",
13378                    scheme = crate::OCI_SCHEME_PREFIX,
13379                    chart = crate::lareira_chart_name(nome),
13380                );
13381                assert_eq!(
13382                    c.oci_chart_ref(registry),
13383                    expected,
13384                    "Caixa::oci_chart_ref must compose \
13385                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
13386                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
13387                     expected {expected:?}",
13388                    scheme = crate::OCI_SCHEME_PREFIX,
13389                    got = c.oci_chart_ref(registry),
13390                );
13391            }
13392        }
13393    }
13394
13395    #[test]
13396    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
13397        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
13398        // emission must begin with the canonical
13399        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
13400        // a hypothetical future implementation that migrated the scheme
13401        // segment to an inline literal (`"oci://"`) that would silently
13402        // drift from any rebrand of the lifted constant. Peer to the
13403        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
13404        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
13405        // co-resident resolved-publish-tag / resolved-chart-name
13406        // composers' prefix axes.
13407        for registry in [
13408            "ghcr.io/pleme-io/charts",
13409            "ghcr.io/pleme-io",
13410            "localhost:5000",
13411        ] {
13412            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13413                let c = caixa_with_nome(nome);
13414                let ref_ = c.oci_chart_ref(registry);
13415                assert!(
13416                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
13417                    "Caixa::oci_chart_ref emission {ref_:?} must start \
13418                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
13419                     — registry ({registry:?}), :nome ({nome:?})",
13420                    scheme = crate::OCI_SCHEME_PREFIX,
13421                );
13422            }
13423        }
13424    }
13425
13426    #[test]
13427    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
13428        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
13429        // identically to the manual open-coded
13430        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
13431        // composition every prior substrate-side caller re-derived.
13432        // Guards the paired-site convergence just applied at caixa-
13433        // tatara's [`derive_chart_ref`] helper (which now routes through
13434        // this accessor): a future implementation of this method that
13435        // reordered the composition arguments, swapped the `<scheme>`
13436        // constant for a different one, migrated the `<chart>` segment
13437        // off the paired [`crate::lareira_chart_name`] composer, or
13438        // interposed a canonicalization pass on either input axis
13439        // surfaces here as a caixa-core build-time test failure rather
13440        // than as a downstream `helm install` / FluxCD OCI-source
13441        // reconcile / tatara `Process`-CR mismatch far from this
13442        // method's source. Sibling to the peer
13443        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
13444        // / [`publish_tag_byte_matches_manual_composition`] /
13445        // [`canonical_git_url_byte_matches_manual_composition`] byte-
13446        // parity pins that carry the same discipline on the co-resident
13447        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
13448        // composers.
13449        for registry in [
13450            "ghcr.io/pleme-io/charts",
13451            "ghcr.io/pleme-io",
13452            "registry.example.com",
13453            "localhost:5000",
13454        ] {
13455            for nome in [
13456                "checkout",
13457                "cart-v2",
13458                "a",
13459                "db",
13460                "3rd-party-shim",
13461                "payment-retry",
13462            ] {
13463                let c = caixa_with_nome(nome);
13464                let manual = crate::oci_chart_ref(registry, c.nome());
13465                assert_eq!(
13466                    c.oci_chart_ref(registry),
13467                    manual,
13468                    "Caixa::oci_chart_ref must byte-equal the manual \
13469                     open-coded `caixa_core::oci_chart_ref(registry, \
13470                     caixa.nome())` composition across every representative \
13471                     (registry, :nome) pair — registry ({registry:?}), \
13472                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
13473                    got = c.oci_chart_ref(registry),
13474                );
13475            }
13476        }
13477    }
13478
13479    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
13480
13481    #[test]
13482    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
13483        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
13484        // pin: [`Caixa::descricao`] must return the `:descricao` typed
13485        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13486        // raw `self.descricao.as_deref()` access across every
13487        // representative value in the accept-set — `None` (the "omit
13488        // the slot to defer to the per-renderer `caixa.nome`-derived
13489        // fallback" arm every existing fixture without a `:descricao`
13490        // line carries), `Some("")` (a past-the-guard sentinel that
13491        // pins the accessor doesn't perform a silent `Some("") → None`
13492        // collapse on the empty arm — validate rejects `Some("")`
13493        // through `DescricaoEmpty` but the accessor must ship the raw
13494        // slot verbatim so a validate-time gate regression surfaces at
13495        // the caixa-helm / caixa-feira emit boundary rather than being
13496        // silently absorbed into the per-renderer `caixa.nome`-derived
13497        // fallback), `Some("Checkout flow.")` (the canonical one-line
13498        // prose descriptor the peer
13499        // `validate_descricao_accepts_canonical_value` positive sweep
13500        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
13501        // Servico.")` (the multi-byte Unicode continuation-byte shape
13502        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
13503        // multi-glyph Unicode shape the peer
13504        // `is_chart_description_shape` predicate accepts), and five
13505        // past-the-guard sentinels for the `DescricaoInvalid` refusal
13506        // cases (`Some(" Checkout flow.")` leading-whitespace,
13507        // `Some("Checkout flow. ")` trailing-whitespace,
13508        // `Some("Checkout\nflow.")` embedded-LF,
13509        // `Some("Checkout\tflow.")` embedded-TAB, and
13510        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
13511        // the accessor doesn't silently absorb the refusal cases into
13512        // a fallback).
13513        //
13514        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
13515        // accessor pin on the substrate primitive — sibling of the peer
13516        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
13517        // (cc7332d) pins that opened the "outer [`Caixa`]
13518        // `Option<&str>` scalar" projection pin pattern this pin folds
13519        // on. Sibling in shape to the peer per-`:placement`
13520        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13521        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13522        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13523        // axes, extended onto the outer top-level [`Caixa`] universal-
13524        // axis surface. Pins against a future silent detour that
13525        // returned an owned `Option<String>` (which would type-check
13526        // but silently allocate on every accessor call, breaking the
13527        // zero-cost projection every peer sibling accessor carries), a
13528        // `Some("") → None` collapse (which would silently absorb the
13529        // `DescricaoEmpty` refusal case at the accessor boundary and
13530        // the caixa-helm `Chart.yaml` `description:` fold would
13531        // silently render a `caixa.nome`-derived fallback on a
13532        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
13533        // `None → Some(<default>)` collapse (which would silently
13534        // reify the per-renderer `caixa.nome`-derived fallback at the
13535        // accessor boundary and every downstream consumer keying off
13536        // the `Option::is_none()` discriminator would lose the "author
13537        // omitted the slot" signal).
13538        for descricao in [
13539            None,
13540            Some(""),
13541            Some("Checkout flow."),
13542            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13543            Some("→ — · ✓"),
13544            Some(" Checkout flow."),
13545            Some("Checkout flow. "),
13546            Some("Checkout\nflow."),
13547            Some("Checkout\tflow."),
13548            Some("Checkout\x00flow."),
13549        ] {
13550            let c = caixa_with_descricao(descricao);
13551            assert_eq!(
13552                c.descricao(),
13553                descricao,
13554                "Caixa::descricao must return :descricao verbatim (got \
13555                 {:?}, expected {descricao:?})",
13556                c.descricao(),
13557            );
13558            assert_eq!(
13559                c.descricao(),
13560                c.descricao.as_deref(),
13561                "Caixa::descricao must byte-equal the raw \
13562                 `self.descricao.as_deref()` field access across every \
13563                 value in the Option<&str> accept-set",
13564            );
13565        }
13566    }
13567
13568    #[test]
13569    fn validate_descricao_empty_arm_routes_through_accessor() {
13570        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
13571        // gate must key off [`Caixa::descricao`], not the raw
13572        // `self.descricao.as_deref()` field access. Structurally: a
13573        // `Caixa { descricao: Some(""), .. }` must surface the
13574        // `DescricaoEmpty` refusal exactly, and a
13575        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
13576        // canonical one-line-prose form) must pass validate. The pair
13577        // jointly pins the accessor + validate-gate composition: any
13578        // future silent detour that had the accessor return `None` on
13579        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
13580        // silently absorb the `DescricaoEmpty` refusal at the accessor
13581        // boundary and the validate gate would accept a struct-literal
13582        // `Caixa { descricao: Some(""), .. }` — the composition pin
13583        // catches that at caixa-core build time.
13584        //
13585        // Peer of the [`Caixa::licenca`] (6d5bc28)
13586        // `validate_licenca_empty_arm_routes_through_accessor` and
13587        // [`Caixa::repositorio`] (cc7332d)
13588        // `validate_repositorio_empty_arm_routes_through_accessor`
13589        // composition pins on the sibling outer top-level [`Caixa`]
13590        // `Option<&str>` universal-axis surface — same "the validate /
13591        // shape-gate predicate must route through the substrate-
13592        // primitive typed dispatch" discipline extended onto the third
13593        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
13594        // composition surface.
13595        let c = caixa_with_descricao(Some(""));
13596        assert!(
13597            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
13598            "validate_descricao must reject descricao == Some(\"\") \
13599             with DescricaoEmpty — the accessor and the validate gate \
13600             must route through the same substrate-primitive typed \
13601             dispatch on the :descricao empty arm",
13602        );
13603        let c = caixa_with_descricao(Some("Checkout flow."));
13604        assert!(
13605            c.validate_descricao().is_ok(),
13606            "validate_descricao must accept descricao == \
13607             Some(\"Checkout flow.\") (the canonical one-line-prose \
13608             chart-description shape)",
13609        );
13610    }
13611
13612    #[test]
13613    fn descricao_projects_option_str_by_borrow() {
13614        // The by-borrow pin: [`Caixa::descricao`] returns
13615        // `Option<&str>` by borrow — the `&str` borrows the underlying
13616        // `String` storage of the `Option<String>` slot and the
13617        // accessor must not allocate a fresh `String` on every call.
13618        // Peer of the [`Caixa::licenca`] (6d5bc28) and
13619        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
13620        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
13621        // the per-`:placement`
13622        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13623        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13624        // return axis, extended onto the third outer top-level
13625        // [`Caixa`] universal-axis `Option<&str>` shape — the
13626        // accessor's returned `&str` must borrow from `&self` (the
13627        // returned reference's lifetime is tied to `&self`), and
13628        // calling the accessor twice on the same [`Caixa`] must yield
13629        // the same `Option<&str>` verbatim (idempotent, no side
13630        // effects on `&self`).
13631        //
13632        // Pins against a future silent detour that returned an owned
13633        // `Option<String>` (which would type-check but silently
13634        // allocate on every call, breaking the zero-cost projection
13635        // every peer sibling accessor carries), or a one-arm-only
13636        // accessor that returned a saturating value on some sentinel
13637        // input (breaking the pass-through invariant the sibling
13638        // required-scalar accessors carry).
13639        for descricao in [
13640            None,
13641            Some(""),
13642            Some("Checkout flow."),
13643            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13644        ] {
13645            let c = caixa_with_descricao(descricao);
13646            let first = c.descricao();
13647            let second = c.descricao();
13648            assert_eq!(
13649                first, second,
13650                "Caixa::descricao must be idempotent — two successive \
13651                 calls on the same &self must return the same \
13652                 Option<&str>",
13653            );
13654            assert_eq!(
13655                first, descricao,
13656                "Caixa::descricao must return :descricao verbatim by \
13657                 borrow — got {first:?}, expected {descricao:?}",
13658            );
13659        }
13660    }
13661
13662    // ── validate_edicao — universal-axis language-edition shape ──
13663
13664    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
13665        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13666        c.edicao = edicao.map(String::from);
13667        c
13668    }
13669
13670    #[test]
13671    fn validate_edicao_accepts_none() {
13672        // The omit-the-slot identity: `:edicao` is optional. The
13673        // gate is a no-op when the author didn't declare a value —
13674        // every caixa without an `:edicao` line trivially passes,
13675        // and the substrate-side build pipeline falls back to the
13676        // documented default edition. Mirrors the peer
13677        // `validate_licenca_accepts_none` posture on the sibling
13678        // `Option<String>` Caixa slot.
13679        let c = caixa_with_edicao(None);
13680        c.validate_edicao().unwrap();
13681    }
13682
13683    #[test]
13684    fn validate_edicao_accepts_canonical_value() {
13685        // Positive control: the canonical `"2026"` edition every
13686        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
13687        // `caixa-mesh`) carries by construction passes the gate.
13688        // Future-introduced sibling editions (`"2027"`, `"2030"`,
13689        // `"2049"`) that match the same 4-digit ASCII decimal year
13690        // shape must also trivially pass — the structural shape
13691        // predicate accepts every well-formed year regardless of
13692        // whether the substrate yet understands the specific value
13693        // (a future known-edition allowlist tightens that).
13694        for ed in ["2026", "2027", "2030", "2049"] {
13695            let c = caixa_with_edicao(Some(ed));
13696            c.validate_edicao()
13697                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
13698        }
13699    }
13700
13701    #[test]
13702    fn validate_edicao_rejects_empty_some() {
13703        // Canonical paste-from-blank-doc footgun. Without this gate
13704        // the empty `Some("")` silently lands as `(:edicao "")` in
13705        // the rendered caixa.lisp and a future renderer-side
13706        // consumer's `Option::unwrap_or_else` (which only fires on
13707        // `None`) skips its fallback. Mirrors the peer
13708        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
13709        // `Option<String>` Caixa slot.
13710        let c = caixa_with_edicao(Some(""));
13711        let err = c.validate_edicao().unwrap_err();
13712        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13713    }
13714
13715    #[test]
13716    fn validate_edicao_rejects_free_form_non_year() {
13717        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
13718        // `"nightly"` shapes carry no operational meaning on the
13719        // substrate's build-time edition selector. Until this gate
13720        // landed the bare empty-arm check let every such value
13721        // through and broke far from the source caixa.lisp. Peer
13722        // with the shape-predicate cascade
13723        // `validate_repositorio_rejects_missing_colon_separator`
13724        // establishes past its own empty arm.
13725        for ed in ["x", "latest", "nightly", "stable"] {
13726            let c = caixa_with_edicao(Some(ed));
13727            let err = c.validate_edicao().unwrap_err();
13728            assert!(
13729                matches!(err, ManifestError::EdicaoInvalid { .. }),
13730                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13731            );
13732        }
13733    }
13734
13735    #[test]
13736    fn validate_edicao_rejects_trailing_whitespace() {
13737        // Paste-from-doc whitespace footgun. A trailing space in
13738        // the `:edicao` value would silently break the substrate's
13739        // build-time edition match-table lookup at the rendered
13740        // artifact's edition-selector consumer. The shape predicate
13741        // refuses every whitespace byte by construction (any byte
13742        // outside `0-9` fails `is_ascii_digit`). Peer with
13743        // `validate_repositorio_rejects_whitespace`.
13744        let c = caixa_with_edicao(Some("2026 "));
13745        let err = c.validate_edicao().unwrap_err();
13746        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
13747            panic!("expected EdicaoInvalid, got {err:?}");
13748        };
13749        assert_eq!(edicao, "2026 ");
13750    }
13751
13752    #[test]
13753    fn validate_edicao_rejects_leading_whitespace() {
13754        // Symmetric paste-from-doc whitespace footgun on the leading
13755        // boundary — the gate refuses every shape with a non-digit
13756        // byte by construction.
13757        let c = caixa_with_edicao(Some(" 2026"));
13758        let err = c.validate_edicao().unwrap_err();
13759        assert!(
13760            matches!(err, ManifestError::EdicaoInvalid { .. }),
13761            "got {err:?}",
13762        );
13763    }
13764
13765    #[test]
13766    fn validate_edicao_rejects_control_char() {
13767        // Paste-from-multiline-doc CRLF footgun — control characters
13768        // at the value boundary break the substrate's build-time
13769        // edition-selector parser. Peer with
13770        // `validate_repositorio_rejects_control_char`.
13771        let c = caixa_with_edicao(Some("2026\n"));
13772        let err = c.validate_edicao().unwrap_err();
13773        assert!(
13774            matches!(err, ManifestError::EdicaoInvalid { .. }),
13775            "got {err:?}",
13776        );
13777    }
13778
13779    #[test]
13780    fn validate_edicao_rejects_non_ascii_lookalike() {
13781        // Fullwidth-keyboard look-alike footgun — `"2026"` is
13782        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
13783        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
13784        // edition selector wants an ASCII year, and the gate
13785        // refuses every non-ASCII shape by construction (length in
13786        // bytes is 12 ≠ 4, *and* every byte falls outside
13787        // `is_ascii_digit`'s `0-9` range).
13788        let c = caixa_with_edicao(Some("2026"));
13789        let err = c.validate_edicao().unwrap_err();
13790        assert!(
13791            matches!(err, ManifestError::EdicaoInvalid { .. }),
13792            "got {err:?}",
13793        );
13794    }
13795
13796    #[test]
13797    fn validate_edicao_rejects_version_tag_prefix() {
13798        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
13799        // / `"r2026"` are familiar shapes from git-tag / Rust
13800        // edition / release-tag conventions that don't apply to
13801        // the year-shaped edition axis. The shape predicate refuses
13802        // every leading non-digit prefix.
13803        for ed in ["v2026", "e2026", "r2026"] {
13804            let c = caixa_with_edicao(Some(ed));
13805            let err = c.validate_edicao().unwrap_err();
13806            assert!(
13807                matches!(err, ManifestError::EdicaoInvalid { .. }),
13808                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13809            );
13810        }
13811    }
13812
13813    #[test]
13814    fn validate_edicao_rejects_decimal_shape() {
13815        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
13816        // `"2026.0"` are familiar shapes from semver / float
13817        // conventions that don't apply to the year-shaped edition
13818        // axis. The shape predicate refuses every non-digit byte
13819        // (`.` falls outside `is_ascii_digit`).
13820        for ed in ["2026.1", "2026.0", "2026.0.1"] {
13821            let c = caixa_with_edicao(Some(ed));
13822            let err = c.validate_edicao().unwrap_err();
13823            assert!(
13824                matches!(err, ManifestError::EdicaoInvalid { .. }),
13825                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13826            );
13827        }
13828    }
13829
13830    #[test]
13831    fn validate_edicao_rejects_wrong_length_numeric() {
13832        // Wrong-length numeric footgun — `"26"` (truncated) /
13833        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
13834        // (zero-padded too wide) all parse as integers but don't
13835        // name a 4-digit year. The shape predicate refuses every
13836        // value whose length isn't exactly 4 bytes.
13837        for ed in ["26", "202", "20260", "00026", "9"] {
13838            let c = caixa_with_edicao(Some(ed));
13839            let err = c.validate_edicao().unwrap_err();
13840            assert!(
13841                matches!(err, ManifestError::EdicaoInvalid { .. }),
13842                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13843            );
13844        }
13845    }
13846
13847    #[test]
13848    fn validate_edicao_empty_takes_precedence_over_shape() {
13849        // Empty-first cascade pin: the empty `Some("")` surfaces
13850        // the narrower `EdicaoEmpty` not the shape-predicate-
13851        // wrapped `EdicaoInvalid`, mirroring the peer
13852        // `validate_repositorio_empty_takes_precedence_over_shape`
13853        // (`RepositorioEmpty` → `RepositorioInvalid`),
13854        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
13855        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
13856        // cascades. The shape predicate also refuses the empty
13857        // input (defensively — `s.len() != 4`), but the
13858        // manifest-layer empty arm runs first to surface the
13859        // narrower diagnostic verbatim.
13860        let c = caixa_with_edicao(Some(""));
13861        let err = c.validate_edicao().unwrap_err();
13862        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13863    }
13864
13865    #[test]
13866    fn validate_edicao_template_passes() {
13867        // Round-trip pin: the bare `Caixa::template` shape (which
13868        // carries `:edicao "2026"` verbatim) passes the gate by
13869        // construction. A future template-shape change that
13870        // introduced `(:edicao "")` or a non-year value would
13871        // surface here as a regression. Mirrors the peer
13872        // `validate_licenca_template_passes` pin.
13873        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13874        c.validate_edicao().unwrap();
13875    }
13876
13877    #[test]
13878    fn validate_edicao_diagnostic_names_offending_slot() {
13879        // Diagnostic-shape pin (peer with
13880        // `validate_licenca_diagnostic_names_offending_slot`): the
13881        // error's Display surfaces the `:edicao` slot name verbatim,
13882        // so a `feira lint` run can render the diagnostic without
13883        // re-parsing and the author can grep their caixa.lisp for
13884        // the offending `:edicao` line.
13885        let c = caixa_with_edicao(Some(""));
13886        let rendered = c.validate_edicao().unwrap_err().to_string();
13887        assert!(
13888            rendered.contains(":edicao"),
13889            "diagnostic must name the offending slot: {rendered}",
13890        );
13891    }
13892
13893    #[test]
13894    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
13895        // Diagnostic-shape pin on the shape-predicate arm (peer
13896        // with `validate_repositorio_diagnostic_carries_offending_value`):
13897        // the error's Display surfaces the offending value + slot
13898        // name verbatim, so a `feira lint` run can render the
13899        // diagnostic without re-parsing and the author can grep
13900        // their caixa.lisp for the offending `:edicao` value.
13901        let c = caixa_with_edicao(Some("v2026"));
13902        let rendered = c.validate_edicao().unwrap_err().to_string();
13903        assert!(
13904            rendered.contains(":edicao"),
13905            "diagnostic must name the offending slot: {rendered}",
13906        );
13907        assert!(
13908            rendered.contains("v2026"),
13909            "diagnostic must quote the offending value: {rendered}",
13910        );
13911    }
13912
13913    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
13914
13915    #[test]
13916    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
13917        // The canonical per-`Caixa` `:edicao` language-edition scalar
13918        // pin: [`Caixa::edicao`] must return the `:edicao` typed
13919        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13920        // raw `self.edicao.as_deref()` access across every representative
13921        // value in the accept-set — `None` (the "omit the slot to defer
13922        // to the substrate's default edition" arm every existing
13923        // [`caixa-resolver`] fixture without an `:edicao` line carries),
13924        // `Some("")` (a past-the-guard sentinel that pins the accessor
13925        // doesn't perform a silent `Some("") → None` collapse on the
13926        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
13927        // but the accessor must ship the raw slot verbatim so a
13928        // validate-time gate regression surfaces at any future edition-
13929        // aware consumer's boundary rather than being silently absorbed
13930        // into the substrate's default edition), `Some("2026")` (the
13931        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
13932        // template scaffolds via [`Caixa::template`] and every
13933        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
13934        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
13935        // carries by construction), `Some("2018")` / `Some("2021")` /
13936        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
13937        // peer with Cargo's `[package] edition` grammar every future-
13938        // introduced sibling to `"2026"` will follow), and eight
13939        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
13940        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
13941        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
13942        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
13943        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
13944        // length-numeric, `Some("latest")` free-form-non-year — the
13945        // sentinels pin the accessor doesn't silently absorb the
13946        // refusal cases into a substrate-default-edition fallback).
13947        //
13948        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
13949        // return scalar accessor pin on the substrate primitive —
13950        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
13951        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13952        // (3f16e2f) pins that opened the "outer [`Caixa`]
13953        // `Option<&str>` scalar" projection pin pattern this pin folds
13954        // on. Sibling in shape to the peer per-`:placement`
13955        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13956        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13957        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13958        // axes, extended onto the outer top-level [`Caixa`] universal-
13959        // axis surface's last unlifted `Option<String>` slot. Pins
13960        // against a future silent detour that returned an owned
13961        // `Option<String>` (which would type-check but silently
13962        // allocate on every accessor call, breaking the zero-cost
13963        // projection every peer sibling accessor carries), a
13964        // `Some("") → None` collapse (which would silently absorb the
13965        // `EdicaoEmpty` refusal case at the accessor boundary and any
13966        // future edition-aware consumer would silently fall back to
13967        // the substrate's default edition on a struct-literal
13968        // `Caixa { edicao: Some(""), .. }`), or a
13969        // `None → Some("2026")` collapse (which would silently reify
13970        // the substrate's default edition at the accessor boundary
13971        // and every downstream consumer keying off the
13972        // `Option::is_none()` discriminator would lose the "author
13973        // omitted the slot" signal).
13974        for edicao in [
13975            None,
13976            Some(""),
13977            Some("2026"),
13978            Some("2018"),
13979            Some("2021"),
13980            Some("2024"),
13981            Some("2026 "),
13982            Some(" 2026"),
13983            Some("2026\n"),
13984            Some("2026"),
13985            Some("v2026"),
13986            Some("2026.1"),
13987            Some("26"),
13988            Some("latest"),
13989        ] {
13990            let c = caixa_with_edicao(edicao);
13991            assert_eq!(
13992                c.edicao(),
13993                edicao,
13994                "Caixa::edicao must return :edicao verbatim (got {:?}, \
13995                 expected {edicao:?})",
13996                c.edicao(),
13997            );
13998            assert_eq!(
13999                c.edicao(),
14000                c.edicao.as_deref(),
14001                "Caixa::edicao must byte-equal the raw \
14002                 `self.edicao.as_deref()` field access across every \
14003                 value in the Option<&str> accept-set",
14004            );
14005        }
14006    }
14007
14008    #[test]
14009    fn validate_edicao_empty_arm_routes_through_accessor() {
14010        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
14011        // must key off [`Caixa::edicao`], not the raw
14012        // `self.edicao.as_deref()` field access. Structurally: a
14013        // `Caixa { edicao: Some(""), .. }` must surface the
14014        // `EdicaoEmpty` refusal exactly, and a
14015        // `Caixa { edicao: Some("2026"), .. }` (the canonical
14016        // 4-digit-ASCII-decimal-year form) must pass validate. The
14017        // pair jointly pins the accessor + validate-gate composition:
14018        // any future silent detour that had the accessor return `None`
14019        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
14020        // would silently absorb the `EdicaoEmpty` refusal at the
14021        // accessor boundary and the validate gate would accept a
14022        // struct-literal `Caixa { edicao: Some(""), .. }` — the
14023        // composition pin catches that at caixa-core build time.
14024        //
14025        // Peer of the [`Caixa::licenca`] (6d5bc28)
14026        // `validate_licenca_empty_arm_routes_through_accessor`,
14027        // [`Caixa::repositorio`] (cc7332d)
14028        // `validate_repositorio_empty_arm_routes_through_accessor`,
14029        // and [`Caixa::descricao`] (3f16e2f)
14030        // `validate_descricao_empty_arm_routes_through_accessor`
14031        // composition pins on the sibling outer top-level [`Caixa`]
14032        // `Option<&str>` universal-axis surface — same "the validate /
14033        // shape-gate predicate must route through the substrate-
14034        // primitive typed dispatch" discipline extended onto the
14035        // fourth and final outer top-level [`Caixa`] universal-axis
14036        // `Option<&str>`-composition surface, closing the accessor-
14037        // composition family.
14038        let c = caixa_with_edicao(Some(""));
14039        assert!(
14040            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
14041            "validate_edicao must reject edicao == Some(\"\") with \
14042             EdicaoEmpty — the accessor and the validate gate must \
14043             route through the same substrate-primitive typed dispatch \
14044             on the :edicao empty arm",
14045        );
14046        let c = caixa_with_edicao(Some("2026"));
14047        assert!(
14048            c.validate_edicao().is_ok(),
14049            "validate_edicao must accept edicao == Some(\"2026\") \
14050             (the canonical 4-digit-ASCII-decimal-year shape)",
14051        );
14052    }
14053
14054    #[test]
14055    fn edicao_projects_option_str_by_borrow() {
14056        // The by-borrow pin: [`Caixa::edicao`] returns
14057        // `Option<&str>` by borrow — the `&str` borrows the underlying
14058        // `String` storage of the `Option<String>` slot and the
14059        // accessor must not allocate a fresh `String` on every call.
14060        // Peer of the [`Caixa::licenca`] (6d5bc28),
14061        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
14062        // (3f16e2f) by-borrow pins on the peer outer top-level
14063        // [`Caixa`] `Option<&str>`-return axes, and of the
14064        // per-`:placement`
14065        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
14066        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
14067        // return axis, extended onto the fourth and final outer top-
14068        // level [`Caixa`] universal-axis `Option<&str>` shape — the
14069        // accessor's returned `&str` must borrow from `&self` (the
14070        // returned reference's lifetime is tied to `&self`), and
14071        // calling the accessor twice on the same [`Caixa`] must yield
14072        // the same `Option<&str>` verbatim (idempotent, no side
14073        // effects on `&self`).
14074        //
14075        // Pins against a future silent detour that returned an owned
14076        // `Option<String>` (which would type-check but silently
14077        // allocate on every call, breaking the zero-cost projection
14078        // every peer sibling accessor carries), or a one-arm-only
14079        // accessor that returned a saturating value on some sentinel
14080        // input (breaking the pass-through invariant the sibling
14081        // required-scalar accessors carry).
14082        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
14083            let c = caixa_with_edicao(edicao);
14084            let first = c.edicao();
14085            let second = c.edicao();
14086            assert_eq!(
14087                first, second,
14088                "Caixa::edicao must be idempotent — two successive \
14089                 calls on the same &self must return the same \
14090                 Option<&str>",
14091            );
14092            assert_eq!(
14093                first, edicao,
14094                "Caixa::edicao must return :edicao verbatim by \
14095                 borrow — got {first:?}, expected {edicao:?}",
14096            );
14097        }
14098    }
14099
14100    #[test]
14101    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
14102        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
14103        // label caixa-identity scalar pin: [`Caixa::nome`] must return
14104        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
14105        // the raw field access across every representative value in
14106        // the accept-set — the canonical `"demo"` template baseline
14107        // (the same `feira init`-scaffolded default the sibling
14108        // `validate_nome_accepts_canonical_template` positive-control
14109        // gate pins), plus every sibling per-typed-slot atom accessor's
14110        // canonical positive-arm byte-string (`"catalog"` per
14111        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
14112        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
14113        // `caixa-helm`/`caixa-flux` cross-crate integration-test
14114        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
14115        // canonical example), plus every past-the-guard sentinel for
14116        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
14117        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
14118        // the bare DNS-1123 63-byte cap but overflows the joint
14119        // `lareira-<nome>` chart-name budget the sibling
14120        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
14121        //
14122        // The past-the-guard sentinels pin the accessor doesn't
14123        // silently absorb the refusal cases into a template-derived
14124        // fallback (a future `.nome().is_empty().then(|| "demo")`
14125        // collapse would silently absorb the `NomeEmpty` refusal at
14126        // the accessor boundary and the validate gate would accept a
14127        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
14128        // catches that at caixa-core build time).
14129        //
14130        // First outer top-level [`Caixa`] `&str`-return required-
14131        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
14132        // required-scalar" projection pattern the sibling per-`Caixa`
14133        // `:versao` future lift folds on. Sibling in shape to the peer
14134        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
14135        // required-`String`-carry accessor pin on the sibling per-
14136        // sub-struct required-axis, extended onto the outer top-level
14137        // [`Caixa`] universal-axis required-`String`-carry axis.
14138        for nome in [
14139            "demo",
14140            "catalog",
14141            "cart",
14142            "hello-rio",
14143            "checkout",
14144            "",
14145            "Bad_Name",
14146            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
14147        ] {
14148            let c = caixa_with_nome(nome);
14149            assert_eq!(
14150                c.nome(),
14151                nome,
14152                "Caixa::nome must return :nome verbatim (got {}, \
14153                 expected {nome})",
14154                c.nome(),
14155            );
14156            assert_eq!(
14157                c.nome(),
14158                c.nome.as_str(),
14159                "Caixa::nome must byte-equal the raw .nome field \
14160                 access across every value in the String accept-set",
14161            );
14162        }
14163    }
14164
14165    #[test]
14166    fn validate_nome_empty_arm_routes_through_accessor() {
14167        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
14168        // key off [`Caixa::nome`], not the raw `.nome` field access.
14169        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
14170        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
14171        // template baseline (the peer positive-arm the sibling
14172        // `validate_nome_accepts_canonical_template` gate carves out)
14173        // must pass validate. The pair jointly pins the accessor +
14174        // validate-gate composition: any future silent detour that
14175        // had the accessor return a fresh `"demo"` on the empty arm
14176        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
14177        // would silently absorb the `NomeEmpty` refusal at the
14178        // accessor boundary and the validate gate would accept a
14179        // struct-literal `Caixa { nome: "".into(), .. }` — the
14180        // composition pin catches that at caixa-core build time.
14181        //
14182        // Peer of the sibling per-`Caixa`
14183        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
14184        // / `validate_repositorio_empty_arm_routes_through_accessor`
14185        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
14186        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
14187        // (2641cbd) composition pins on the sibling outer top-level
14188        // [`Caixa`] `Option<&str>` axes — same "the validate /
14189        // shape-gate predicate must route through the substrate-
14190        // primitive typed dispatch" discipline extended onto the peer
14191        // outer top-level [`Caixa`] required-`&str` composition axis.
14192        let c = caixa_with_nome("");
14193        assert!(
14194            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
14195            "validate_nome must reject nome == \"\" with NomeEmpty — \
14196             the accessor and the validate gate must route through the \
14197             same substrate-primitive typed dispatch on the :nome \
14198             empty-arm",
14199        );
14200        let c = caixa_with_nome("demo");
14201        assert!(
14202            c.validate_nome().is_ok(),
14203            "validate_nome must accept nome == \"demo\" (the canonical \
14204             DNS-1123-label template baseline)",
14205        );
14206    }
14207
14208    #[test]
14209    fn nome_projects_str_by_borrow() {
14210        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
14211        // — the `&str` borrows the underlying `String` storage of the
14212        // required `nome` slot and the accessor must not allocate a
14213        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
14214        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
14215        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
14216        // by-borrow pins on the peer outer top-level [`Caixa`]
14217        // `Option<&str>`-return axes, extended onto the first outer
14218        // top-level [`Caixa`] required-`&str`-return axis — the
14219        // accessor's returned `&str` must borrow from `&self` (the
14220        // returned reference's lifetime is tied to `&self`), and
14221        // calling the accessor twice on the same [`Caixa`] must yield
14222        // the same `&str` verbatim (idempotent, no side effects on
14223        // `&self`).
14224        //
14225        // Pins against a future silent detour that returned an owned
14226        // `String` (which would type-check but silently allocate on
14227        // every call, breaking the zero-cost projection every peer
14228        // sibling accessor carries), an accidental
14229        // `.nome.to_lowercase()` detour that returned a fresh
14230        // allocation through an already-DNS-1123-lowercase-only
14231        // string (breaking a future `const fn` regression), or a
14232        // one-arm-only accessor that returned a canonicalized value
14233        // on some sentinel input (breaking the pass-through invariant
14234        // the sibling required-scalar accessors carry).
14235        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
14236            let c = caixa_with_nome(nome);
14237            let first = c.nome();
14238            let second = c.nome();
14239            assert_eq!(
14240                first, second,
14241                "Caixa::nome must be idempotent — two successive calls \
14242                 on the same &self must return the same &str",
14243            );
14244            assert_eq!(
14245                first, nome,
14246                "Caixa::nome must return :nome verbatim by borrow — \
14247                 got {first}, expected {nome}",
14248            );
14249        }
14250    }
14251
14252    #[test]
14253    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
14254        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
14255        // pinned-version scalar pin: [`Caixa::versao`] must return the
14256        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
14257        // raw `.versao` field access across every representative value
14258        // in the accept-set — the canonical `"0.1.0"` template baseline
14259        // (the same `feira init`-scaffolded default the sibling
14260        // `validate_versao_accepts_canonical_template` positive-control
14261        // gate pins), plus every canonical SemVer-2 shape the sibling
14262        // `validate_versao_accepts_canonical_forms` positive-arm sweep
14263        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
14264        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
14265        // `"10.20.30"`), plus every past-the-guard sentinel for the
14266        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
14267        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
14268        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
14269        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
14270        // `"latest"` the docker-tag-shape footgun — the sentinels pin
14271        // the accessor doesn't silently absorb the refusal cases into a
14272        // template-derived fallback like `"0.1.0"`).
14273        //
14274        // The past-the-guard sentinels pin the accessor doesn't silently
14275        // absorb the refusal cases into a template-derived fallback (a
14276        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
14277        // silently absorb the `VersaoEmpty` refusal at the accessor
14278        // boundary and the validate gate would accept a struct-literal
14279        // `Caixa { versao: "".into(), .. }` — the pin catches that at
14280        // caixa-core build time).
14281        //
14282        // Second outer top-level [`Caixa`] `&str`-return required-scalar
14283        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
14284        // scalar" projection pattern the sibling per-`Caixa`
14285        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
14286        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
14287        // (4127bb6) / per-`:children`
14288        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
14289        // / per-`:upgrade-from`
14290        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
14291        // struct `:versao`-shaped `&str`-return accessor pins on the
14292        // sibling per-typed-slot version-carrier axes, extended onto the
14293        // second outer top-level [`Caixa`] universal-axis required-
14294        // `String`-carry axis so the two universal-axis identity-
14295        // carrying scalars every `defcaixa` form supplies (`:nome` +
14296        // `:versao`) share the same "one typed dispatch per axis" pin
14297        // discipline.
14298        for versao in [
14299            "0.1.0",
14300            "0.0.0",
14301            "1.0.0",
14302            "0.2.0-rc.1",
14303            "1.0.0-alpha.0",
14304            "1.0.0+build.42",
14305            "1.0.0-rc.1+build.42",
14306            "10.20.30",
14307            "",
14308            "v0.1.0",
14309            "0.1",
14310            "^0.1",
14311            "0.1.0.0",
14312            "latest",
14313        ] {
14314            let c = caixa_with_versao(versao);
14315            assert_eq!(
14316                c.versao(),
14317                versao,
14318                "Caixa::versao must return :versao verbatim (got {}, \
14319                 expected {versao})",
14320                c.versao(),
14321            );
14322            assert_eq!(
14323                c.versao(),
14324                c.versao.as_str(),
14325                "Caixa::versao must byte-equal the raw .versao field \
14326                 access across every value in the String accept-set",
14327            );
14328        }
14329    }
14330
14331    #[test]
14332    fn validate_versao_empty_arm_routes_through_accessor() {
14333        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
14334        // must key off [`Caixa::versao`], not the raw `.versao` field
14335        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
14336        // surface the `VersaoEmpty` refusal exactly, and the canonical
14337        // `"0.1.0"` template baseline (the peer positive-arm the sibling
14338        // `validate_versao_accepts_canonical_template` gate carves out)
14339        // must pass validate. The pair jointly pins the accessor +
14340        // validate-gate composition: any future silent detour that had
14341        // the accessor return a fresh `"0.1.0"` on the empty arm
14342        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
14343        // would silently absorb the `VersaoEmpty` refusal at the
14344        // accessor boundary and the validate gate would accept a
14345        // struct-literal `Caixa { versao: "".into(), .. }` — the
14346        // composition pin catches that at caixa-core build time.
14347        //
14348        // Peer of the sibling per-`Caixa`
14349        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
14350        // composition pin on the sibling outer top-level [`Caixa`]
14351        // required-`&str` universal-axis surface — same "the validate /
14352        // shape-gate predicate must route through the substrate-
14353        // primitive typed dispatch" discipline extended onto the peer
14354        // outer top-level [`Caixa`] required-`&str` universal-axis
14355        // pinned-version composition axis, closing the second
14356        // coordinate of the "one canonical typed dispatch per per-Caixa
14357        // required-`&str` universal-axis" discipline.
14358        let c = caixa_with_versao("");
14359        assert!(
14360            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
14361            "validate_versao must reject versao == \"\" with VersaoEmpty — \
14362             the accessor and the validate gate must route through the \
14363             same substrate-primitive typed dispatch on the :versao \
14364             empty-arm",
14365        );
14366        let c = caixa_with_versao("0.1.0");
14367        assert!(
14368            c.validate_versao().is_ok(),
14369            "validate_versao must accept versao == \"0.1.0\" (the \
14370             canonical SemVer-2 template baseline)",
14371        );
14372    }
14373
14374    #[test]
14375    fn versao_projects_str_by_borrow() {
14376        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
14377        // — the `&str` borrows the underlying `String` storage of the
14378        // required `versao` slot and the accessor must not allocate a
14379        // fresh `String` on every call. Peer of the [`Caixa::nome`]
14380        // (e6b7d97) by-borrow pin on the sibling outer top-level
14381        // [`Caixa`] required-`&str`-return axis, extended onto the
14382        // second outer top-level [`Caixa`] required-`&str`-return
14383        // universal-axis pinned-version surface — the accessor's
14384        // returned `&str` must borrow from `&self` (the returned
14385        // reference's lifetime is tied to `&self`), and calling the
14386        // accessor twice on the same [`Caixa`] must yield the same
14387        // `&str` verbatim (idempotent, no side effects on `&self`).
14388        //
14389        // Pins against a future silent detour that returned an owned
14390        // `String` (which would type-check but silently allocate on
14391        // every call, breaking the zero-cost projection every peer
14392        // sibling accessor carries), an accidental
14393        // `semver::Version::parse(&self.versao).unwrap().to_string()`
14394        // detour that returned a canonicalized fresh allocation through
14395        // an already-canonical byte-string (breaking a future `const fn`
14396        // regression and silently absorbing the `VersaoInvalid` refusal
14397        // at the accessor boundary), or a one-arm-only accessor that
14398        // returned a canonicalized value on some sentinel input
14399        // (breaking the pass-through invariant the sibling required-
14400        // scalar accessors carry).
14401        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
14402            let c = caixa_with_versao(versao);
14403            let first = c.versao();
14404            let second = c.versao();
14405            assert_eq!(
14406                first, second,
14407                "Caixa::versao must be idempotent — two successive \
14408                 calls on the same &self must return the same &str",
14409            );
14410            assert_eq!(
14411                first, versao,
14412                "Caixa::versao must return :versao verbatim by borrow \
14413                 — got {first}, expected {versao}",
14414            );
14415        }
14416    }
14417
14418    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
14419        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14420        c.kind = kind;
14421        c
14422    }
14423
14424    #[test]
14425    fn kind_returns_kind_variant_verbatim_across_permutations() {
14426        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
14427        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
14428        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
14429        // the raw `.kind` field access across every variant in the
14430        // closed accept-set (`Biblioteca` — the library kind that
14431        // exports lisp forms; `Binario` — the nix-built executable kind
14432        // under `exe/`; `Servico` — the wasm-component daemon kind
14433        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
14434        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
14435        // composition kind).
14436        //
14437        // Pins against a future silent detour that re-derived the kind
14438        // from a peer axis (an accidental fallback to
14439        // `if !servicos.is_empty() { Servico } else if
14440        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
14441        // collapse that read the code-surface / mesh-slot columns into
14442        // the kind discriminator), a variant remap the operator
14443        // authors on one consumer without the other, or a stale-derive
14444        // detour that substituted [`CaixaKind::Biblioteca`] as the
14445        // default when the field held any other variant (which would
14446        // silently collapse the distinction between "author explicitly
14447        // declared `:kind Servico`" and "author declared any other
14448        // kind" every downstream renderer-dispatch site depends on).
14449        //
14450        // First outer top-level [`Caixa`] `Copy`-return required-enum-
14451        // discriminant accessor pin — opens the "outer [`Caixa`]
14452        // `Copy`-return required-discriminant" projection pattern.
14453        // Sibling in shape to the peer per-`:supervisor`
14454        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
14455        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
14456        // (921fe1b), and per-`:children`
14457        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
14458        // `Copy`-return closed-set-enum discriminant accessor pins on
14459        // the sibling nested-spec typed-slot discriminator axes,
14460        // extended here to the outer top-level [`Caixa`] universal-
14461        // axis surface.
14462        for kind in [
14463            CaixaKind::Biblioteca,
14464            CaixaKind::Binario,
14465            CaixaKind::Servico,
14466            CaixaKind::Supervisor,
14467            CaixaKind::Aplicacao,
14468        ] {
14469            let c = caixa_with_kind(kind);
14470            assert_eq!(
14471                c.kind(),
14472                kind,
14473                "Caixa::kind must return :kind verbatim (got {:?}, \
14474                 expected {kind:?})",
14475                c.kind(),
14476            );
14477            assert_eq!(
14478                c.kind(),
14479                c.kind,
14480                "Caixa::kind accessor and .kind field access must \
14481                 byte-equal — the accessor is the substrate-primitive \
14482                 typed dispatch every downstream kind-gate consumer \
14483                 must route through",
14484            );
14485        }
14486    }
14487
14488    #[test]
14489    fn require_kind_reads_through_lifted_kind_accessor() {
14490        // Two-consumer coherence pin: the [`crate::render::require_kind`]
14491        // entry-gate predicate (the canonical two-line
14492        // `require_kind(caixa, Servico)?` prelude every per-Servico /
14493        // per-Aplicacao renderer runs at its entry-point) and the
14494        // sibling [`crate::render::KindMismatch`] error carrier's
14495        // `actual:` field (which names the offending caixa's variant
14496        // in the diagnostic) must both key off the lifted accessor, so
14497        // any future rebrand on the typed slot's reader shape lands at
14498        // exactly one place. Pins the two-site coherence by exercising
14499        // every off-diagonal `(actual, expected)` pair across the
14500        // closed accept-set — the `KindMismatch { actual, expected }`
14501        // surfaced on the mismatch arm must byte-equal the pair the
14502        // accessor returns for each side.
14503        //
14504        // Peer of the sibling per-`:placement`
14505        // `validate_placement_reads_through_lifted_estrategia_accessor`
14506        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
14507        // `Copy`-return discriminant axis — same "the entry-gate
14508        // predicate and the error carrier's `actual:` field must route
14509        // through the substrate-primitive typed dispatch" discipline
14510        // extended onto the outer top-level [`Caixa`] universal-axis
14511        // discriminant surface.
14512        for expected in [
14513            CaixaKind::Biblioteca,
14514            CaixaKind::Binario,
14515            CaixaKind::Servico,
14516            CaixaKind::Supervisor,
14517            CaixaKind::Aplicacao,
14518        ] {
14519            for actual in [
14520                CaixaKind::Biblioteca,
14521                CaixaKind::Binario,
14522                CaixaKind::Servico,
14523                CaixaKind::Supervisor,
14524                CaixaKind::Aplicacao,
14525            ] {
14526                let c = caixa_with_kind(actual);
14527                let result = crate::render::require_kind(&c, expected);
14528                if expected == actual {
14529                    assert!(
14530                        result.is_ok(),
14531                        "require_kind must accept when actual == expected \
14532                         (actual={actual:?}, expected={expected:?})",
14533                    );
14534                } else {
14535                    let err = result.expect_err("require_kind must reject when actual != expected");
14536                    assert_eq!(
14537                        err.actual,
14538                        c.kind(),
14539                        "KindMismatch.actual must byte-equal Caixa::kind() \
14540                         — the error carrier's `actual:` field reads \
14541                         through the lifted accessor",
14542                    );
14543                    assert_eq!(
14544                        err.expected, expected,
14545                        "KindMismatch.expected must byte-equal the \
14546                         expected variant passed to require_kind",
14547                    );
14548                }
14549            }
14550        }
14551    }
14552
14553    #[test]
14554    fn aplicacao_view_kind_gate_routes_through_accessor() {
14555        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
14556        // must key off [`Caixa::kind`], not the raw `.kind` field
14557        // access. Structurally: a `Caixa { kind: X, .. }` for any
14558        // non-`Aplicacao` variant must fold to `None` on the
14559        // `aplicacao_view` composer (the "kind mismatch → no typed
14560        // view" contract every downstream Aplicacao consumer keys off
14561        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
14562        // `Some(_)`. The pair jointly pins the accessor + view-gate
14563        // composition: any future silent detour that had the accessor
14564        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
14565        // input would silently absorb the kind-mismatch case at the
14566        // accessor boundary and every per-Aplicacao renderer would
14567        // silently render a non-Aplicacao caixa's mesh slots — the
14568        // composition pin catches that at caixa-core build time.
14569        //
14570        // Peer of the sibling per-`Caixa`
14571        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
14572        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
14573        // composition pins on the sibling outer top-level [`Caixa`]
14574        // required-`&str` universal-axis surfaces — same "the
14575        // composer / validate gate must route through the substrate-
14576        // primitive typed dispatch" discipline extended onto the
14577        // outer top-level [`Caixa`] `Copy`-return required-
14578        // discriminant composition axis.
14579        for kind in [
14580            CaixaKind::Biblioteca,
14581            CaixaKind::Binario,
14582            CaixaKind::Servico,
14583            CaixaKind::Supervisor,
14584        ] {
14585            let c = caixa_with_kind(kind);
14586            assert!(
14587                c.aplicacao_view().is_none(),
14588                "aplicacao_view must return None on non-Aplicacao \
14589                 kind {kind:?} — the composer's kind-gate must route \
14590                 through Caixa::kind()",
14591            );
14592        }
14593        let c = caixa_with_kind(CaixaKind::Aplicacao);
14594        assert!(
14595            c.aplicacao_view().is_some(),
14596            "aplicacao_view must return Some on kind Aplicacao — \
14597             the composer's kind-gate must accept the matching arm \
14598             through Caixa::kind()",
14599        );
14600    }
14601
14602    #[test]
14603    fn supervisor_view_kind_gate_routes_through_accessor() {
14604        // Composition pin (mirror of the sibling
14605        // `aplicacao_view_kind_gate_routes_through_accessor` on the
14606        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
14607        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
14608        // field access. A `Caixa { kind: X, .. }` for any non-
14609        // `Supervisor` variant must fold to `None` on the
14610        // `supervisor_view` composer, and a `Caixa { kind:
14611        // Supervisor, .. }` must fold to `Some(_)`. Same peer
14612        // composition pin discipline on the second `_view` composer
14613        // axis.
14614        for kind in [
14615            CaixaKind::Biblioteca,
14616            CaixaKind::Binario,
14617            CaixaKind::Servico,
14618            CaixaKind::Aplicacao,
14619        ] {
14620            let c = caixa_with_kind(kind);
14621            assert!(
14622                c.supervisor_view().is_none(),
14623                "supervisor_view must return None on non-Supervisor \
14624                 kind {kind:?} — the composer's kind-gate must route \
14625                 through Caixa::kind()",
14626            );
14627        }
14628        let mut c = caixa_with_kind(CaixaKind::Supervisor);
14629        // A Supervisor caixa needs a strategy + at least one child to
14630        // fold to a Some(_) that also validates; the composer itself
14631        // requires only the kind arm, so bare kind flip is enough to
14632        // pin the `Some(_)` return, but we populate the minimum
14633        // supervisor shape so a future strengthening of the composer
14634        // to reject an empty spec doesn't false-positive this pin.
14635        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
14636        c.children = vec![crate::supervisor::ChildSpec {
14637            caixa: "child".into(),
14638            versao: "^0.1".into(),
14639            restart: crate::supervisor::RestartPolicy::Permanent,
14640        }];
14641        assert!(
14642            c.supervisor_view().is_some(),
14643            "supervisor_view must return Some on kind Supervisor — \
14644             the composer's kind-gate must accept the matching arm \
14645             through Caixa::kind()",
14646        );
14647    }
14648
14649    #[test]
14650    fn kind_projects_by_copy() {
14651        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
14652        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
14653        // `&self` (the returned value is owned, `Copy`-projected from
14654        // the underlying [`CaixaKind`] storage; two calls on the same
14655        // [`Caixa`] must yield byte-equal values). Peer of the peer
14656        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
14657        // `SupervisorSpec::estrategia` / per-`:children`
14658        // `ChildSpec::restart` `Copy`-return discriminant accessor
14659        // pins on the sibling nested-spec typed-slot discriminator
14660        // axes, extended onto the first outer top-level [`Caixa`]
14661        // required-`Copy`-return axis — pins against a future silent
14662        // detour that returned `&CaixaKind` (which would type-check
14663        // but silently constrain every consumer's callsite to a
14664        // borrow-shaped dispatch, breaking the zero-cost `Copy`
14665        // projection every peer sibling accessor carries).
14666        for kind in [
14667            CaixaKind::Biblioteca,
14668            CaixaKind::Binario,
14669            CaixaKind::Servico,
14670            CaixaKind::Supervisor,
14671            CaixaKind::Aplicacao,
14672        ] {
14673            let c = caixa_with_kind(kind);
14674            let first: CaixaKind = c.kind();
14675            let second: CaixaKind = c.kind();
14676            assert_eq!(
14677                first, second,
14678                "Caixa::kind must be idempotent — two successive \
14679                 calls on the same &self must return the same \
14680                 CaixaKind variant",
14681            );
14682            assert_eq!(
14683                first, kind,
14684                "Caixa::kind must return :kind verbatim by Copy — \
14685                 got {first:?}, expected {kind:?}",
14686            );
14687        }
14688    }
14689
14690    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
14691
14692    #[test]
14693    fn autores_returns_autores_slice_verbatim_across_permutations() {
14694        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
14695        // name-list slice pin: [`Caixa::autores`] must return the
14696        // `:autores` typed [`Vec<String>`] list verbatim as a
14697        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
14698        // access across every representative value in the accept-set —
14699        // `[]` (the "no maintainers declared" arm every existing
14700        // fixture without an `:autores` line carries), `[""]` (a past-
14701        // the-guard sentinel that pins the accessor doesn't perform a
14702        // silent `[""] → []` collapse on the empty-entry arm — validate
14703        // rejects `[""]` through `AutorEmpty` but the accessor must
14704        // ship the raw slot verbatim so a validate-time gate regression
14705        // surfaces at the caixa-helm emit boundary rather than being
14706        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
14707        // canonical single-maintainer form every `feira init` template
14708        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
14709        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
14710        // (the canonical RFC-5322 `<name> <email>` form the
14711        // `is_chart_maintainer_name_shape` predicate accepts), and
14712        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
14713        // sentinel — validate rejects through `AutorDuplicate` but the
14714        // accessor must ship the raw slot verbatim).
14715        //
14716        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
14717        // pin on the substrate primitive — opens the "outer [`Caixa`]
14718        // `&[T]` slice" projection pattern the sibling per-`Caixa`
14719        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
14720        // / `:servicos` / `:upgrade-from` / `:children` future lifts
14721        // fold on. Sibling in shape to the peer per-`:supervisor`
14722        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
14723        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
14724        // (a6e18d7), per-`:membros`
14725        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
14726        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14727        // (0dcc926), and per-`:upgrade-from :instructions`
14728        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
14729        // `&[T]`-return slice accessor pins on the sibling per-M2 /
14730        // per-M3 typed-slot list axes, extended onto the outer top-
14731        // level [`Caixa`] universal-axis surface. Pins against a future
14732        // silent detour that returned an owned `Vec<String>` (which
14733        // would type-check but silently clone on every accessor call,
14734        // breaking the zero-cost projection every peer sibling slice
14735        // accessor carries), a `[""] → []` collapse (which would
14736        // silently absorb the `AutorEmpty` refusal case at the accessor
14737        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
14738        // would silently absorb the `AutorDuplicate` refusal case at
14739        // the accessor boundary and the caixa-helm `maintainers:` fold
14740        // would silently render a dedupped list on a struct-literal
14741        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
14742        for autores in [
14743            vec![],
14744            vec![""],
14745            vec!["pleme-io"],
14746            vec!["alice", "bob"],
14747            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
14748            vec!["pleme-io", "pleme-io"],
14749        ] {
14750            let c = caixa_with_autores(autores.clone());
14751            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14752            assert_eq!(
14753                c.autores(),
14754                expected.as_slice(),
14755                "Caixa::autores must return :autores verbatim (got {:?}, \
14756                 expected {expected:?})",
14757                c.autores(),
14758            );
14759            assert_eq!(
14760                c.autores(),
14761                c.autores.as_slice(),
14762                "Caixa::autores must byte-equal the raw \
14763                 `self.autores.as_slice()` field access across every \
14764                 value in the Vec<String> accept-set",
14765            );
14766        }
14767    }
14768
14769    #[test]
14770    fn validate_autores_empty_entry_arm_routes_through_accessor() {
14771        // Composition pin: [`Caixa::validate_autores`]'s per-entry
14772        // empty-arm gate must key off [`Caixa::autores`], not the raw
14773        // `&self.autores` field-borrow walk. Structurally: a
14774        // `Caixa { autores: vec!["".into()], .. }` must surface the
14775        // `AutorEmpty` refusal exactly, and a
14776        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
14777        // canonical single-maintainer form) must pass validate. The
14778        // pair jointly pins the accessor + validate-gate composition:
14779        // any future silent detour that had the accessor return an
14780        // empty slice on the `[""]` arm (a
14781        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
14782        // would silently absorb the `AutorEmpty` refusal at the
14783        // accessor boundary and the validate gate would accept a
14784        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
14785        // the composition pin catches that at caixa-core build time.
14786        //
14787        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
14788        // accessor-composition pin
14789        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
14790        // sibling `Option<&str>`-composition axis and the
14791        // per-`:politicas :circuit-breaker`
14792        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
14793        // accessor-composition pin
14794        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
14795        // on the sibling required-`u32`-composition axis — same "the
14796        // validate / shape-gate predicate must route through the
14797        // substrate-primitive typed dispatch" discipline extended onto
14798        // the outer top-level [`Caixa`] universal-axis `&[T]`-
14799        // composition surface.
14800        let c = caixa_with_autores(vec![""]);
14801        assert!(
14802            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
14803            "validate_autores must reject autores == vec![\"\"] with \
14804             AutorEmpty — the accessor and the validate gate must \
14805             route through the same substrate-primitive typed dispatch \
14806             on the :autores per-entry empty arm",
14807        );
14808        let c = caixa_with_autores(vec!["pleme-io"]);
14809        assert!(
14810            c.validate_autores().is_ok(),
14811            "validate_autores must accept autores == vec![\"pleme-io\"] \
14812             (the canonical single-maintainer shape every `feira init` \
14813             template scaffolds)",
14814        );
14815    }
14816
14817    #[test]
14818    fn autores_projects_slice_by_borrow() {
14819        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
14820        // borrow — the returned slice borrows the underlying
14821        // `Vec<String>` storage of the `:autores` slot and the
14822        // accessor must not clone the backing `Vec` on every call.
14823        // Peer of the per-`:membros`
14824        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
14825        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14826        // (0dcc926) / per-`:placement`
14827        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
14828        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
14829        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
14830        // typed-slot `&[T]`-return axes, extended onto the outer top-
14831        // level [`Caixa`] universal-axis `&[String]` shape — the
14832        // accessor's returned slice must borrow from `&self` (the
14833        // returned reference's lifetime is tied to `&self`), and
14834        // calling the accessor twice on the same [`Caixa`] must yield
14835        // slices that are pointer-equal (the underlying byte-buffer is
14836        // the storage `Vec`'s allocation, not a fresh copy) as well as
14837        // value-equal (idempotent, no side effects on `&self`).
14838        //
14839        // Pins against a future silent detour that returned an owned
14840        // `Vec<String>` (which would type-check but silently clone on
14841        // every call, breaking the zero-cost projection every peer
14842        // sibling slice accessor carries), a `&Vec<String>` return
14843        // (which would leak the backing `Vec`'s grow/push/reserve
14844        // surface no downstream consumer reaches for), or a one-arm-
14845        // only accessor that returned a saturating value on some
14846        // sentinel input (breaking the pass-through invariant the
14847        // sibling slice accessors carry).
14848        for autores in [
14849            vec![],
14850            vec!["pleme-io"],
14851            vec!["alice", "bob"],
14852            vec!["pleme-io", "pleme-io"],
14853        ] {
14854            let c = caixa_with_autores(autores.clone());
14855            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14856            let first = c.autores();
14857            let second = c.autores();
14858            assert_eq!(
14859                first, second,
14860                "Caixa::autores must be idempotent — two successive \
14861                 calls on the same &self must return the same \
14862                 &[String]",
14863            );
14864            assert_eq!(
14865                first.as_ptr(),
14866                second.as_ptr(),
14867                "Caixa::autores must borrow the underlying Vec<String> \
14868                 storage — two successive calls must return slices \
14869                 with the same backing pointer (a fresh Vec<String> \
14870                 clone would change the pointer on every call)",
14871            );
14872            assert_eq!(
14873                first,
14874                expected.as_slice(),
14875                "Caixa::autores must return :autores verbatim by \
14876                 borrow — got {first:?}, expected {expected:?}",
14877            );
14878        }
14879    }
14880
14881    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
14882
14883    #[test]
14884    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
14885        // The canonical per-`Caixa` `:etiquetas` universal-axis
14886        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
14887        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
14888        // as a `&[String]`, byte-equal to the raw
14889        // `self.etiquetas.as_slice()` access across every representative
14890        // value in the accept-set — `[]` (the "no tags declared" arm
14891        // every existing fixture without an `:etiquetas` line carries),
14892        // `[""]` (a past-the-guard sentinel that pins the accessor
14893        // doesn't perform a silent `[""] → []` collapse on the empty-
14894        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
14895        // but the accessor must ship the raw slot verbatim so a
14896        // validate-time gate regression surfaces at the caixa-helm emit
14897        // boundary rather than being silently absorbed into a keyword-
14898        // drop), `["demo"]` (the canonical single-tag form every
14899        // `feira init` template scaffolds), `["example", "aplicacao",
14900        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
14901        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
14902        // (a past-the-guard duplicate sentinel — validate rejects
14903        // through `EtiquetaDuplicate` but the accessor must ship the
14904        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
14905        // at chart-render time isn't silently promoted into the
14906        // accessor boundary and struct-literal
14907        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
14908        // fixtures continue to expose the duplicate at the accessor).
14909        //
14910        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
14911        // pin on the substrate primitive — folds on the "outer
14912        // [`Caixa`] `&[T]` slice" projection pattern
14913        // `autores_returns_autores_slice_verbatim_across_permutations`
14914        // (b5d813f) opened, sibling in shape and idiom. Pins against a
14915        // future silent detour that returned an owned `Vec<String>`
14916        // (which would type-check but silently clone on every accessor
14917        // call, breaking the zero-cost projection every peer sibling
14918        // slice accessor carries), a `[""] → []` collapse (which would
14919        // silently absorb the `EtiquetaEmpty` refusal case at the
14920        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
14921        // (which would silently absorb the `EtiquetaDuplicate` refusal
14922        // case at the accessor boundary — the caixa-helm chart-render
14923        // `BTreeSet::collect` dedup is downstream of the accessor and
14924        // must not be silently promoted into it).
14925        for etiquetas in [
14926            vec![],
14927            vec![""],
14928            vec!["demo"],
14929            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
14930            vec!["demo", "demo"],
14931        ] {
14932            let c = caixa_with_etiquetas(etiquetas.clone());
14933            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14934            assert_eq!(
14935                c.etiquetas(),
14936                expected.as_slice(),
14937                "Caixa::etiquetas must return :etiquetas verbatim (got \
14938                 {:?}, expected {expected:?})",
14939                c.etiquetas(),
14940            );
14941            assert_eq!(
14942                c.etiquetas(),
14943                c.etiquetas.as_slice(),
14944                "Caixa::etiquetas must byte-equal the raw \
14945                 `self.etiquetas.as_slice()` field access across every \
14946                 value in the Vec<String> accept-set",
14947            );
14948        }
14949    }
14950
14951    #[test]
14952    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
14953        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
14954        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
14955        // `&self.etiquetas` field-borrow walk. Structurally: a
14956        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
14957        // `EtiquetaEmpty` refusal exactly, and a
14958        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
14959        // single-tag form) must pass validate. The pair jointly pins
14960        // the accessor + validate-gate composition: any future silent
14961        // detour that had the accessor return an empty slice on the
14962        // `[""]` arm (a
14963        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
14964        // silently absorb the `EtiquetaEmpty` refusal at the accessor
14965        // boundary and the validate gate would accept a struct-literal
14966        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
14967        // pin catches that at caixa-core build time.
14968        //
14969        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
14970        // through_accessor` (b5d813f) accessor-composition pin on the
14971        // sibling `&[T]`-composition axis — same "the validate / shape-
14972        // gate predicate must route through the substrate-primitive
14973        // typed dispatch" discipline extended onto the sibling outer
14974        // top-level [`Caixa`] `&[T]`-composition surface.
14975        let c = caixa_with_etiquetas(vec![""]);
14976        assert!(
14977            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
14978            "validate_etiquetas must reject etiquetas == vec![\"\"] \
14979             with EtiquetaEmpty — the accessor and the validate gate \
14980             must route through the same substrate-primitive typed \
14981             dispatch on the :etiquetas per-entry empty arm",
14982        );
14983        let c = caixa_with_etiquetas(vec!["demo"]);
14984        assert!(
14985            c.validate_etiquetas().is_ok(),
14986            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
14987             (the canonical single-tag shape every `feira init` \
14988             template scaffolds)",
14989        );
14990    }
14991
14992    #[test]
14993    fn etiquetas_projects_slice_by_borrow() {
14994        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
14995        // by borrow — the returned slice borrows the underlying
14996        // `Vec<String>` storage of the `:etiquetas` slot and the
14997        // accessor must not clone the backing `Vec` on every call.
14998        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14999        // (b5d813f) by-borrow pin on the sibling outer top-level
15000        // [`Caixa`] `&[String]`-return axis — the accessor's returned
15001        // slice must borrow from `&self` (the returned reference's
15002        // lifetime is tied to `&self`), and calling the accessor twice
15003        // on the same [`Caixa`] must yield slices that are pointer-
15004        // equal (the underlying byte-buffer is the storage `Vec`'s
15005        // allocation, not a fresh copy) as well as value-equal
15006        // (idempotent, no side effects on `&self`).
15007        //
15008        // Pins against a future silent detour that returned an owned
15009        // `Vec<String>` (which would type-check but silently clone on
15010        // every call, breaking the zero-cost projection every peer
15011        // sibling slice accessor carries), a `&Vec<String>` return
15012        // (which would leak the backing `Vec`'s grow/push/reserve
15013        // surface no downstream consumer reaches for), or a one-arm-
15014        // only accessor that returned a saturating value on some
15015        // sentinel input (breaking the pass-through invariant the
15016        // sibling slice accessors carry).
15017        for etiquetas in [
15018            vec![],
15019            vec!["demo"],
15020            vec!["example", "aplicacao", "mesh"],
15021            vec!["demo", "demo"],
15022        ] {
15023            let c = caixa_with_etiquetas(etiquetas.clone());
15024            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
15025            let first = c.etiquetas();
15026            let second = c.etiquetas();
15027            assert_eq!(
15028                first, second,
15029                "Caixa::etiquetas must be idempotent — two successive \
15030                 calls on the same &self must return the same \
15031                 &[String]",
15032            );
15033            assert_eq!(
15034                first.as_ptr(),
15035                second.as_ptr(),
15036                "Caixa::etiquetas must borrow the underlying \
15037                 Vec<String> storage — two successive calls must \
15038                 return slices with the same backing pointer (a fresh \
15039                 Vec<String> clone would change the pointer on every \
15040                 call)",
15041            );
15042            assert_eq!(
15043                first,
15044                expected.as_slice(),
15045                "Caixa::etiquetas must return :etiquetas verbatim by \
15046                 borrow — got {first:?}, expected {expected:?}",
15047            );
15048        }
15049    }
15050
15051    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
15052
15053    #[test]
15054    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
15055        // The canonical per-`Caixa` `:bibliotecas` universal-axis
15056        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
15057        // must return the `:bibliotecas` typed [`Vec<String>`] list
15058        // verbatim as a `&[String]`, byte-equal to the raw
15059        // `self.bibliotecas.as_slice()` access across every
15060        // representative value in the accept-set — `[]` (the "no
15061        // libraries declared" arm every `:kind` other than `Biblioteca`
15062        // + every `Biblioteca` relying on the canonical
15063        // `lib/<nome>.lisp` implicit-default path carries; the
15064        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
15065        // fires exactly on this empty-slot + `Biblioteca`-kind
15066        // combination), `[""]` (a past-the-guard sentinel that pins
15067        // the accessor doesn't perform a silent `[""] → []` collapse
15068        // on the empty-entry arm — validate rejects `[""]` through
15069        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
15070        // must ship the raw slot verbatim so a validate-time gate
15071        // regression surfaces at the `feira build` phase-1 parse
15072        // boundary rather than being silently absorbed into a
15073        // library-drop), `["lib/demo.lisp"]` (the canonical single-
15074        // entry form `Caixa::template` scaffolds and every `feira init`
15075        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
15076        // (the canonical multi-library form the
15077        // `validate_code_paths_accepts_explicit_relative_paths_on_
15078        // every_slot` fixture emits), and `["lib/foo.lisp",
15079        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
15080        // validate rejects through `CodePathDuplicate { slot:
15081        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
15082        // but the accessor must ship the raw slot verbatim so the
15083        // `feira build` `for entry in caixa.bibliotecas()` parse walk
15084        // sees the duplicate at the accessor boundary and struct-
15085        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
15086        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
15087        // the duplicate at the accessor).
15088        //
15089        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
15090        // pin on the substrate primitive — folds on the "outer
15091        // [`Caixa`] `&[T]` slice" projection pattern
15092        // `autores_returns_autores_slice_verbatim_across_permutations`
15093        // (b5d813f) opened and
15094        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15095        // (78c7d3c) folded on, sibling in shape and idiom. Pins
15096        // against a future silent detour that returned an owned
15097        // `Vec<String>` (which would type-check but silently clone on
15098        // every accessor call, breaking the zero-cost projection
15099        // every peer sibling slice accessor carries), a `[""] → []`
15100        // collapse (which would silently absorb the `CodePathEmpty`
15101        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
15102        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
15103        // would silently absorb the `CodePathDuplicate` refusal case
15104        // at the accessor boundary — the per-slot set-not-multiset
15105        // gate is downstream of the accessor and must not be silently
15106        // promoted into it).
15107        for bibliotecas in [
15108            vec![],
15109            vec![""],
15110            vec!["lib/demo.lisp"],
15111            vec!["lib/demo.lisp", "lib/helpers.lisp"],
15112            vec!["lib/foo.lisp", "lib/foo.lisp"],
15113        ] {
15114            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
15115            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
15116            assert_eq!(
15117                c.bibliotecas(),
15118                expected.as_slice(),
15119                "Caixa::bibliotecas must return :bibliotecas verbatim \
15120                 (got {:?}, expected {expected:?})",
15121                c.bibliotecas(),
15122            );
15123            assert_eq!(
15124                c.bibliotecas(),
15125                c.bibliotecas.as_slice(),
15126                "Caixa::bibliotecas must byte-equal the raw \
15127                 `self.bibliotecas.as_slice()` field access across \
15128                 every value in the Vec<String> accept-set",
15129            );
15130        }
15131    }
15132
15133    #[test]
15134    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
15135        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15136        // empty-arm gate on the `:bibliotecas` slot must key off
15137        // [`Caixa::bibliotecas`], not a divergent raw
15138        // `&self.bibliotecas` field-borrow walk. Structurally: a
15139        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
15140        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
15141        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
15142        // into()], .. }` (the canonical single-library form
15143        // `Caixa::template` scaffolds) must pass validate. The pair
15144        // jointly pins the accessor + validate-gate composition: any
15145        // future silent detour that had the accessor return an empty
15146        // slice on the `[""]` arm (a `.iter().filter(|s|
15147        // !s.is_empty()).collect()` collapse) would silently absorb
15148        // the `CodePathEmpty` refusal at the accessor boundary and
15149        // the validate gate would accept a struct-literal
15150        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
15151        // composition pin catches that at caixa-core build time.
15152        //
15153        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
15154        // through_accessor` (b5d813f) and
15155        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15156        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15157        // composition axes — same "the validate / shape-gate
15158        // predicate must route through the substrate-primitive typed
15159        // dispatch" discipline extended onto the sibling outer top-
15160        // level [`Caixa`] `&[T]`-composition surface. Nominally the
15161        // in-tree `validate_code_paths` production body still keys
15162        // off the internal `[(":bibliotecas", &self.bibliotecas,
15163        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15164        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15165        // (the tuple's homogeneous slice-typed shape blocks a per-
15166        // element accessor swap in isolation — a future companion
15167        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
15168        // `&[T]` slice-accessor axis closes that tuple onto the
15169        // triple of typed dispatches as a unit); the composition pin
15170        // catches any future accessor-side silent filter drop against
15171        // that eventual tuple-closure regardless of whether the
15172        // `:bibliotecas` slot is threaded through the accessor or the
15173        // raw field access at the tuple's construction site.
15174        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
15175        assert!(
15176            matches!(
15177                c.validate_code_paths(),
15178                Err(ManifestError::CodePathEmpty {
15179                    slot: ":bibliotecas"
15180                })
15181            ),
15182            "validate_code_paths must reject bibliotecas == vec![\"\"] \
15183             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
15184             accessor and the validate gate must route through the \
15185             same substrate-primitive typed dispatch on the \
15186             :bibliotecas per-entry empty arm",
15187        );
15188        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
15189        assert!(
15190            c.validate_code_paths().is_ok(),
15191            "validate_code_paths must accept bibliotecas == \
15192             vec![\"lib/demo.lisp\"] (the canonical single-library \
15193             shape every `feira init` template scaffolds)",
15194        );
15195    }
15196
15197    #[test]
15198    fn bibliotecas_projects_slice_by_borrow() {
15199        // The by-borrow pin: [`Caixa::bibliotecas`] returns
15200        // `&[String]` by borrow — the returned slice borrows the
15201        // underlying `Vec<String>` storage of the `:bibliotecas` slot
15202        // and the accessor must not clone the backing `Vec` on every
15203        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15204        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
15205        // by-borrow pins on the sibling outer top-level [`Caixa`]
15206        // `&[String]`-return axes — the accessor's returned slice
15207        // must borrow from `&self` (the returned reference's lifetime
15208        // is tied to `&self`), and calling the accessor twice on the
15209        // same [`Caixa`] must yield slices that are pointer-equal
15210        // (the underlying byte-buffer is the storage `Vec`'s
15211        // allocation, not a fresh copy) as well as value-equal
15212        // (idempotent, no side effects on `&self`).
15213        //
15214        // Pins against a future silent detour that returned an owned
15215        // `Vec<String>` (which would type-check but silently clone on
15216        // every call, breaking the zero-cost projection every peer
15217        // sibling slice accessor carries), a `&Vec<String>` return
15218        // (which would leak the backing `Vec`'s grow/push/reserve
15219        // surface no downstream consumer reaches for), or a one-arm-
15220        // only accessor that returned a saturating value on some
15221        // sentinel input (breaking the pass-through invariant the
15222        // sibling slice accessors carry).
15223        for bibliotecas in [
15224            vec![],
15225            vec!["lib/demo.lisp"],
15226            vec!["lib/demo.lisp", "lib/helpers.lisp"],
15227            vec!["lib/foo.lisp", "lib/foo.lisp"],
15228        ] {
15229            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
15230            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
15231            let first = c.bibliotecas();
15232            let second = c.bibliotecas();
15233            assert_eq!(
15234                first, second,
15235                "Caixa::bibliotecas must be idempotent — two \
15236                 successive calls on the same &self must return the \
15237                 same &[String]",
15238            );
15239            assert_eq!(
15240                first.as_ptr(),
15241                second.as_ptr(),
15242                "Caixa::bibliotecas must borrow the underlying \
15243                 Vec<String> storage — two successive calls must \
15244                 return slices with the same backing pointer (a \
15245                 fresh Vec<String> clone would change the pointer on \
15246                 every call)",
15247            );
15248            assert_eq!(
15249                first,
15250                expected.as_slice(),
15251                "Caixa::bibliotecas must return :bibliotecas verbatim \
15252                 by borrow — got {first:?}, expected {expected:?}",
15253            );
15254        }
15255    }
15256
15257    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
15258
15259    #[test]
15260    fn exe_returns_exe_slice_verbatim_across_permutations() {
15261        // The canonical per-`Caixa` `:exe` universal-axis
15262        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
15263        // must return the `:exe` typed [`Vec<String>`] list verbatim as
15264        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
15265        // access across every representative value in the accept-set —
15266        // `[]` (the "no executable declared" arm every `:kind` other
15267        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
15268        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
15269        // + `Binario`-kind combination), `[""]` (a past-the-guard
15270        // sentinel that pins the accessor doesn't perform a silent
15271        // `[""] → []` collapse on the empty-entry arm — validate rejects
15272        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
15273        // accessor must ship the raw slot verbatim so a validate-time
15274        // gate regression surfaces at the layout / `feira nix` boundary
15275        // rather than being silently absorbed into an executable-drop),
15276        // `["exe/cli"]` (the canonical single-entry Binario form every
15277        // in-tree `caixa_with_code_paths` positive control uses),
15278        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
15279        // form the `validate_code_paths_accepts_explicit_relative_paths_
15280        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
15281        // (a past-the-guard duplicate sentinel — validate rejects
15282        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
15283        // set-not-multiset gate, but the accessor must ship the raw
15284        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
15285        // into(), "exe/cli".into()], .. }` fixtures continue to expose
15286        // the duplicate at the accessor).
15287        //
15288        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
15289        // pin on the substrate primitive — folds on the "outer
15290        // [`Caixa`] `&[T]` slice" projection pattern
15291        // `autores_returns_autores_slice_verbatim_across_permutations`
15292        // (b5d813f) opened,
15293        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15294        // (78c7d3c) folded on, and
15295        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15296        // (8a36c23) closed the universal-axis text-tag family of.
15297        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
15298        // the sibling `:servicos` future lift closes onto. Pins against
15299        // a future silent detour that returned an owned `Vec<String>`
15300        // (which would type-check but silently clone on every accessor
15301        // call, breaking the zero-cost projection every peer sibling
15302        // slice accessor carries), a `[""] → []` collapse (which would
15303        // silently absorb the `CodePathEmpty` refusal case at the
15304        // accessor boundary), or an `["exe/cli", "exe/cli"] →
15305        // ["exe/cli"]` dedup collapse (which would silently absorb the
15306        // `CodePathDuplicate` refusal case at the accessor boundary —
15307        // the per-slot set-not-multiset gate is downstream of the
15308        // accessor and must not be silently promoted into it).
15309        for exe in [
15310            vec![],
15311            vec![""],
15312            vec!["exe/cli"],
15313            vec!["exe/cli", "exe/serve"],
15314            vec!["exe/cli", "exe/cli"],
15315        ] {
15316            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15317            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15318            assert_eq!(
15319                c.exe(),
15320                expected.as_slice(),
15321                "Caixa::exe must return :exe verbatim (got {:?}, \
15322                 expected {expected:?})",
15323                c.exe(),
15324            );
15325            assert_eq!(
15326                c.exe(),
15327                c.exe.as_slice(),
15328                "Caixa::exe must byte-equal the raw \
15329                 `self.exe.as_slice()` field access across every value \
15330                 in the Vec<String> accept-set",
15331            );
15332        }
15333    }
15334
15335    #[test]
15336    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
15337        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15338        // empty-arm gate on the `:exe` slot must key off
15339        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
15340        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
15341        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
15342        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
15343        // (the canonical single-executable form every in-tree
15344        // `caixa_with_code_paths` positive control uses) must pass
15345        // validate. The pair jointly pins the accessor + validate-gate
15346        // composition: any future silent detour that had the accessor
15347        // return an empty slice on the `[""]` arm (a
15348        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
15349        // silently absorb the `CodePathEmpty` refusal at the accessor
15350        // boundary and the validate gate would accept a struct-literal
15351        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
15352        // catches that at caixa-core build time.
15353        //
15354        // Peer of the per-`Caixa`
15355        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15356        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
15357        // (b5d813f), and
15358        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15359        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15360        // composition axes — same "the validate / shape-gate predicate
15361        // must route through the substrate-primitive typed dispatch"
15362        // discipline extended onto the sibling outer top-level [`Caixa`]
15363        // `&[T]`-composition surface. Nominally the in-tree
15364        // `validate_code_paths` production body still keys off the
15365        // internal `[(":bibliotecas", &self.bibliotecas,
15366        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15367        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15368        // (the tuple's homogeneous slice-typed shape blocks a per-
15369        // element accessor swap in isolation — a future companion lift
15370        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
15371        // accessor axis closes that tuple onto the triple of typed
15372        // dispatches as a unit); the composition pin catches any future
15373        // accessor-side silent filter drop against that eventual tuple-
15374        // closure regardless of whether the `:exe` slot is threaded
15375        // through the accessor or the raw field access at the tuple's
15376        // construction site.
15377        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
15378        assert!(
15379            matches!(
15380                c.validate_code_paths(),
15381                Err(ManifestError::CodePathEmpty { slot: ":exe" })
15382            ),
15383            "validate_code_paths must reject exe == vec![\"\"] \
15384             with CodePathEmpty {{ slot: \":exe\" }} — the \
15385             accessor and the validate gate must route through the \
15386             same substrate-primitive typed dispatch on the \
15387             :exe per-entry empty arm",
15388        );
15389        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
15390        assert!(
15391            c.validate_code_paths().is_ok(),
15392            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
15393             (the canonical single-executable shape every in-tree \
15394             `caixa_with_code_paths` positive control uses)",
15395        );
15396    }
15397
15398    #[test]
15399    fn exe_projects_slice_by_borrow() {
15400        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
15401        // borrow — the returned slice borrows the underlying
15402        // `Vec<String>` storage of the `:exe` slot and the accessor
15403        // must not clone the backing `Vec` on every call. Peer of the
15404        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
15405        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
15406        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
15407        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
15408        // return axes — the accessor's returned slice must borrow from
15409        // `&self` (the returned reference's lifetime is tied to
15410        // `&self`), and calling the accessor twice on the same
15411        // [`Caixa`] must yield slices that are pointer-equal (the
15412        // underlying byte-buffer is the storage `Vec`'s allocation,
15413        // not a fresh copy) as well as value-equal (idempotent, no
15414        // side effects on `&self`).
15415        //
15416        // Pins against a future silent detour that returned an owned
15417        // `Vec<String>` (which would type-check but silently clone on
15418        // every call, breaking the zero-cost projection every peer
15419        // sibling slice accessor carries), a `&Vec<String>` return
15420        // (which would leak the backing `Vec`'s grow/push/reserve
15421        // surface no downstream consumer reaches for), or a one-arm-
15422        // only accessor that returned a saturating value on some
15423        // sentinel input (breaking the pass-through invariant the
15424        // sibling slice accessors carry).
15425        for exe in [
15426            vec![],
15427            vec!["exe/cli"],
15428            vec!["exe/cli", "exe/serve"],
15429            vec!["exe/cli", "exe/cli"],
15430        ] {
15431            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15432            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15433            let first = c.exe();
15434            let second = c.exe();
15435            assert_eq!(
15436                first, second,
15437                "Caixa::exe must be idempotent — two successive calls \
15438                 on the same &self must return the same &[String]",
15439            );
15440            assert_eq!(
15441                first.as_ptr(),
15442                second.as_ptr(),
15443                "Caixa::exe must borrow the underlying Vec<String> \
15444                 storage — two successive calls must return slices \
15445                 with the same backing pointer (a fresh Vec<String> \
15446                 clone would change the pointer on every call)",
15447            );
15448            assert_eq!(
15449                first,
15450                expected.as_slice(),
15451                "Caixa::exe must return :exe verbatim by borrow — \
15452                 got {first:?}, expected {expected:?}",
15453            );
15454        }
15455    }
15456
15457    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
15458
15459    #[test]
15460    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
15461        // The canonical per-`Caixa` `:servicos` universal-axis
15462        // ComputeUnit-CR-YAML-entry-path-list slice pin:
15463        // [`Caixa::servicos`] must return the `:servicos` typed
15464        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
15465        // the raw `self.servicos.as_slice()` access across every
15466        // representative value in the accept-set — `[]` (the "no
15467        // ComputeUnit-CR declared" arm every `:kind` other than
15468        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
15469        // `ServicoWithoutServicos` arm-gate fires exactly on this
15470        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
15471        // guard sentinel that pins the accessor doesn't perform a
15472        // silent `[""] → []` collapse on the empty-entry arm — validate
15473        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
15474        // but the accessor must ship the raw slot verbatim so a
15475        // validate-time gate regression surfaces at the layout /
15476        // per-Servico renderer boundary rather than being silently
15477        // absorbed into a component-drop),
15478        // `["servicos/demo.computeunit.yaml"]` (the canonical
15479        // singleton V0-shape every in-tree `caixa_with_code_paths`
15480        // positive control uses; the same shape
15481        // [`crate::require_single_servico`] admits),
15482        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
15483        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
15484        // singularity gate rejects through `ServicoCountMismatch
15485        // { count: 2 }` but the accessor must ship the raw slot
15486        // verbatim so struct-literal `Caixa { servicos: vec![...,
15487        // ...], .. }` fixtures continue to expose the count at the
15488        // accessor), and `["servicos/a.computeunit.yaml",
15489        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
15490        // sentinel — validate rejects through
15491        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
15492        // set-not-multiset gate, but the accessor must ship the raw
15493        // slot verbatim so struct-literal fixtures continue to expose
15494        // the duplicate at the accessor).
15495        //
15496        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
15497        // slice accessor pin on the substrate primitive — folds on the
15498        // "outer [`Caixa`] `&[T]` slice" projection pattern
15499        // `autores_returns_autores_slice_verbatim_across_permutations`
15500        // (b5d813f) opened,
15501        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15502        // (78c7d3c) folded on,
15503        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15504        // (8a36c23) closed the universal-axis text-tag family of, and
15505        // `exe_returns_exe_slice_verbatim_across_permutations`
15506        // (65d9527) opened the foreign-code-slot sub-family of. Closes
15507        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
15508        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
15509        // `:servicos`) now each carries a substrate-canonical slice
15510        // accessor. Pins against a future silent detour that returned
15511        // an owned `Vec<String>` (which would type-check but silently
15512        // clone on every accessor call, breaking the zero-cost
15513        // projection every peer sibling slice accessor carries), a
15514        // `[""] → []` collapse (which would silently absorb the
15515        // `CodePathEmpty` refusal case at the accessor boundary), an
15516        // `[a, a] → [a]` dedup collapse (which would silently absorb
15517        // the `CodePathDuplicate` refusal case at the accessor
15518        // boundary — the per-slot set-not-multiset gate is downstream
15519        // of the accessor and must not be silently promoted into it),
15520        // or a `[a, b] → [a]` singleton collapse (which would silently
15521        // absorb the V0 `ServicoCountMismatch` refusal case at the
15522        // accessor boundary — the V0 singularity gate is downstream of
15523        // the accessor and must not be silently promoted into it).
15524        for servicos in [
15525            vec![],
15526            vec![""],
15527            vec!["servicos/demo.computeunit.yaml"],
15528            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15529            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15530        ] {
15531            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15532            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15533            assert_eq!(
15534                c.servicos(),
15535                expected.as_slice(),
15536                "Caixa::servicos must return :servicos verbatim (got \
15537                 {:?}, expected {expected:?})",
15538                c.servicos(),
15539            );
15540            assert_eq!(
15541                c.servicos(),
15542                c.servicos.as_slice(),
15543                "Caixa::servicos must byte-equal the raw \
15544                 `self.servicos.as_slice()` field access across every \
15545                 value in the Vec<String> accept-set",
15546            );
15547        }
15548    }
15549
15550    #[test]
15551    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
15552        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15553        // empty-arm gate on the `:servicos` slot must key off
15554        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
15555        // field-borrow walk. Structurally: a `Caixa { servicos:
15556        // vec!["".into()], .. }` must surface the `CodePathEmpty
15557        // { slot: ":servicos" }` refusal exactly, and a `Caixa
15558        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
15559        // .. }` (the canonical singleton V0-shape every in-tree
15560        // `caixa_with_code_paths` positive control uses) must pass
15561        // validate. The pair jointly pins the accessor + validate-gate
15562        // composition: any future silent detour that had the accessor
15563        // return an empty slice on the `[""]` arm (a `.iter().filter
15564        // (|s| !s.is_empty()).collect()` collapse) would silently
15565        // absorb the `CodePathEmpty` refusal at the accessor boundary
15566        // and the validate gate would accept a struct-literal
15567        // `Caixa { servicos: vec!["".into()], .. }` — the composition
15568        // pin catches that at caixa-core build time.
15569        //
15570        // Peer of the per-`Caixa`
15571        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15572        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15573        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
15574        // (b5d813f), and
15575        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15576        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15577        // composition axes — same "the validate / shape-gate predicate
15578        // must route through the substrate-primitive typed dispatch"
15579        // discipline extended onto the sibling outer top-level
15580        // [`Caixa`] `&[T]`-composition surface, closing the trio of
15581        // code-surface accessor-composition pins on the same axis.
15582        // Nominally the in-tree `validate_code_paths` production body
15583        // still keys off the internal
15584        // `[(":bibliotecas", &self.bibliotecas,
15585        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15586        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15587        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
15588        // per-element accessor swap in isolation — a future companion
15589        // lift promotes the tuple's element type to `&[String]` and
15590        // threads the triple of typed dispatches through as a unit);
15591        // the composition pin catches any future accessor-side silent
15592        // filter drop against that eventual tuple-closure regardless
15593        // of whether the `:servicos` slot is threaded through the
15594        // accessor or the raw field access at the tuple's construction
15595        // site.
15596        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
15597        assert!(
15598            matches!(
15599                c.validate_code_paths(),
15600                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
15601            ),
15602            "validate_code_paths must reject servicos == vec![\"\"] \
15603             with CodePathEmpty {{ slot: \":servicos\" }} — the \
15604             accessor and the validate gate must route through the \
15605             same substrate-primitive typed dispatch on the \
15606             :servicos per-entry empty arm",
15607        );
15608        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
15609        assert!(
15610            c.validate_code_paths().is_ok(),
15611            "validate_code_paths must accept servicos == \
15612             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
15613             singleton V0-shape every in-tree `caixa_with_code_paths` \
15614             positive control uses)",
15615        );
15616    }
15617
15618    #[test]
15619    fn servicos_projects_slice_by_borrow() {
15620        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
15621        // borrow — the returned slice borrows the underlying
15622        // `Vec<String>` storage of the `:servicos` slot and the
15623        // accessor must not clone the backing `Vec` on every call.
15624        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15625        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
15626        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
15627        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
15628        // the sibling outer top-level [`Caixa`] `&[String]`-return
15629        // axes — the accessor's returned slice must borrow from
15630        // `&self` (the returned reference's lifetime is tied to
15631        // `&self`), and calling the accessor twice on the same
15632        // [`Caixa`] must yield slices that are pointer-equal (the
15633        // underlying byte-buffer is the storage `Vec`'s allocation,
15634        // not a fresh copy) as well as value-equal (idempotent, no
15635        // side effects on `&self`).
15636        //
15637        // Pins against a future silent detour that returned an owned
15638        // `Vec<String>` (which would type-check but silently clone on
15639        // every call, breaking the zero-cost projection every peer
15640        // sibling slice accessor carries), a `&Vec<String>` return
15641        // (which would leak the backing `Vec`'s grow/push/reserve
15642        // surface no downstream consumer reaches for), or a one-arm-
15643        // only accessor that returned a saturating value on some
15644        // sentinel input (breaking the pass-through invariant the
15645        // sibling slice accessors carry).
15646        for servicos in [
15647            vec![],
15648            vec!["servicos/demo.computeunit.yaml"],
15649            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15650            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15651        ] {
15652            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15653            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15654            let first = c.servicos();
15655            let second = c.servicos();
15656            assert_eq!(
15657                first, second,
15658                "Caixa::servicos must be idempotent — two successive \
15659                 calls on the same &self must return the same &[String]",
15660            );
15661            assert_eq!(
15662                first.as_ptr(),
15663                second.as_ptr(),
15664                "Caixa::servicos must borrow the underlying \
15665                 Vec<String> storage — two successive calls must \
15666                 return slices with the same backing pointer (a fresh \
15667                 Vec<String> clone would change the pointer on every \
15668                 call)",
15669            );
15670            assert_eq!(
15671                first,
15672                expected.as_slice(),
15673                "Caixa::servicos must return :servicos verbatim by \
15674                 borrow — got {first:?}, expected {expected:?}",
15675            );
15676        }
15677    }
15678
15679    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
15680
15681    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
15682        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15683        c.deps = deps;
15684        c
15685    }
15686
15687    #[test]
15688    fn deps_returns_deps_slice_verbatim_across_permutations() {
15689        // The canonical per-`Caixa` `:deps` universal-axis runtime-
15690        // dependency-declaration-list slice pin: [`Caixa::deps`] must
15691        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
15692        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
15693        // access across every representative value in the accept-set —
15694        // `[]` (the "no runtime deps declared" arm every existing
15695        // fixture without a `:deps` line carries; the
15696        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
15697        // single-entry list (the shape most consumer caixas carry), a
15698        // canonical two-entry list (the multi-dep runtime closure), and
15699        // two past-the-guard sentinels — a `[""]`-`:nome` entry
15700        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15701        // `NomeInvalid` but the accessor must ship the raw slot
15702        // verbatim) and a `[a, a]` duplicate (validate rejects through
15703        // `DuplicateNome { list: ":deps" }` but the accessor must ship
15704        // the raw slot verbatim so struct-literal fixtures continue to
15705        // expose the duplicate at the accessor).
15706        //
15707        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
15708        // pin on the substrate primitive — opens the outer-`Caixa`
15709        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
15710        // future lift closes on. Peer of the closed outer-`Caixa`
15711        // foreign-code-slot `&[String]` sub-family
15712        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15713        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
15714        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
15715        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
15716        // (`autores_returns_autores_slice_verbatim_across_permutations`
15717        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15718        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
15719        // projection pattern onto a novel element-type axis (`Dep`
15720        // composite vs the prior sibling family's `String` scalar).
15721        // Pins against a future silent detour that returned an owned
15722        // `Vec<Dep>` (which would type-check but silently clone on every
15723        // accessor call, breaking the zero-cost projection every peer
15724        // sibling slice accessor carries), a `[""] → []` collapse (which
15725        // would silently absorb the `NomeEmpty` refusal case at the
15726        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15727        // would silently absorb the `DuplicateNome` refusal case at the
15728        // accessor boundary).
15729        for deps in [
15730            vec![],
15731            vec![Dep::simple("", "^0.1")],
15732            vec![Dep::simple("caixa-teia", "^0.1")],
15733            vec![
15734                Dep::simple("caixa-teia", "^0.1"),
15735                Dep::simple("caixa-core", "^0.1"),
15736            ],
15737            vec![
15738                Dep::simple("caixa-teia", "^0.1"),
15739                Dep::simple("caixa-teia", "^0.2"),
15740            ],
15741        ] {
15742            let c = caixa_with_deps(deps.clone());
15743            assert_eq!(
15744                c.deps(),
15745                deps.as_slice(),
15746                "Caixa::deps must return :deps verbatim (got {:?}, \
15747                 expected {deps:?})",
15748                c.deps(),
15749            );
15750            assert_eq!(
15751                c.deps(),
15752                c.deps.as_slice(),
15753                "Caixa::deps must element-equal the raw \
15754                 `self.deps.as_slice()` field access across every \
15755                 value in the Vec<Dep> accept-set",
15756            );
15757        }
15758    }
15759
15760    #[test]
15761    fn validate_deps_duplicate_arm_routes_through_accessor() {
15762        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
15763        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
15764        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
15765        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
15766        // "^0.2")], .. }` must surface the `DuplicateNome { list:
15767        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
15768        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
15769        // form) must pass validate. The pair jointly pins the accessor +
15770        // validate-gate composition: any future silent detour that had
15771        // the accessor return a dedupped slice on the `[a, a]` arm (a
15772        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15773        // would silently absorb the `DuplicateNome` refusal at the
15774        // accessor boundary and the validate gate would accept a
15775        // struct-literal `Caixa` carrying the drift — the composition
15776        // pin catches that at caixa-core build time.
15777        //
15778        // Peer of the per-`Caixa`
15779        // `validate_autores_empty_entry_arm_routes_through_accessor`
15780        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15781        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15782        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15783        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
15784        // (611f78b) accessor-composition pins on the sibling `&[T]`-
15785        // composition axes — same "the validate gate must route through
15786        // the substrate-primitive typed dispatch" discipline extended
15787        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
15788        // composition surface, opening the outer-`Caixa` dependency-slot
15789        // arm of the composition-pin family.
15790        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15791        let err = c.validate_deps().unwrap_err();
15792        assert!(
15793            matches!(
15794                err,
15795                DepError::DuplicateNome { ref nome, list } if nome == "d"
15796                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
15797            ),
15798            "validate_deps must reject deps == \
15799             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15800             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
15801             accessor and the validate gate must route through the \
15802             same substrate-primitive typed dispatch on the :deps \
15803             within-list duplicate arm (got {err:?})",
15804        );
15805        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
15806        assert!(
15807            c.validate_deps().is_ok(),
15808            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
15809             (the canonical single-entry form)",
15810        );
15811    }
15812
15813    #[test]
15814    fn deps_projects_slice_by_borrow() {
15815        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
15816        // — the returned slice borrows the underlying `Vec<Dep>` storage
15817        // of the `:deps` slot and the accessor must not clone the
15818        // backing `Vec` on every call. Peer of the per-`Caixa`
15819        // `autores_projects_slice_by_borrow` (b5d813f),
15820        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15821        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15822        // `exe_projects_slice_by_borrow` (65d9527), and
15823        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15824        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15825        // axes — the accessor's returned slice must borrow from `&self`
15826        // (the returned reference's lifetime is tied to `&self`), and
15827        // calling the accessor twice on the same [`Caixa`] must yield
15828        // slices that are pointer-equal (the underlying byte-buffer is
15829        // the storage `Vec`'s allocation, not a fresh copy) as well as
15830        // value-equal (idempotent, no side effects on `&self`).
15831        //
15832        // Pins against a future silent detour that returned an owned
15833        // `Vec<Dep>` (which would type-check but silently clone on
15834        // every call), a `&Vec<Dep>` return (which would leak the
15835        // backing `Vec`'s grow/push/reserve surface no downstream
15836        // consumer reaches for), or a one-arm-only accessor that
15837        // returned a saturating value on some sentinel input.
15838        for deps in [
15839            vec![],
15840            vec![Dep::simple("caixa-teia", "^0.1")],
15841            vec![
15842                Dep::simple("caixa-teia", "^0.1"),
15843                Dep::simple("caixa-core", "^0.1"),
15844            ],
15845        ] {
15846            let c = caixa_with_deps(deps.clone());
15847            let first = c.deps();
15848            let second = c.deps();
15849            assert_eq!(
15850                first, second,
15851                "Caixa::deps must be idempotent — two successive calls \
15852                 on the same &self must return the same &[Dep]",
15853            );
15854            assert_eq!(
15855                first.as_ptr(),
15856                second.as_ptr(),
15857                "Caixa::deps must borrow the underlying Vec<Dep> \
15858                 storage — two successive calls must return slices \
15859                 with the same backing pointer (a fresh Vec<Dep> clone \
15860                 would change the pointer on every call)",
15861            );
15862            assert_eq!(
15863                first,
15864                deps.as_slice(),
15865                "Caixa::deps must return :deps verbatim by borrow — \
15866                 got {first:?}, expected {deps:?}",
15867            );
15868        }
15869    }
15870
15871    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
15872
15873    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
15874        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15875        c.deps_dev = deps_dev;
15876        c
15877    }
15878
15879    #[test]
15880    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
15881        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
15882        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
15883        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
15884        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
15885        // access across every representative value in the accept-set —
15886        // `[]` (the "no dev deps declared" arm every existing fixture
15887        // without a `:deps-dev` line carries; the [`Caixa::template`]
15888        // scaffold emits `:deps-dev ()`), a canonical single-entry list
15889        // (the shape most consumer caixas carry — a `tatara-check` dev
15890        // pin), a canonical two-entry list (the multi-dev-dep closure),
15891        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
15892        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15893        // `NomeInvalid` but the accessor must ship the raw slot
15894        // verbatim) and a `[a, a]` duplicate (validate rejects through
15895        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
15896        // ship the raw slot verbatim so struct-literal fixtures continue
15897        // to expose the duplicate at the accessor).
15898        //
15899        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
15900        // pin on the substrate primitive — closes the outer-`Caixa`
15901        // dependency-slot `&[Dep]` sub-family the sibling
15902        // `deps_returns_deps_slice_verbatim_across_permutations`
15903        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
15904        // slice" projection pattern onto the sibling dev-dep axis —
15905        // pins against a future silent detour that returned an owned
15906        // `Vec<Dep>` (which would type-check but silently clone on every
15907        // accessor call, breaking the zero-cost projection every peer
15908        // sibling slice accessor carries), a `[""] → []` collapse (which
15909        // would silently absorb the `NomeEmpty` refusal case at the
15910        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15911        // would silently absorb the `DuplicateNome` refusal case at the
15912        // accessor boundary).
15913        for deps_dev in [
15914            vec![],
15915            vec![Dep::simple("", "^0.1")],
15916            vec![Dep::simple("tatara-check", "^0.1")],
15917            vec![
15918                Dep::simple("tatara-check", "^0.1"),
15919                Dep::simple("caixa-lint", "^0.1"),
15920            ],
15921            vec![
15922                Dep::simple("tatara-check", "^0.1"),
15923                Dep::simple("tatara-check", "^0.2"),
15924            ],
15925        ] {
15926            let c = caixa_with_deps_dev(deps_dev.clone());
15927            assert_eq!(
15928                c.deps_dev(),
15929                deps_dev.as_slice(),
15930                "Caixa::deps_dev must return :deps-dev verbatim (got \
15931                 {:?}, expected {deps_dev:?})",
15932                c.deps_dev(),
15933            );
15934            assert_eq!(
15935                c.deps_dev(),
15936                c.deps_dev.as_slice(),
15937                "Caixa::deps_dev must element-equal the raw \
15938                 `self.deps_dev.as_slice()` field access across every \
15939                 value in the Vec<Dep> accept-set",
15940            );
15941        }
15942    }
15943
15944    #[test]
15945    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
15946        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
15947        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
15948        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
15949        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
15950        // Dep::simple("d", "^0.2")], .. }` must surface the
15951        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
15952        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
15953        // canonical single-entry form) must pass validate. The pair
15954        // jointly pins the accessor + validate-gate composition: any
15955        // future silent detour that had the accessor return a dedupped
15956        // slice on the `[a, a]` arm (a
15957        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15958        // would silently absorb the `DuplicateNome` refusal at the
15959        // accessor boundary and the validate gate would accept a
15960        // struct-literal `Caixa` carrying the drift — the composition
15961        // pin catches that at caixa-core build time.
15962        //
15963        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
15964        // (ad34b4e) on the sibling `:deps` axis — same "the validate
15965        // gate must route through the substrate-primitive typed
15966        // dispatch" discipline folded onto the sibling `:deps-dev`
15967        // axis, closing the two-list dep-graph composition-pin family.
15968        // The `:deps-dev` diagnostic must carry the
15969        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
15970        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
15971        // offending list unambiguously.
15972        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15973        let err = c.validate_deps().unwrap_err();
15974        assert!(
15975            matches!(
15976                err,
15977                DepError::DuplicateNome { ref nome, list } if nome == "d"
15978                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
15979            ),
15980            "validate_deps must reject deps_dev == \
15981             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15982             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
15983             accessor and the validate gate must route through the \
15984             same substrate-primitive typed dispatch on the :deps-dev \
15985             within-list duplicate arm (got {err:?})",
15986        );
15987        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
15988        assert!(
15989            c.validate_deps().is_ok(),
15990            "validate_deps must accept deps_dev == \
15991             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
15992        );
15993    }
15994
15995    #[test]
15996    fn deps_dev_projects_slice_by_borrow() {
15997        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
15998        // borrow — the returned slice borrows the underlying `Vec<Dep>`
15999        // storage of the `:deps-dev` slot and the accessor must not
16000        // clone the backing `Vec` on every call. Peer of
16001        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
16002        // `:deps` axis, and of the per-`Caixa`
16003        // `autores_projects_slice_by_borrow` (b5d813f),
16004        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
16005        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
16006        // `exe_projects_slice_by_borrow` (65d9527), and
16007        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
16008        // on the sibling outer top-level [`Caixa`] `&[String]`-return
16009        // axes — the accessor's returned slice must borrow from `&self`
16010        // (the returned reference's lifetime is tied to `&self`), and
16011        // calling the accessor twice on the same [`Caixa`] must yield
16012        // slices that are pointer-equal (the underlying byte-buffer is
16013        // the storage `Vec`'s allocation, not a fresh copy) as well as
16014        // value-equal (idempotent, no side effects on `&self`).
16015        //
16016        // Pins against a future silent detour that returned an owned
16017        // `Vec<Dep>` (which would type-check but silently clone on
16018        // every call), a `&Vec<Dep>` return (which would leak the
16019        // backing `Vec`'s grow/push/reserve surface no downstream
16020        // consumer reaches for), or a one-arm-only accessor that
16021        // returned a saturating value on some sentinel input.
16022        for deps_dev in [
16023            vec![],
16024            vec![Dep::simple("tatara-check", "^0.1")],
16025            vec![
16026                Dep::simple("tatara-check", "^0.1"),
16027                Dep::simple("caixa-lint", "^0.1"),
16028            ],
16029        ] {
16030            let c = caixa_with_deps_dev(deps_dev.clone());
16031            let first = c.deps_dev();
16032            let second = c.deps_dev();
16033            assert_eq!(
16034                first, second,
16035                "Caixa::deps_dev must be idempotent — two successive \
16036                 calls on the same &self must return the same &[Dep]",
16037            );
16038            assert_eq!(
16039                first.as_ptr(),
16040                second.as_ptr(),
16041                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
16042                 storage — two successive calls must return slices \
16043                 with the same backing pointer (a fresh Vec<Dep> clone \
16044                 would change the pointer on every call)",
16045            );
16046            assert_eq!(
16047                first,
16048                deps_dev.as_slice(),
16049                "Caixa::deps_dev must return :deps-dev verbatim by \
16050                 borrow — got {first:?}, expected {deps_dev:?}",
16051            );
16052        }
16053    }
16054
16055    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
16056
16057    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
16058        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16059        c.limits = limits;
16060        c
16061    }
16062
16063    #[test]
16064    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
16065        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
16066        // composite optional-composite-reference-shape pin:
16067        // [`Caixa::limits`] must return the `:limits` typed
16068        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
16069        // reference over the same backing storage the raw
16070        // `self.limits.as_ref()` field access borrows from, byte-equal
16071        // across every representative fixture in the accept-set — the
16072        // author-omitted `None` shape (the "engine-default applies"
16073        // partition every downstream Servico M2 overlay emitter treats
16074        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
16075        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
16076        // per-axis cap is `None`, so the peer M2 overlay emitter's
16077        // `.is_empty()`-gated projection still emits nothing but the
16078        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
16079        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
16080        // fixture (only `:memory` set — the canonical shape most
16081        // memory-heavy Servicos carry), and a fully-populated composite
16082        // (every per-axis cap set — the canonical shape a
16083        // sandboxed-by-default Servico carries).
16084        //
16085        // Pins against a future silent detour that returned a fresh-
16086        // cloned [`LimitsSpec`] copy (which would type-check via the
16087        // `Clone` impl but silently break every downstream caller that
16088        // relied on the reference sharing the composite's backing
16089        // identity), a reference to an operator-resolved overlay (the
16090        // future per-cluster `:limits-overrides` slot — its resolution
16091        // must land at exactly this accessor body, not silently divert
16092        // the raw slot away from a second consumer), a
16093        // `None` → `Some(LimitsSpec::default)` cluster-default
16094        // projection (which would collapse the load-bearing
16095        // "author-omitted `:limits` ⇒ engine-default applies" partition
16096        // the peer [`crate::render::servico_m2_overlay`] emitter and
16097        // the peer [`Caixa::declared_servico_slots`] enumerator both
16098        // read), or an axis-shuffled projection (a future detour that
16099        // swapped `memory` and `fuel` through the accessor would
16100        // silently split the paired [`crate::StandardLayout::verify`]
16101        // per-`:limits` shape gate's traversal input from the peer
16102        // `servico_m2_overlay` emitter's projection input).
16103        //
16104        // First outer top-level [`Caixa`] `Option<&Composite>`-return
16105        // composite-reference accessor pin on the substrate primitive
16106        // — opens the outer-`Caixa` `Option<&Composite>` composite-
16107        // reference projection pattern the sibling `:behavior`
16108        // [`crate::BehaviorSpec`] / `:politicas`
16109        // [`crate::aplicacao::MeshPolicy`] / `:placement`
16110        // [`crate::aplicacao::Placement`] / `:entrada`
16111        // [`crate::aplicacao::Entrada`] future outer-composite lifts
16112        // fold on. Peer of the closed M3 outer-composite family the
16113        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
16114        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
16115        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
16116        // reference accessor pins already carry on the outer
16117        // [`crate::AplicacaoSpec`] altitude — extends the outer-
16118        // accessor byte-equal-projection discipline onto the outer
16119        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
16120        use crate::LimitsSpec;
16121        use std::time::Duration;
16122        let fixtures: Vec<Option<LimitsSpec>> = vec![
16123            None,
16124            Some(LimitsSpec::default()),
16125            Some(LimitsSpec {
16126                memory: Some(64 * 1024 * 1024),
16127                ..Default::default()
16128            }),
16129            Some(LimitsSpec {
16130                memory: Some(64 * 1024 * 1024),
16131                fuel: Some(1_000_000),
16132                wall_clock: Some(Duration::from_secs(30)),
16133                cpu: Some(500),
16134            }),
16135        ];
16136        for limits in fixtures {
16137            let c = caixa_with_limits(limits.clone());
16138            assert_eq!(
16139                c.limits(),
16140                limits.as_ref(),
16141                "Caixa::limits must return :limits verbatim (got {:?}, \
16142                 expected {:?})",
16143                c.limits(),
16144                limits.as_ref(),
16145            );
16146            match (c.limits(), c.limits.as_ref()) {
16147                (Some(a), Some(b)) => assert!(
16148                    std::ptr::eq(a, b),
16149                    "Caixa::limits accessor and self.limits.as_ref() \
16150                     field access must borrow the same backing storage \
16151                     — the accessor is the substrate-primitive typed \
16152                     dispatch every downstream Servico-M2-overlay \
16153                     composite consumer must route through, and a \
16154                     reference-identity split would silently break \
16155                     every consumer that relied on the borrow sharing \
16156                     the composite's storage",
16157                ),
16158                (None, None) => {}
16159                _ => panic!(
16160                    "Caixa::limits presence bit must byte-equal \
16161                     self.limits.is_some() — a presence-bit drift would \
16162                     silently split the paired StandardLayout::verify \
16163                     per-`:limits` shape gate's traversal head from \
16164                     the peer render::servico_m2_overlay M2 overlay \
16165                     emitter's traversal head from the peer \
16166                     Caixa::declared_servico_slots M2 declared-slot \
16167                     enumerator's presence probe",
16168                ),
16169            }
16170            assert_eq!(
16171                c.limits().is_some(),
16172                c.limits.is_some(),
16173                "Caixa::limits().is_some() must byte-equal \
16174                 self.limits.is_some() — a presence-bit drift would \
16175                 silently split every downstream Option<&LimitsSpec> \
16176                 consumer's partition on the engine-default arm",
16177            );
16178        }
16179    }
16180
16181    #[test]
16182    fn declared_servico_slots_limits_arm_routes_through_accessor() {
16183        // Composition pin: [`Caixa::declared_servico_slots`]'s
16184        // `:limits` presence-probe arm must key off [`Caixa::limits`],
16185        // not the raw `self.limits.is_some()` field-probe. Structurally:
16186        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
16187        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
16188        // (the presence bit is `Some`, so the M2 kind-coherence gate
16189        // must surface the slot as "declared" even when every per-axis
16190        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
16191        // push the label (the "author omitted the slot entirely"
16192        // partition). The pair jointly pins the accessor + declared-
16193        // slot enumerator composition: any future silent detour that
16194        // had the accessor collapse `Some(LimitsSpec::default())` to
16195        // `None` (a `.filter(|l| !l.is_empty())` projection) would
16196        // silently absorb the "declared but empty" arm at the
16197        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
16198        // kind-coherence gate would silently accept a
16199        // struct-literal `Caixa` carrying the drift.
16200        //
16201        // Peer of the sibling per-`Caixa`
16202        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
16203        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
16204        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
16205        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
16206        // enumerator gate must route through the substrate-primitive
16207        // typed dispatch" discipline extended onto the outer top-level
16208        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
16209        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
16210        // composition-pin family.
16211        use crate::LimitsSpec;
16212        let c = caixa_with_limits(Some(LimitsSpec::default()));
16213        let slots = c.declared_servico_slots();
16214        assert!(
16215            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
16216            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
16217             when `:limits` is Some (even for LimitsSpec::default()) \
16218             — the accessor and the enumerator gate must route through \
16219             the same substrate-primitive typed dispatch on the outer \
16220             :limits presence bit (got slots={slots:?})",
16221        );
16222        let c = caixa_with_limits(None);
16223        let slots = c.declared_servico_slots();
16224        assert!(
16225            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
16226            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
16227             when `:limits` is None — the author-omitted arm must \
16228             route through the accessor's None-return unchanged (got \
16229             slots={slots:?})",
16230        );
16231    }
16232
16233    #[test]
16234    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
16235        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16236        // per-`:limits` M2 overlay emit arm must key off
16237        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
16238        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
16239        // Some(64 MiB), .. default }), .. }` must surface the
16240        // `M2_KEY_LIMITS` key with the per-axis
16241        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
16242        // limits: Some(LimitsSpec::default()), .. }` must omit the
16243        // key entirely (the `.is_empty()`-gated inner arm elides an
16244        // empty composite even when the outer presence bit is `Some`),
16245        // and a `Caixa { limits: None, .. }` must also omit the key
16246        // (the "author omitted the slot entirely" partition). The
16247        // three-fixture family jointly pins the accessor + M2 overlay
16248        // emitter composition: any future silent detour that had the
16249        // accessor return a fresh-cloned copy on the `Some` arm (a
16250        // `LimitsSpec::clone()` projection) would silently break the
16251        // reference-identity pin the peer per-axis
16252        // `serde_yaml::to_value(limits)` projection reads from.
16253        use crate::LimitsSpec;
16254        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
16255        let c = caixa_with_limits(Some(LimitsSpec {
16256            memory: Some(64 * 1024 * 1024),
16257            ..Default::default()
16258        }));
16259        let overlay = servico_m2_overlay(&c).unwrap();
16260        assert!(
16261            overlay.contains_key(M2_KEY_LIMITS),
16262            "servico_m2_overlay must surface M2_KEY_LIMITS when \
16263             `:limits` carries a non-empty composite — the accessor \
16264             and the M2 overlay emitter must route through the same \
16265             substrate-primitive typed dispatch on the outer :limits \
16266             composite (got overlay={overlay:?})",
16267        );
16268        let c = caixa_with_limits(Some(LimitsSpec::default()));
16269        let overlay = servico_m2_overlay(&c).unwrap();
16270        assert!(
16271            !overlay.contains_key(M2_KEY_LIMITS),
16272            "servico_m2_overlay must omit M2_KEY_LIMITS when \
16273             `:limits` is Some(LimitsSpec::default()) — the empty \
16274             composite's `.is_empty()`-gated inner arm must elide \
16275             the key regardless of the outer presence bit (got \
16276             overlay={overlay:?})",
16277        );
16278        let c = caixa_with_limits(None);
16279        let overlay = servico_m2_overlay(&c).unwrap();
16280        assert!(
16281            !overlay.contains_key(M2_KEY_LIMITS),
16282            "servico_m2_overlay must omit M2_KEY_LIMITS when \
16283             `:limits` is None — the author-omitted arm must route \
16284             through the accessor's None-return unchanged (got \
16285             overlay={overlay:?})",
16286        );
16287    }
16288
16289    #[test]
16290    fn limits_projects_option_ref_by_borrow() {
16291        // The by-borrow pin: [`Caixa::limits`] returns
16292        // `Option<&LimitsSpec>` by borrow — the returned reference
16293        // borrows the underlying `Option<LimitsSpec>` storage of the
16294        // `:limits` slot and the accessor must not clone the backing
16295        // composite on every call. Peer of the sibling
16296        // `deps_projects_slice_by_borrow` (ad34b4e) /
16297        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
16298        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
16299        // extended here to the outer [`Caixa`] `Option<&Composite>`-
16300        // return axis: the accessor's returned reference must borrow
16301        // from `&self` (the returned reference's lifetime is tied to
16302        // `&self`), and calling the accessor twice on the same
16303        // [`Caixa`] must yield references that are pointer-equal (the
16304        // underlying byte-buffer is the storage `LimitsSpec`'s
16305        // allocation, not a fresh copy) as well as value-equal
16306        // (idempotent, no side effects on `&self`).
16307        //
16308        // Pins against a future silent detour that returned an owned
16309        // `LimitsSpec` (which would type-check via the `Clone` impl
16310        // but silently clone on every call), a `&LimitsSpec` panic-
16311        // return on the `None` arm (which would collapse the load-
16312        // bearing `Option` presence-bit into a runtime panic), or a
16313        // one-arm-only accessor that returned a saturating composite
16314        // on some sentinel input.
16315        use crate::LimitsSpec;
16316        use std::time::Duration;
16317        for limits in [
16318            Some(LimitsSpec::default()),
16319            Some(LimitsSpec {
16320                memory: Some(64 * 1024 * 1024),
16321                fuel: Some(1_000_000),
16322                wall_clock: Some(Duration::from_secs(30)),
16323                cpu: Some(500),
16324            }),
16325        ] {
16326            let c = caixa_with_limits(limits.clone());
16327            let first = c.limits().unwrap();
16328            let second = c.limits().unwrap();
16329            assert_eq!(
16330                first, second,
16331                "Caixa::limits must be idempotent — two successive \
16332                 calls on the same &self must return the same \
16333                 &LimitsSpec",
16334            );
16335            assert!(
16336                std::ptr::eq(first, second),
16337                "Caixa::limits must borrow the underlying \
16338                 Option<LimitsSpec> storage — two successive calls \
16339                 must return references with the same backing pointer \
16340                 (a fresh LimitsSpec clone would change the pointer \
16341                 on every call)",
16342            );
16343            assert_eq!(
16344                Some(first),
16345                limits.as_ref(),
16346                "Caixa::limits must return :limits verbatim by borrow \
16347                 — got {first:?}, expected {:?}",
16348                limits.as_ref(),
16349            );
16350        }
16351        let c = caixa_with_limits(None);
16352        assert!(
16353            c.limits().is_none(),
16354            "Caixa::limits must return None when :limits is absent — \
16355             the author-omitted arm must project through the \
16356             accessor's Option::None unchanged",
16357        );
16358    }
16359
16360    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
16361
16362    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
16363        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16364        c.behavior = behavior;
16365        c
16366    }
16367
16368    #[test]
16369    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
16370        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
16371        // composite optional-composite-reference-shape pin:
16372        // [`Caixa::behavior`] must return the `:behavior` typed
16373        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
16374        // reference over the same backing storage the raw
16375        // `self.behavior.as_ref()` field access borrows from, byte-equal
16376        // across every representative fixture in the accept-set — the
16377        // author-omitted `None` shape (the "runtime-default applies"
16378        // partition every downstream Servico M2 overlay emitter treats
16379        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
16380        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
16381        // every per-callback path is `None`, so the peer M2 overlay
16382        // emitter's `.is_empty()`-gated projection still emits nothing
16383        // but the outer presence-bit is `Some`, so
16384        // [`Caixa::declared_servico_slots`] still pushes the
16385        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
16386        // (only `:on-state-change` set — the canonical shape a caixa
16387        // that only wires the hot-upgrade migration path carries), and
16388        // a fully-populated composite (every per-callback path set —
16389        // the canonical shape a fully-instrumented gen_server-shaped
16390        // Servico carries).
16391        //
16392        // Peer of the sibling
16393        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16394        // (b2bd9d7) opening fixture-family + reference-identity +
16395        // presence-bit tetrad pin on the outer top-level [`Caixa`]
16396        // `Option<&Composite>`-return sub-family — extended here to the
16397        // second axis of that sub-family so both of the currently-lifted
16398        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
16399        // `:behavior`) carry the same "byte-equal, borrow-shared,
16400        // presence-bit-preserved" outer-accessor discipline.
16401        //
16402        // Pins against a future silent detour that returned a fresh-
16403        // cloned [`crate::BehaviorSpec`] copy (which would type-check
16404        // via the `Clone` impl but silently break every downstream
16405        // caller that relied on the reference sharing the composite's
16406        // backing identity), a reference to an operator-resolved
16407        // overlay (a future per-cluster `:behavior-overrides` slot —
16408        // its resolution must land at exactly this accessor body, not
16409        // silently divert the raw slot away from a second consumer), a
16410        // `None` → `Some(BehaviorSpec::default)` cluster-default
16411        // projection (which would collapse the load-bearing
16412        // "author-omitted `:behavior` ⇒ runtime-default applies"
16413        // partition the peer [`crate::render::servico_m2_overlay`]
16414        // emitter, the peer [`Caixa::declared_servico_slots`]
16415        // enumerator, and the cross-slot
16416        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
16417        // gate all read), or a callback-shuffled projection (a future
16418        // detour that swapped `on_init` and `on_terminate` through the
16419        // accessor would silently split the paired
16420        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
16421        // traversal input from the peer `servico_m2_overlay` emitter's
16422        // projection input from the cross-slot `:state-change`
16423        // composition gate's traversal input).
16424        use crate::BehaviorSpec;
16425        use std::path::PathBuf;
16426        let fixtures: Vec<Option<BehaviorSpec>> = vec![
16427            None,
16428            Some(BehaviorSpec::default()),
16429            Some(BehaviorSpec {
16430                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16431                ..Default::default()
16432            }),
16433            Some(BehaviorSpec {
16434                on_init: Some(PathBuf::from("lib/init.lisp")),
16435                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16436                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16437                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16438                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16439                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16440            }),
16441        ];
16442        for behavior in fixtures {
16443            let c = caixa_with_behavior(behavior.clone());
16444            assert_eq!(
16445                c.behavior(),
16446                behavior.as_ref(),
16447                "Caixa::behavior must return :behavior verbatim (got \
16448                 {:?}, expected {:?})",
16449                c.behavior(),
16450                behavior.as_ref(),
16451            );
16452            match (c.behavior(), c.behavior.as_ref()) {
16453                (Some(a), Some(b)) => assert!(
16454                    std::ptr::eq(a, b),
16455                    "Caixa::behavior accessor and self.behavior.as_ref() \
16456                     field access must borrow the same backing storage \
16457                     — the accessor is the substrate-primitive typed \
16458                     dispatch every downstream Servico-M2-overlay \
16459                     composite consumer must route through, and a \
16460                     reference-identity split would silently break \
16461                     every consumer that relied on the borrow sharing \
16462                     the composite's storage",
16463                ),
16464                (None, None) => {}
16465                _ => panic!(
16466                    "Caixa::behavior presence bit must byte-equal \
16467                     self.behavior.is_some() — a presence-bit drift \
16468                     would silently split the paired \
16469                     StandardLayout::verify per-`:behavior` shape \
16470                     gate's traversal head from the peer \
16471                     render::servico_m2_overlay M2 overlay emitter's \
16472                     traversal head from the cross-slot \
16473                     validate_upgrade_from_against_behavior \
16474                     composition gate's traversal head from the peer \
16475                     Caixa::declared_servico_slots M2 declared-slot \
16476                     enumerator's presence probe",
16477                ),
16478            }
16479            assert_eq!(
16480                c.behavior().is_some(),
16481                c.behavior.is_some(),
16482                "Caixa::behavior().is_some() must byte-equal \
16483                 self.behavior.is_some() — a presence-bit drift would \
16484                 silently split every downstream Option<&BehaviorSpec> \
16485                 consumer's partition on the runtime-default arm",
16486            );
16487        }
16488    }
16489
16490    #[test]
16491    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
16492        // Composition pin: [`Caixa::declared_servico_slots`]'s
16493        // `:behavior` presence-probe arm must key off
16494        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
16495        // field-probe. Structurally: a `Caixa { behavior:
16496        // Some(BehaviorSpec::default()), .. }` must still push
16497        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
16498        // presence bit is `Some`, so the M2 kind-coherence gate must
16499        // surface the slot as "declared" even when every per-callback
16500        // path is unset), and a `Caixa { behavior: None, .. }` must
16501        // NOT push the label (the "author omitted the slot entirely"
16502        // partition). The pair jointly pins the accessor + declared-
16503        // slot enumerator composition: any future silent detour that
16504        // had the accessor collapse `Some(BehaviorSpec::default())`
16505        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
16506        // silently absorb the "declared but empty" arm at the
16507        // accessor boundary and the
16508        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
16509        // kind-coherence gate would silently accept a struct-literal
16510        // `Caixa` carrying the drift.
16511        //
16512        // Peer of the sibling
16513        // `declared_servico_slots_limits_arm_routes_through_accessor`
16514        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16515        // `Option<&LimitsSpec>` arm of the same
16516        // [`Caixa::declared_servico_slots`] M2 declared-slot
16517        // enumerator's traversal — same "the enumerator gate must
16518        // route through the substrate-primitive typed dispatch"
16519        // discipline extended onto the outer top-level [`Caixa`]
16520        // `Option<&BehaviorSpec>`-composition surface.
16521        use crate::BehaviorSpec;
16522        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16523        let slots = c.declared_servico_slots();
16524        assert!(
16525            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16526            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
16527             when `:behavior` is Some (even for BehaviorSpec::default()) \
16528             — the accessor and the enumerator gate must route through \
16529             the same substrate-primitive typed dispatch on the outer \
16530             :behavior presence bit (got slots={slots:?})",
16531        );
16532        let c = caixa_with_behavior(None);
16533        let slots = c.declared_servico_slots();
16534        assert!(
16535            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16536            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
16537             when `:behavior` is None — the author-omitted arm must \
16538             route through the accessor's None-return unchanged (got \
16539             slots={slots:?})",
16540        );
16541    }
16542
16543    #[test]
16544    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
16545        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16546        // per-`:behavior` M2 overlay emit arm must key off
16547        // [`Caixa::behavior`], not the raw `&caixa.behavior`
16548        // field-borrow. Structurally: a `Caixa { behavior:
16549        // Some(BehaviorSpec { on_state_change: Some(...), .. default
16550        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
16551        // per-callback `onStateChange` sub-mapping in the overlay, a
16552        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
16553        // must omit the key entirely (the `.is_empty()`-gated inner
16554        // arm elides an empty composite even when the outer presence
16555        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
16556        // also omit the key (the "author omitted the slot entirely"
16557        // partition). The three-fixture family jointly pins the
16558        // accessor + M2 overlay emitter composition: any future
16559        // silent detour that had the accessor return a fresh-cloned
16560        // copy on the `Some` arm (a `BehaviorSpec::clone()`
16561        // projection) would silently break the reference-identity
16562        // pin the peer per-callback `serde_yaml::to_value(behavior)`
16563        // projection reads from.
16564        //
16565        // Peer of the sibling
16566        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16567        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16568        // `Option<&LimitsSpec>` arm of the same
16569        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
16570        // traversal — same "the emitter must route through the
16571        // substrate-primitive typed dispatch on the outer composite"
16572        // discipline extended onto the outer top-level [`Caixa`]
16573        // `Option<&BehaviorSpec>`-composition surface.
16574        use crate::BehaviorSpec;
16575        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
16576        use std::path::PathBuf;
16577        let c = caixa_with_behavior(Some(BehaviorSpec {
16578            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16579            ..Default::default()
16580        }));
16581        let overlay = servico_m2_overlay(&c).unwrap();
16582        assert!(
16583            overlay.contains_key(M2_KEY_BEHAVIOR),
16584            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
16585             `:behavior` carries a non-empty composite — the accessor \
16586             and the M2 overlay emitter must route through the same \
16587             substrate-primitive typed dispatch on the outer :behavior \
16588             composite (got overlay={overlay:?})",
16589        );
16590        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16591        let overlay = servico_m2_overlay(&c).unwrap();
16592        assert!(
16593            !overlay.contains_key(M2_KEY_BEHAVIOR),
16594            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16595             `:behavior` is Some(BehaviorSpec::default()) — the empty \
16596             composite's `.is_empty()`-gated inner arm must elide the \
16597             key regardless of the outer presence bit (got \
16598             overlay={overlay:?})",
16599        );
16600        let c = caixa_with_behavior(None);
16601        let overlay = servico_m2_overlay(&c).unwrap();
16602        assert!(
16603            !overlay.contains_key(M2_KEY_BEHAVIOR),
16604            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16605             `:behavior` is None — the author-omitted arm must route \
16606             through the accessor's None-return unchanged (got \
16607             overlay={overlay:?})",
16608        );
16609    }
16610
16611    #[test]
16612    fn behavior_projects_option_ref_by_borrow() {
16613        // The by-borrow pin: [`Caixa::behavior`] returns
16614        // `Option<&BehaviorSpec>` by borrow — the returned reference
16615        // borrows the underlying `Option<BehaviorSpec>` storage of the
16616        // `:behavior` slot and the accessor must not clone the backing
16617        // composite on every call. Peer of the sibling
16618        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
16619        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
16620        // return sub-family — extended here to the second axis of the
16621        // same sub-family: the accessor's returned reference must
16622        // borrow from `&self` (the returned reference's lifetime is
16623        // tied to `&self`), and calling the accessor twice on the same
16624        // [`Caixa`] must yield references that are pointer-equal (the
16625        // underlying byte-buffer is the storage `BehaviorSpec`'s
16626        // allocation, not a fresh copy) as well as value-equal
16627        // (idempotent, no side effects on `&self`).
16628        //
16629        // Pins against a future silent detour that returned an owned
16630        // `BehaviorSpec` (which would type-check via the `Clone` impl
16631        // but silently clone on every call), a `&BehaviorSpec` panic-
16632        // return on the `None` arm (which would collapse the load-
16633        // bearing `Option` presence-bit into a runtime panic), or a
16634        // one-arm-only accessor that returned a saturating composite
16635        // on some sentinel input.
16636        use crate::BehaviorSpec;
16637        use std::path::PathBuf;
16638        for behavior in [
16639            Some(BehaviorSpec::default()),
16640            Some(BehaviorSpec {
16641                on_init: Some(PathBuf::from("lib/init.lisp")),
16642                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16643                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16644                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16645                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16646                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16647            }),
16648        ] {
16649            let c = caixa_with_behavior(behavior.clone());
16650            let first = c.behavior().unwrap();
16651            let second = c.behavior().unwrap();
16652            assert_eq!(
16653                first, second,
16654                "Caixa::behavior must be idempotent — two successive \
16655                 calls on the same &self must return the same \
16656                 &BehaviorSpec",
16657            );
16658            assert!(
16659                std::ptr::eq(first, second),
16660                "Caixa::behavior must borrow the underlying \
16661                 Option<BehaviorSpec> storage — two successive calls \
16662                 must return references with the same backing pointer \
16663                 (a fresh BehaviorSpec clone would change the pointer \
16664                 on every call)",
16665            );
16666            assert_eq!(
16667                Some(first),
16668                behavior.as_ref(),
16669                "Caixa::behavior must return :behavior verbatim by \
16670                 borrow — got {first:?}, expected {:?}",
16671                behavior.as_ref(),
16672            );
16673        }
16674        let c = caixa_with_behavior(None);
16675        assert!(
16676            c.behavior().is_none(),
16677            "Caixa::behavior must return None when :behavior is absent \
16678             — the author-omitted arm must project through the \
16679             accessor's Option::None unchanged",
16680        );
16681    }
16682
16683    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
16684
16685    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
16686        use crate::aplicacao::{Membro, WitContract};
16687        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16688        c.kind = CaixaKind::Aplicacao;
16689        c.membros = vec![Membro {
16690            caixa: "a".into(),
16691            versao: "^0.1".into(),
16692        }];
16693        c.contratos = vec![WitContract {
16694            de: "a".into(),
16695            para: "a".into(),
16696            wit: "wasi:http/proxy".into(),
16697            endpoint: Some("/x".into()),
16698            subject: None,
16699            slot: None,
16700        }];
16701        c.politicas = politicas;
16702        c
16703    }
16704
16705    #[test]
16706    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
16707        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
16708        // composite optional-composite-reference-shape pin:
16709        // [`Caixa::politicas`] must return the `:politicas` typed
16710        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
16711        // reference over the same backing storage the raw
16712        // `self.politicas.as_ref()` field access borrows from,
16713        // byte-equal across every representative fixture in the
16714        // accept-set — the author-omitted `None` shape (the "cluster-
16715        // default applies" partition every downstream mesh-artifact
16716        // emitter treats as "emit no `:politicas` overlay"), the
16717        // empty-composite `Some(MeshPolicy { .. default })` shape
16718        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
16719        // per-axis mesh-policy scalar is `None`, so the peer inner
16720        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
16721        // caixa-mesh overlay elides every per-axis emit but the outer
16722        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
16723        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
16724        // single-axis fixture (only `:timeout` set — the canonical
16725        // shape a latency-sensitive Aplicacao carries), and a
16726        // fully-populated composite (every per-axis mesh-policy
16727        // scalar set — the canonical shape a fully-governed
16728        // Aplicacao carries).
16729        //
16730        // Pins against a future silent detour that returned a fresh-
16731        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
16732        // type-check via the `Clone` impl but silently break every
16733        // downstream caller that relied on the reference sharing the
16734        // composite's backing identity), a reference to an operator-
16735        // resolved overlay (the future per-cluster
16736        // `:politicas-overrides` slot — its resolution must land at
16737        // exactly this accessor body, not silently divert the raw
16738        // slot away from the peer [`Caixa::declared_mesh_slots`]
16739        // enumerator's presence probe), a
16740        // `None` → `Some(MeshPolicy::default)` cluster-default
16741        // projection (which would collapse the load-bearing
16742        // "author-omitted `:politicas` ⇒ cluster-default applies"
16743        // partition the peer [`Caixa::declared_mesh_slots`]
16744        // enumerator and the peer [`Caixa::aplicacao_view`]
16745        // Aplicacao-composition seed both read), or an axis-shuffled
16746        // projection (a future detour that swapped `timeout` and
16747        // `retries` through the accessor would silently split the
16748        // paired [`Caixa::aplicacao_view`] seed's fold input from the
16749        // sibling M3 mesh-artifact emitter's projection input).
16750        //
16751        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
16752        // composite-reference accessor pin on the substrate primitive
16753        // — peer of the sibling
16754        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16755        // (b2bd9d7) and
16756        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16757        // (35d8b52) opening tetrad pins on the outer top-level
16758        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16759        // here to the first of the three M3 mesh-slot axes so the
16760        // opening third of the outer `Option<&Composite>` sub-family
16761        // carries the same "byte-equal, borrow-shared, presence-bit-
16762        // preserved" outer-accessor discipline.
16763        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16764        use std::time::Duration;
16765        let fixtures: Vec<Option<MeshPolicy>> = vec![
16766            None,
16767            Some(MeshPolicy::default()),
16768            Some(MeshPolicy {
16769                timeout: Some(Duration::from_secs(30)),
16770                ..Default::default()
16771            }),
16772            Some(MeshPolicy {
16773                timeout: Some(Duration::from_secs(30)),
16774                retries: Some(3),
16775                circuit_breaker: Some(CircuitBreaker {
16776                    max_failures: 5,
16777                    window: Duration::from_secs(60),
16778                }),
16779                mtls_required: Some(true),
16780                rate_limit: Some(RateLimit {
16781                    rate: 100,
16782                    window: Duration::from_secs(1),
16783                }),
16784            }),
16785        ];
16786        for politicas in fixtures {
16787            let c = caixa_aplicacao_with_politicas(politicas.clone());
16788            assert_eq!(
16789                c.politicas(),
16790                politicas.as_ref(),
16791                "Caixa::politicas must return :politicas verbatim (got \
16792                 {:?}, expected {:?})",
16793                c.politicas(),
16794                politicas.as_ref(),
16795            );
16796            match (c.politicas(), c.politicas.as_ref()) {
16797                (Some(a), Some(b)) => assert!(
16798                    std::ptr::eq(a, b),
16799                    "Caixa::politicas accessor and self.politicas.as_ref() \
16800                     field access must borrow the same backing storage \
16801                     — the accessor is the substrate-primitive typed \
16802                     dispatch every downstream Aplicacao-mesh-overlay \
16803                     composite consumer must route through, and a \
16804                     reference-identity split would silently break \
16805                     every consumer that relied on the borrow sharing \
16806                     the composite's storage",
16807                ),
16808                (None, None) => {}
16809                _ => panic!(
16810                    "Caixa::politicas presence bit must byte-equal \
16811                     self.politicas.is_some() — a presence-bit drift \
16812                     would silently split the paired \
16813                     Caixa::aplicacao_view Aplicacao-composition seed's \
16814                     traversal head from the peer \
16815                     Caixa::declared_mesh_slots M3 declared-slot \
16816                     enumerator's presence probe",
16817                ),
16818            }
16819            assert_eq!(
16820                c.politicas().is_some(),
16821                c.politicas.is_some(),
16822                "Caixa::politicas().is_some() must byte-equal \
16823                 self.politicas.is_some() — a presence-bit drift would \
16824                 silently split every downstream Option<&MeshPolicy> \
16825                 consumer's partition on the cluster-default arm",
16826            );
16827        }
16828    }
16829
16830    #[test]
16831    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
16832        // Composition pin: [`Caixa::declared_mesh_slots`]'s
16833        // `:politicas` presence-probe arm must key off
16834        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
16835        // field-probe. Structurally: a `Caixa { politicas:
16836        // Some(MeshPolicy::default()), .. }` must still push
16837        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
16838        // presence bit is `Some`, so the M3 kind-coherence gate must
16839        // surface the slot as "declared" even when every per-axis
16840        // scalar is unset), and a `Caixa { politicas: None, .. }` must
16841        // NOT push the label (the "author omitted the slot entirely"
16842        // partition). The pair jointly pins the accessor + declared-
16843        // slot enumerator composition: any future silent detour that
16844        // had the accessor collapse `Some(MeshPolicy::default())` to
16845        // `None` (a `.filter(|p| !p.is_empty())` projection) would
16846        // silently absorb the "declared but empty" arm at the
16847        // accessor boundary and the
16848        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16849        // coherence gate would silently accept a struct-literal
16850        // `Caixa` carrying the drift.
16851        //
16852        // Peer of the sibling
16853        // `declared_servico_slots_limits_arm_routes_through_accessor`
16854        // (b2bd9d7) and
16855        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16856        // (35d8b52) composition pins on the sibling `:limits` /
16857        // `:behavior` outer-`Option<&Composite>` arms of the peer
16858        // [`Caixa::declared_servico_slots`] M2 declared-slot
16859        // enumerator's traversal — same "the enumerator gate must
16860        // route through the substrate-primitive typed dispatch"
16861        // discipline extended onto the outer top-level [`Caixa`] M3
16862        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
16863        // enumerator carries the same routing invariant as its M2
16864        // sibling.
16865        use crate::aplicacao::MeshPolicy;
16866        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16867        let slots = c.declared_mesh_slots();
16868        assert!(
16869            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16870            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
16871             when `:politicas` is Some (even for MeshPolicy::default()) \
16872             — the accessor and the enumerator gate must route through \
16873             the same substrate-primitive typed dispatch on the outer \
16874             :politicas presence bit (got slots={slots:?})",
16875        );
16876        let c = caixa_aplicacao_with_politicas(None);
16877        let slots = c.declared_mesh_slots();
16878        assert!(
16879            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16880            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
16881             when `:politicas` is None — the author-omitted arm must \
16882             route through the accessor's None-return unchanged (got \
16883             slots={slots:?})",
16884        );
16885    }
16886
16887    #[test]
16888    fn aplicacao_view_politicas_arm_folds_through_accessor() {
16889        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
16890        // Aplicacao-composition seed must fold through
16891        // [`Caixa::politicas`], not the raw
16892        // `self.politicas.clone().unwrap_or_default()` field-borrow.
16893        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
16894        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
16895        // must surface a projected [`crate::AplicacaoSpec`] whose
16896        // `politicas().timeout()` field byte-equals the outer
16897        // composite's `timeout` scalar (the fold must project the
16898        // authored composite verbatim), a `Caixa { politicas:
16899        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
16900        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
16901        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
16902        // fold's empty-composite arm collapses to the same default the
16903        // author-omitted arm does), and a `Caixa { politicas: None,
16904        // kind: Aplicacao, .. }` must surface an
16905        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
16906        // [`crate::aplicacao::MeshPolicy::default`] (the "author
16907        // omitted the slot entirely" arm folds through the
16908        // `unwrap_or_default` onto the cluster-default). The triad
16909        // jointly pins the accessor + Aplicacao-composition seed
16910        // composition: any future silent detour that had the accessor
16911        // divert the raw slot away from the seed's fold (an operator-
16912        // resolved overlay's default-fold arm silently differing from
16913        // the raw slot's default-fold arm) would silently split the
16914        // build-time mesh-artifact emission gate from the caixa-mesh
16915        // renderer's Aplicacao-view input at the composition boundary.
16916        use crate::aplicacao::MeshPolicy;
16917        use std::time::Duration;
16918        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
16919            timeout: Some(Duration::from_secs(30)),
16920            ..Default::default()
16921        }));
16922        let view = c.aplicacao_view().unwrap();
16923        assert_eq!(
16924            view.politicas().timeout(),
16925            Some(Duration::from_secs(30)),
16926            "Caixa::aplicacao_view must fold the authored :politicas \
16927             :timeout scalar through the accessor verbatim onto the \
16928             projected AplicacaoSpec — a future silent detour at the \
16929             seed's fold arm would surface here as a projected-scalar \
16930             drift (got {:?})",
16931            view.politicas().timeout(),
16932        );
16933        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16934        let view = c.aplicacao_view().unwrap();
16935        assert_eq!(
16936            view.politicas(),
16937            &MeshPolicy::default(),
16938            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
16939             through the accessor onto MeshPolicy::default — the empty- \
16940             composite arm collapses to the same default the author- \
16941             omitted arm does (got {:?})",
16942            view.politicas(),
16943        );
16944        let c = caixa_aplicacao_with_politicas(None);
16945        let view = c.aplicacao_view().unwrap();
16946        assert_eq!(
16947            view.politicas(),
16948            &MeshPolicy::default(),
16949            "Caixa::aplicacao_view must fold None through the accessor's \
16950             unwrap_or_default onto MeshPolicy::default — the author- \
16951             omitted arm must route through the accessor's None-return \
16952             unchanged (got {:?})",
16953            view.politicas(),
16954        );
16955    }
16956
16957    #[test]
16958    fn politicas_projects_option_ref_by_borrow() {
16959        // The by-borrow pin: [`Caixa::politicas`] returns
16960        // `Option<&MeshPolicy>` by borrow — the returned reference
16961        // borrows the underlying `Option<MeshPolicy>` storage of the
16962        // `:politicas` slot and the accessor must not clone the
16963        // backing composite on every call. Peer of the sibling
16964        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
16965        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
16966        // pins on the outer top-level [`Caixa`]
16967        // `Option<&Composite>`-return sub-family — extended here to
16968        // the third axis of the same sub-family: the accessor's
16969        // returned reference must borrow from `&self` (the returned
16970        // reference's lifetime is tied to `&self`), and calling the
16971        // accessor twice on the same [`Caixa`] must yield references
16972        // that are pointer-equal (the underlying byte-buffer is the
16973        // storage `MeshPolicy`'s allocation, not a fresh copy) as
16974        // well as value-equal (idempotent, no side effects on
16975        // `&self`).
16976        //
16977        // Pins against a future silent detour that returned an owned
16978        // `MeshPolicy` (which would type-check via the `Clone` impl
16979        // but silently clone on every call), a `&MeshPolicy` panic-
16980        // return on the `None` arm (which would collapse the load-
16981        // bearing `Option` presence-bit into a runtime panic), or a
16982        // one-arm-only accessor that returned a saturating composite
16983        // on some sentinel input.
16984        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16985        use std::time::Duration;
16986        for politicas in [
16987            Some(MeshPolicy::default()),
16988            Some(MeshPolicy {
16989                timeout: Some(Duration::from_secs(30)),
16990                retries: Some(3),
16991                circuit_breaker: Some(CircuitBreaker {
16992                    max_failures: 5,
16993                    window: Duration::from_secs(60),
16994                }),
16995                mtls_required: Some(true),
16996                rate_limit: Some(RateLimit {
16997                    rate: 100,
16998                    window: Duration::from_secs(1),
16999                }),
17000            }),
17001        ] {
17002            let c = caixa_aplicacao_with_politicas(politicas.clone());
17003            let first = c.politicas().unwrap();
17004            let second = c.politicas().unwrap();
17005            assert_eq!(
17006                first, second,
17007                "Caixa::politicas must be idempotent — two successive \
17008                 calls on the same &self must return the same \
17009                 &MeshPolicy",
17010            );
17011            assert!(
17012                std::ptr::eq(first, second),
17013                "Caixa::politicas must borrow the underlying \
17014                 Option<MeshPolicy> storage — two successive calls \
17015                 must return references with the same backing pointer \
17016                 (a fresh MeshPolicy clone would change the pointer on \
17017                 every call)",
17018            );
17019            assert_eq!(
17020                Some(first),
17021                politicas.as_ref(),
17022                "Caixa::politicas must return :politicas verbatim by \
17023                 borrow — got {first:?}, expected {:?}",
17024                politicas.as_ref(),
17025            );
17026        }
17027        let c = caixa_aplicacao_with_politicas(None);
17028        assert!(
17029            c.politicas().is_none(),
17030            "Caixa::politicas must return None when :politicas is \
17031             absent — the author-omitted arm must project through the \
17032             accessor's Option::None unchanged",
17033        );
17034    }
17035
17036    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
17037
17038    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
17039        use crate::aplicacao::{Membro, WitContract};
17040        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17041        c.kind = CaixaKind::Aplicacao;
17042        c.membros = vec![Membro {
17043            caixa: "a".into(),
17044            versao: "^0.1".into(),
17045        }];
17046        c.contratos = vec![WitContract {
17047            de: "a".into(),
17048            para: "a".into(),
17049            wit: "wasi:http/proxy".into(),
17050            endpoint: Some("/x".into()),
17051            subject: None,
17052            slot: None,
17053        }];
17054        c.placement = placement;
17055        c
17056    }
17057
17058    #[test]
17059    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
17060        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
17061        // composite optional-composite-reference-shape pin:
17062        // [`Caixa::placement`] must return the `:placement` typed
17063        // `Option<Placement>` verbatim as an `Option<&Placement>`
17064        // reference over the same backing storage the raw
17065        // `self.placement.as_ref()` field access borrows from,
17066        // byte-equal across every representative fixture in the
17067        // accept-set — the author-omitted `None` shape (the
17068        // "cluster-default applies" partition every downstream mesh-
17069        // artifact emitter treats as "emit no `:placement` overlay"),
17070        // the empty-composite `Some(Placement { .. default })` shape
17071        // (`estrategia: SingleNode`, empty clusters, no shard-key /
17072        // affinity — the outer presence-bit is `Some` so
17073        // [`Caixa::declared_mesh_slots`] still pushes the
17074        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
17075        // `Replicated`-on-two-clusters fixture (the canonical shape a
17076        // stateless HTTP Aplicacao carries), and a fully-populated
17077        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
17078        // shape a stateful Akka-style cluster-sharding Aplicacao
17079        // carries).
17080        //
17081        // Pins against a future silent detour that returned a fresh-
17082        // cloned [`crate::aplicacao::Placement`] copy (which would
17083        // type-check via the `Clone` impl but silently break every
17084        // downstream caller that relied on the reference sharing the
17085        // composite's backing identity), a reference to an operator-
17086        // resolved overlay (the future per-cluster
17087        // `:placement-overrides` slot — its resolution must land at
17088        // exactly this accessor body, not silently divert the raw
17089        // slot away from the peer [`Caixa::declared_mesh_slots`]
17090        // enumerator's presence probe), a `None` →
17091        // `Some(Placement::default)` cluster-default projection (which
17092        // would collapse the load-bearing "author-omitted `:placement`
17093        // ⇒ cluster-default applies" partition the peer
17094        // [`Caixa::declared_mesh_slots`] enumerator and the peer
17095        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
17096        // read), or an axis-shuffled projection (a future detour that
17097        // swapped `clusters` and `affinity` through the accessor would
17098        // silently split the paired [`Caixa::aplicacao_view`] seed's
17099        // fold input from the sibling M3 mesh-artifact emitter's
17100        // projection input).
17101        //
17102        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
17103        // composite-reference accessor pin on the substrate primitive
17104        // — peer of the sibling
17105        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17106        // (b2bd9d7),
17107        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17108        // (35d8b52), and
17109        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17110        // (5d23d29) opening triad pins on the outer top-level
17111        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17112        // here to the second of the three M3 mesh-slot axes so the
17113        // opening four-fifths of the outer `Option<&Composite>` sub-
17114        // family carries the same "byte-equal, borrow-shared,
17115        // presence-bit-preserved" outer-accessor discipline.
17116        use crate::aplicacao::{Placement, PlacementStrategy};
17117        let fixtures: Vec<Option<Placement>> = vec![
17118            None,
17119            Some(Placement::default()),
17120            Some(Placement {
17121                estrategia: PlacementStrategy::Replicated,
17122                clusters: vec!["rio".into(), "sao-paulo".into()],
17123                affinity: None,
17124                shard_key: None,
17125            }),
17126            Some(Placement {
17127                estrategia: PlacementStrategy::Sharded,
17128                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
17129                affinity: Some("data-locality".into()),
17130                shard_key: Some("$tenantId".into()),
17131            }),
17132        ];
17133        for placement in fixtures {
17134            let c = caixa_aplicacao_with_placement(placement.clone());
17135            assert_eq!(
17136                c.placement(),
17137                placement.as_ref(),
17138                "Caixa::placement must return :placement verbatim (got \
17139                 {:?}, expected {:?})",
17140                c.placement(),
17141                placement.as_ref(),
17142            );
17143            match (c.placement(), c.placement.as_ref()) {
17144                (Some(a), Some(b)) => assert!(
17145                    std::ptr::eq(a, b),
17146                    "Caixa::placement accessor and self.placement.as_ref() \
17147                     field access must borrow the same backing storage \
17148                     — the accessor is the substrate-primitive typed \
17149                     dispatch every downstream Aplicacao-distribution- \
17150                     overlay composite consumer must route through, and \
17151                     a reference-identity split would silently break \
17152                     every consumer that relied on the borrow sharing \
17153                     the composite's storage",
17154                ),
17155                (None, None) => {}
17156                _ => panic!(
17157                    "Caixa::placement presence bit must byte-equal \
17158                     self.placement.is_some() — a presence-bit drift \
17159                     would silently split the paired \
17160                     Caixa::aplicacao_view Aplicacao-composition seed's \
17161                     traversal head from the peer \
17162                     Caixa::declared_mesh_slots M3 declared-slot \
17163                     enumerator's presence probe",
17164                ),
17165            }
17166            assert_eq!(
17167                c.placement().is_some(),
17168                c.placement.is_some(),
17169                "Caixa::placement().is_some() must byte-equal \
17170                 self.placement.is_some() — a presence-bit drift would \
17171                 silently split every downstream Option<&Placement> \
17172                 consumer's partition on the cluster-default arm",
17173            );
17174        }
17175    }
17176
17177    #[test]
17178    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
17179        // Composition pin: [`Caixa::declared_mesh_slots`]'s
17180        // `:placement` presence-probe arm must key off
17181        // [`Caixa::placement`], not the raw `self.placement.is_some()`
17182        // field-probe. Structurally: a `Caixa { placement:
17183        // Some(Placement::default()), .. }` must still push
17184        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
17185        // presence bit is `Some`, so the M3 kind-coherence gate must
17186        // surface the slot as "declared" even when every per-axis
17187        // scalar defers to the cluster-default arm), and a `Caixa {
17188        // placement: None, .. }` must NOT push the label (the "author
17189        // omitted the slot entirely" partition). The pair jointly pins
17190        // the accessor + declared-slot enumerator composition: any
17191        // future silent detour that had the accessor collapse
17192        // `Some(Placement::default())` to `None` (a `.filter(|p|
17193        // p.clusters().is_empty().not())` projection) would silently
17194        // absorb the "declared but empty" arm at the accessor boundary
17195        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
17196        // kind-coherence gate would silently accept a struct-literal
17197        // `Caixa` carrying the drift.
17198        //
17199        // Peer of the sibling
17200        // `declared_servico_slots_limits_arm_routes_through_accessor`
17201        // (b2bd9d7),
17202        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17203        // (35d8b52), and
17204        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17205        // (5d23d29) composition pins on the sibling `:limits` /
17206        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
17207        // — same "the enumerator gate must route through the
17208        // substrate-primitive typed dispatch" discipline extended onto
17209        // the second of the three M3 mesh-slot axes so the
17210        // [`Caixa::declared_mesh_slots`] enumerator carries the same
17211        // routing invariant on the `:placement` arm as the peer
17212        // `:politicas` arm.
17213        use crate::aplicacao::Placement;
17214        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
17215        let slots = c.declared_mesh_slots();
17216        assert!(
17217            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
17218            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
17219             when `:placement` is Some (even for Placement::default()) \
17220             — the accessor and the enumerator gate must route through \
17221             the same substrate-primitive typed dispatch on the outer \
17222             :placement presence bit (got slots={slots:?})",
17223        );
17224        let c = caixa_aplicacao_with_placement(None);
17225        let slots = c.declared_mesh_slots();
17226        assert!(
17227            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
17228            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
17229             when `:placement` is None — the author-omitted arm must \
17230             route through the accessor's None-return unchanged (got \
17231             slots={slots:?})",
17232        );
17233    }
17234
17235    #[test]
17236    fn aplicacao_view_placement_arm_folds_through_accessor() {
17237        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
17238        // Aplicacao-composition seed must fold through
17239        // [`Caixa::placement`], not the raw
17240        // `self.placement.clone().unwrap_or_default()` field-borrow.
17241        // Structurally: a `Caixa { placement: Some(Placement {
17242        // estrategia: Replicated, clusters: ["rio"], .. default }),
17243        // kind: Aplicacao, .. }` must surface a projected
17244        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
17245        // `placement().clusters()` byte-equal the outer composite's
17246        // authored values (the fold must project the authored
17247        // composite verbatim), a `Caixa { placement:
17248        // Some(Placement::default()), kind: Aplicacao, .. }` must
17249        // surface an [`crate::AplicacaoSpec`] whose `placement()`
17250        // byte-equals [`crate::aplicacao::Placement::default`] (the
17251        // fold's empty-composite arm collapses to the same default
17252        // the author-omitted arm does), and a `Caixa { placement:
17253        // None, kind: Aplicacao, .. }` must surface an
17254        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
17255        // [`crate::aplicacao::Placement::default`] (the "author
17256        // omitted the slot entirely" arm folds through the
17257        // `unwrap_or_default` onto the cluster-default). The triad
17258        // jointly pins the accessor + Aplicacao-composition seed
17259        // composition: any future silent detour that had the accessor
17260        // divert the raw slot away from the seed's fold (an operator-
17261        // resolved overlay's default-fold arm silently differing from
17262        // the raw slot's default-fold arm) would silently split the
17263        // build-time distribution-artifact emission gate from the
17264        // caixa-mesh renderer's Aplicacao-view input at the
17265        // composition boundary.
17266        use crate::aplicacao::{Placement, PlacementStrategy};
17267        let c = caixa_aplicacao_with_placement(Some(Placement {
17268            estrategia: PlacementStrategy::Replicated,
17269            clusters: vec!["rio".into()],
17270            affinity: None,
17271            shard_key: None,
17272        }));
17273        let view = c.aplicacao_view().unwrap();
17274        assert_eq!(
17275            view.placement().estrategia(),
17276            PlacementStrategy::Replicated,
17277            "Caixa::aplicacao_view must fold the authored :placement \
17278             :estrategia scalar through the accessor verbatim onto the \
17279             projected AplicacaoSpec — a future silent detour at the \
17280             seed's fold arm would surface here as a projected-scalar \
17281             drift (got {:?})",
17282            view.placement().estrategia(),
17283        );
17284        assert_eq!(
17285            view.placement().clusters(),
17286            &["rio"],
17287            "Caixa::aplicacao_view must fold the authored :placement \
17288             :clusters list through the accessor verbatim onto the \
17289             projected AplicacaoSpec — a future silent detour at the \
17290             seed's fold arm would surface here as a projected-list \
17291             drift (got {:?})",
17292            view.placement().clusters(),
17293        );
17294        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
17295        let view = c.aplicacao_view().unwrap();
17296        assert_eq!(
17297            view.placement(),
17298            &Placement::default(),
17299            "Caixa::aplicacao_view must fold Some(Placement::default()) \
17300             through the accessor onto Placement::default — the empty- \
17301             composite arm collapses to the same default the author- \
17302             omitted arm does (got {:?})",
17303            view.placement(),
17304        );
17305        let c = caixa_aplicacao_with_placement(None);
17306        let view = c.aplicacao_view().unwrap();
17307        assert_eq!(
17308            view.placement(),
17309            &Placement::default(),
17310            "Caixa::aplicacao_view must fold None through the accessor's \
17311             unwrap_or_default onto Placement::default — the author- \
17312             omitted arm must route through the accessor's None-return \
17313             unchanged (got {:?})",
17314            view.placement(),
17315        );
17316    }
17317
17318    #[test]
17319    fn placement_projects_option_ref_by_borrow() {
17320        // The by-borrow pin: [`Caixa::placement`] returns
17321        // `Option<&Placement>` by borrow — the returned reference
17322        // borrows the underlying `Option<Placement>` storage of the
17323        // `:placement` slot and the accessor must not clone the
17324        // backing composite on every call. Peer of the sibling
17325        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17326        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
17327        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
17328        // pins on the outer top-level [`Caixa`]
17329        // `Option<&Composite>`-return sub-family — extended here to
17330        // the fourth axis of the same sub-family: the accessor's
17331        // returned reference must borrow from `&self` (the returned
17332        // reference's lifetime is tied to `&self`), and calling the
17333        // accessor twice on the same [`Caixa`] must yield references
17334        // that are pointer-equal (the underlying byte-buffer is the
17335        // storage `Placement`'s allocation, not a fresh copy) as well
17336        // as value-equal (idempotent, no side effects on `&self`).
17337        //
17338        // Pins against a future silent detour that returned an owned
17339        // `Placement` (which would type-check via the `Clone` impl
17340        // but silently clone on every call), a `&Placement` panic-
17341        // return on the `None` arm (which would collapse the load-
17342        // bearing `Option` presence-bit into a runtime panic), or a
17343        // one-arm-only accessor that returned a saturating composite
17344        // on some sentinel input.
17345        use crate::aplicacao::{Placement, PlacementStrategy};
17346        for placement in [
17347            Some(Placement::default()),
17348            Some(Placement {
17349                estrategia: PlacementStrategy::Sharded,
17350                clusters: vec!["rio".into(), "sao-paulo".into()],
17351                affinity: Some("data-locality".into()),
17352                shard_key: Some("$tenantId".into()),
17353            }),
17354        ] {
17355            let c = caixa_aplicacao_with_placement(placement.clone());
17356            let first = c.placement().unwrap();
17357            let second = c.placement().unwrap();
17358            assert_eq!(
17359                first, second,
17360                "Caixa::placement must be idempotent — two successive \
17361                 calls on the same &self must return the same \
17362                 &Placement",
17363            );
17364            assert!(
17365                std::ptr::eq(first, second),
17366                "Caixa::placement must borrow the underlying \
17367                 Option<Placement> storage — two successive calls \
17368                 must return references with the same backing pointer \
17369                 (a fresh Placement clone would change the pointer on \
17370                 every call)",
17371            );
17372            assert_eq!(
17373                Some(first),
17374                placement.as_ref(),
17375                "Caixa::placement must return :placement verbatim by \
17376                 borrow — got {first:?}, expected {:?}",
17377                placement.as_ref(),
17378            );
17379        }
17380        let c = caixa_aplicacao_with_placement(None);
17381        assert!(
17382            c.placement().is_none(),
17383            "Caixa::placement must return None when :placement is \
17384             absent — the author-omitted arm must project through the \
17385             accessor's Option::None unchanged",
17386        );
17387    }
17388
17389    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
17390
17391    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
17392        use crate::aplicacao::{Membro, WitContract};
17393        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17394        c.kind = CaixaKind::Aplicacao;
17395        c.membros = vec![Membro {
17396            caixa: "a".into(),
17397            versao: "^0.1".into(),
17398        }];
17399        c.contratos = vec![WitContract {
17400            de: "a".into(),
17401            para: "a".into(),
17402            wit: "wasi:http/proxy".into(),
17403            endpoint: Some("/x".into()),
17404            subject: None,
17405            slot: None,
17406        }];
17407        c.entrada = entrada;
17408        c
17409    }
17410
17411    #[test]
17412    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
17413        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
17414        // composite optional-composite-reference-shape pin:
17415        // [`Caixa::entrada`] must return the `:entrada` typed
17416        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
17417        // reference over the same backing storage the raw
17418        // `self.entrada.as_ref()` field access borrows from,
17419        // byte-equal across every representative fixture in the
17420        // accept-set — the author-omitted `None` shape (the
17421        // "cluster-internal Aplicacao" partition every downstream
17422        // Gateway-API emitter treats as "emit no listener + no
17423        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
17424        // (empty `paths` — the resolved-paths fallback the peer
17425        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
17426        // onto the substrate catch-all), and a fully-populated
17427        // multi-path-with-non-default-port fixture (the canonical
17428        // shape a public HTTP Aplicacao carries).
17429        //
17430        // Pins against a future silent detour that returned a fresh-
17431        // cloned [`crate::aplicacao::Entrada`] copy (which would
17432        // type-check via the `Clone` impl but silently break every
17433        // downstream caller that relied on the reference sharing the
17434        // composite's backing identity), a reference to an operator-
17435        // resolved overlay (the future per-cluster
17436        // `:entrada-overrides` slot — its resolution must land at
17437        // exactly this accessor body, not silently divert the raw
17438        // slot away from the peer [`Caixa::declared_mesh_slots`]
17439        // enumerator's presence probe), or an axis-shuffled projection
17440        // (a future detour that swapped `host` and `para` through the
17441        // accessor would silently split the paired
17442        // [`Caixa::aplicacao_view`] seed's forward input from the
17443        // sibling M3 gateway-artifact emitter's projection input).
17444        //
17445        // Fifth and final outer top-level [`Caixa`]
17446        // `Option<&Composite>`-return composite-reference accessor pin
17447        // on the substrate primitive — peer of the sibling
17448        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17449        // (b2bd9d7),
17450        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17451        // (35d8b52),
17452        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17453        // (5d23d29), and
17454        // `placement_returns_placement_option_ref_verbatim_across_permutations`
17455        // (4fb8074) opening tetrad pins on the outer top-level
17456        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17457        // here to the third and final M3 mesh-slot axis so the closed
17458        // outer `Option<&Composite>` sub-family carries the same
17459        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
17460        // accessor discipline across all five arms.
17461        use crate::aplicacao::Entrada;
17462        let fixtures: Vec<Option<Entrada>> = vec![
17463            None,
17464            Some(Entrada {
17465                host: "checkout.quero.cloud".into(),
17466                para: "gateway".into(),
17467                paths: Vec::new(),
17468                port: crate::DEFAULT_SERVICO_PORT,
17469            }),
17470            Some(Entrada {
17471                host: "api.pleme.io".into(),
17472                para: "public-api".into(),
17473                paths: vec!["/v1".into(), "/v2".into()],
17474                port: 8080,
17475            }),
17476        ];
17477        for entrada in fixtures {
17478            let c = caixa_aplicacao_with_entrada(entrada.clone());
17479            assert_eq!(
17480                c.entrada(),
17481                entrada.as_ref(),
17482                "Caixa::entrada must return :entrada verbatim (got \
17483                 {:?}, expected {:?})",
17484                c.entrada(),
17485                entrada.as_ref(),
17486            );
17487            match (c.entrada(), c.entrada.as_ref()) {
17488                (Some(a), Some(b)) => assert!(
17489                    std::ptr::eq(a, b),
17490                    "Caixa::entrada accessor and self.entrada.as_ref() \
17491                     field access must borrow the same backing storage \
17492                     — the accessor is the substrate-primitive typed \
17493                     dispatch every downstream Aplicacao-external- \
17494                     gateway composite consumer must route through, and \
17495                     a reference-identity split would silently break \
17496                     every consumer that relied on the borrow sharing \
17497                     the composite's storage",
17498                ),
17499                (None, None) => {}
17500                _ => panic!(
17501                    "Caixa::entrada presence bit must byte-equal \
17502                     self.entrada.is_some() — a presence-bit drift \
17503                     would silently split the paired \
17504                     Caixa::aplicacao_view Aplicacao-composition seed's \
17505                     traversal head from the peer \
17506                     Caixa::declared_mesh_slots M3 declared-slot \
17507                     enumerator's presence probe",
17508                ),
17509            }
17510            assert_eq!(
17511                c.entrada().is_some(),
17512                c.entrada.is_some(),
17513                "Caixa::entrada().is_some() must byte-equal \
17514                 self.entrada.is_some() — a presence-bit drift would \
17515                 silently split every downstream Option<&Entrada> \
17516                 consumer's partition on the cluster-internal arm",
17517            );
17518        }
17519    }
17520
17521    #[test]
17522    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
17523        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
17524        // presence-probe arm must key off [`Caixa::entrada`], not the
17525        // raw `self.entrada.is_some()` field-probe. Structurally: a
17526        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
17527        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
17528        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
17529        // presence bit is `Some`, so the M3 kind-coherence gate must
17530        // surface the slot as "declared" even when every per-axis
17531        // scalar defers to the substrate catch-all / default port),
17532        // and a `Caixa { entrada: None, .. }` must NOT push the label
17533        // (the "author omitted the slot entirely" partition). The pair
17534        // jointly pins the accessor + declared-slot enumerator
17535        // composition: any future silent detour that had the accessor
17536        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
17537        // `.filter(|e| !e.paths.is_empty())` projection) would silently
17538        // absorb the "declared but empty-paths" arm at the accessor
17539        // boundary and the
17540        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17541        // coherence gate would silently accept a struct-literal
17542        // `Caixa` carrying the drift.
17543        //
17544        // Peer of the sibling
17545        // `declared_servico_slots_limits_arm_routes_through_accessor`
17546        // (b2bd9d7),
17547        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17548        // (35d8b52),
17549        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17550        // (5d23d29), and
17551        // `declared_mesh_slots_placement_arm_routes_through_accessor`
17552        // (4fb8074) composition pins on the sibling `:limits` /
17553        // `:behavior` / `:politicas` / `:placement` outer-
17554        // `Option<&Composite>` arms — same "the enumerator gate must
17555        // route through the substrate-primitive typed dispatch"
17556        // discipline extended onto the third and final M3 mesh-slot
17557        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
17558        // carries the routing invariant on every M3 mesh-slot arm.
17559        use crate::aplicacao::Entrada;
17560        let c = caixa_aplicacao_with_entrada(Some(Entrada {
17561            host: "checkout.quero.cloud".into(),
17562            para: "gateway".into(),
17563            paths: Vec::new(),
17564            port: crate::DEFAULT_SERVICO_PORT,
17565        }));
17566        let slots = c.declared_mesh_slots();
17567        assert!(
17568            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17569            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
17570             `:entrada` is Some (even for empty-paths / default-port) \
17571             — the accessor and the enumerator gate must route through \
17572             the same substrate-primitive typed dispatch on the outer \
17573             :entrada presence bit (got slots={slots:?})",
17574        );
17575        let c = caixa_aplicacao_with_entrada(None);
17576        let slots = c.declared_mesh_slots();
17577        assert!(
17578            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17579            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
17580             when `:entrada` is None — the author-omitted arm must \
17581             route through the accessor's None-return unchanged (got \
17582             slots={slots:?})",
17583        );
17584    }
17585
17586    #[test]
17587    fn aplicacao_view_entrada_arm_folds_through_accessor() {
17588        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
17589        // Aplicacao-composition seed must fold through
17590        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
17591        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
17592        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
17593        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
17594        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
17595        // equals the outer composite's authored value (the fold must
17596        // project the authored composite verbatim), and a `Caixa {
17597        // entrada: None, kind: Aplicacao, .. }` must surface an
17598        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
17599        // "author omitted the slot entirely" arm folds through the
17600        // accessor's `Option::cloned` onto the same `None` presence
17601        // bit — unlike the peer `:politicas` / `:placement` arms
17602        // `:entrada` has no cluster-default fold, the omitted arm
17603        // stays omitted). The pair jointly pins the accessor +
17604        // Aplicacao-composition seed composition: any future silent
17605        // detour that had the accessor divert the raw slot away from
17606        // the seed's fold (an operator-resolved overlay's forward arm
17607        // silently differing from the raw slot's forward arm) would
17608        // silently split the build-time gateway-artifact emission gate
17609        // from the caixa-mesh renderer's Aplicacao-view input at the
17610        // composition boundary.
17611        use crate::aplicacao::Entrada;
17612        let authored = Entrada {
17613            host: "api.pleme.io".into(),
17614            para: "public-api".into(),
17615            paths: vec!["/v1".into()],
17616            port: 8080,
17617        };
17618        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
17619        let view = c.aplicacao_view().unwrap();
17620        assert_eq!(
17621            view.entrada(),
17622            Some(&authored),
17623            "Caixa::aplicacao_view must fold the authored :entrada \
17624             composite through the accessor verbatim onto the \
17625             projected AplicacaoSpec — a future silent detour at the \
17626             seed's fold arm would surface here as a projected- \
17627             composite drift (got {:?})",
17628            view.entrada(),
17629        );
17630        let c = caixa_aplicacao_with_entrada(None);
17631        let view = c.aplicacao_view().unwrap();
17632        assert!(
17633            view.entrada().is_none(),
17634            "Caixa::aplicacao_view must fold None through the \
17635             accessor's Option::cloned onto None — the author- \
17636             omitted arm must route through the accessor's None-return \
17637             unchanged (got {:?})",
17638            view.entrada(),
17639        );
17640    }
17641
17642    #[test]
17643    fn entrada_projects_option_ref_by_borrow() {
17644        // The by-borrow pin: [`Caixa::entrada`] returns
17645        // `Option<&Entrada>` by borrow — the returned reference
17646        // borrows the underlying `Option<Entrada>` storage of the
17647        // `:entrada` slot and the accessor must not clone the backing
17648        // composite on every call. Peer of the sibling
17649        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17650        // `behavior_projects_option_ref_by_borrow` (35d8b52),
17651        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
17652        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
17653        // borrow pins on the outer top-level [`Caixa`]
17654        // `Option<&Composite>`-return sub-family — extended here to
17655        // the fifth and final axis of the same sub-family, closing
17656        // the discipline: the accessor's returned reference must
17657        // borrow from `&self` (the returned reference's lifetime is
17658        // tied to `&self`), and calling the accessor twice on the
17659        // same [`Caixa`] must yield references that are pointer-equal
17660        // (the underlying byte-buffer is the storage `Entrada`'s
17661        // allocation, not a fresh copy) as well as value-equal
17662        // (idempotent, no side effects on `&self`).
17663        //
17664        // Pins against a future silent detour that returned an owned
17665        // `Entrada` (which would type-check via the `Clone` impl but
17666        // silently clone on every call), a `&Entrada` panic-return on
17667        // the `None` arm (which would collapse the load-bearing
17668        // `Option` presence-bit into a runtime panic), or a one-arm-
17669        // only accessor that returned a saturating composite on some
17670        // sentinel input.
17671        use crate::aplicacao::Entrada;
17672        for entrada in [
17673            Some(Entrada {
17674                host: "checkout.quero.cloud".into(),
17675                para: "gateway".into(),
17676                paths: Vec::new(),
17677                port: crate::DEFAULT_SERVICO_PORT,
17678            }),
17679            Some(Entrada {
17680                host: "api.pleme.io".into(),
17681                para: "public-api".into(),
17682                paths: vec!["/v1".into(), "/v2".into()],
17683                port: 8080,
17684            }),
17685        ] {
17686            let c = caixa_aplicacao_with_entrada(entrada.clone());
17687            let first = c.entrada().unwrap();
17688            let second = c.entrada().unwrap();
17689            assert_eq!(
17690                first, second,
17691                "Caixa::entrada must be idempotent — two successive \
17692                 calls on the same &self must return the same &Entrada",
17693            );
17694            assert!(
17695                std::ptr::eq(first, second),
17696                "Caixa::entrada must borrow the underlying \
17697                 Option<Entrada> storage — two successive calls must \
17698                 return references with the same backing pointer (a \
17699                 fresh Entrada clone would change the pointer on every \
17700                 call)",
17701            );
17702            assert_eq!(
17703                Some(first),
17704                entrada.as_ref(),
17705                "Caixa::entrada must return :entrada verbatim by \
17706                 borrow — got {first:?}, expected {:?}",
17707                entrada.as_ref(),
17708            );
17709        }
17710        let c = caixa_aplicacao_with_entrada(None);
17711        assert!(
17712            c.entrada().is_none(),
17713            "Caixa::entrada must return None when :entrada is absent \
17714             — the author-omitted arm must project through the \
17715             accessor's Option::None unchanged",
17716        );
17717    }
17718
17719    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
17720
17721    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
17722        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17723        c.estrategia = estrategia;
17724        c
17725    }
17726
17727    #[test]
17728    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
17729        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
17730        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
17731        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
17732        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
17733        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
17734        // over the same discriminant the raw `self.estrategia` field
17735        // access carries, byte-equal across every representative fixture
17736        // in the accept-set — the author-omitted `None` shape (the
17737        // "defer to [`RestartStrategy::default`] through the
17738        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
17739        // every non-`Supervisor`-kind `defcaixa` carries by
17740        // `#[serde(default)]`), and each of the four closed-set variants
17741        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
17742        // / [`RestartStrategy::RestForOne`] /
17743        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
17744        // partitions on.
17745        //
17746        // Pins against a future silent detour that re-derived the
17747        // strategy from a peer axis (an accidental fallback to
17748        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
17749        // collapse that read the outer `:children` list-length axis into
17750        // the strategy discriminator at the accessor boundary), a
17751        // stale-derive detour that substituted [`RestartStrategy::default`]
17752        // when the outer `Option` held `None` (which would silently
17753        // collapse the load-bearing "author explicitly declared
17754        // `:estrategia OneForOne`" vs "author omitted the slot and
17755        // inherited the default" partition the [`Self::declared_supervisor_slots`]
17756        // presence-probe reads — the enumerator gate would still push
17757        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
17758        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17759        // kind-coherence gate's traversal head from the
17760        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
17761        // composition head), a reference to an operator-resolved overlay
17762        // (the future per-cluster `:estrategia-overrides` slot — its
17763        // resolution must land at exactly this accessor body, not
17764        // silently divert the raw slot away from a second consumer), or
17765        // an axis-remap projection (a future detour that mapped
17766        // `OneForAll` through the accessor onto `OneForOne` would
17767        // silently split every downstream sibling-restart-strategy
17768        // consumer's per-arm fan-out).
17769        //
17770        // First outer top-level [`Caixa`] `Option<Copy>`-return
17771        // supervisor-tree-slot flat-spread accessor pin on the substrate
17772        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
17773        // projection pattern the sibling per-`Caixa` `:max-restarts` /
17774        // `:restart-window` future outer-scalar pins fold on. Peer of
17775        // the inner-altitude
17776        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17777        // (eafb619) pin on the post-composition [`SupervisorSpec`]
17778        // altitude — same "the substrate-primitive accessor must byte-
17779        // equal the raw field access verbatim across every author-
17780        // declared value" discipline extended onto the pre-composition
17781        // outer author-surface [`Caixa`] altitude. Peer of the closed
17782        // outer-`Caixa` `Option<&Composite>` composite-reference family
17783        // the sibling `limits` / `behavior` / `politicas` / `placement` /
17784        // `entrada`
17785        // `..._returns_..._option_ref_verbatim_across_permutations` pins
17786        // already carry on the outer `Option<&Composite>` altitude.
17787        use crate::supervisor::RestartStrategy;
17788        let fixtures: Vec<Option<RestartStrategy>> = vec![
17789            None,
17790            Some(RestartStrategy::OneForOne),
17791            Some(RestartStrategy::OneForAll),
17792            Some(RestartStrategy::RestForOne),
17793            Some(RestartStrategy::SimpleOneForOne),
17794        ];
17795        for estrategia in fixtures {
17796            let c = caixa_with_estrategia(estrategia);
17797            assert_eq!(
17798                c.estrategia(),
17799                estrategia,
17800                "Caixa::estrategia must return :estrategia verbatim (got \
17801                 {:?}, expected {:?})",
17802                c.estrategia(),
17803                estrategia,
17804            );
17805            assert_eq!(
17806                c.estrategia(),
17807                c.estrategia,
17808                "Caixa::estrategia accessor and self.estrategia field \
17809                 access must byte-equal — the accessor is the substrate-\
17810                 primitive typed dispatch every downstream supervisor-\
17811                 tree flat-spread consumer must route through, and a \
17812                 discriminant split would silently break every consumer \
17813                 that relied on the accessor sharing the field's own \
17814                 Option<Copy> shape",
17815            );
17816            assert_eq!(
17817                c.estrategia().is_some(),
17818                c.estrategia.is_some(),
17819                "Caixa::estrategia().is_some() must byte-equal \
17820                 self.estrategia.is_some() — a presence-bit drift would \
17821                 silently split the paired Caixa::declared_supervisor_slots \
17822                 presence-probe arm from the Caixa::supervisor_view \
17823                 unwrap_or_default() fold's composition input",
17824            );
17825        }
17826    }
17827
17828    #[test]
17829    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
17830        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17831        // `:estrategia` presence-probe arm must key off
17832        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
17833        // field-probe. Structurally: every `Caixa { estrategia:
17834        // Some(RestartStrategy::_), .. }` variant must push
17835        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
17836        // (the presence bit is `Some` for every closed-set variant, so
17837        // the M2 supervisor-tree kind-coherence gate must surface the
17838        // slot as "declared" regardless of which variant the author
17839        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
17840        // the label (the "author omitted the slot entirely, deferring
17841        // to [`RestartStrategy::default`] through the supervisor_view
17842        // fold" partition). The pair jointly pins the accessor +
17843        // declared-slot enumerator composition: any future silent detour
17844        // that had the accessor collapse `Some(RestartStrategy::default())`
17845        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
17846        // projection) would silently absorb the "declared but default-
17847        // valued" arm at the accessor boundary and the
17848        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
17849        // coherence gate would silently accept a struct-literal `Caixa`
17850        // carrying the drift.
17851        //
17852        // Peer of the sibling per-`Caixa`
17853        // `declared_servico_slots_limits_arm_routes_through_accessor`
17854        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
17855        // `Option<&LimitsSpec>` composition axis — same "the enumerator
17856        // gate must route through the substrate-primitive typed
17857        // dispatch" discipline extended onto the flat-spread M2
17858        // supervisor-tree `Option<RestartStrategy>`-composition surface,
17859        // opening the outer-`Caixa` supervisor-tree-slot arm of the
17860        // composition-pin family.
17861        use crate::supervisor::RestartStrategy;
17862        for estrategia in [
17863            RestartStrategy::OneForOne,
17864            RestartStrategy::OneForAll,
17865            RestartStrategy::RestForOne,
17866            RestartStrategy::SimpleOneForOne,
17867        ] {
17868            let c = caixa_with_estrategia(Some(estrategia));
17869            let slots = c.declared_supervisor_slots();
17870            assert!(
17871                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17872                "declared_supervisor_slots must push \
17873                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
17874                 Some({estrategia:?}) — the accessor and the enumerator \
17875                 gate must route through the same substrate-primitive \
17876                 typed dispatch on the outer :estrategia presence bit \
17877                 (got slots={slots:?})",
17878            );
17879        }
17880        let c = caixa_with_estrategia(None);
17881        let slots = c.declared_supervisor_slots();
17882        assert!(
17883            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17884            "declared_supervisor_slots must NOT push \
17885             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
17886             — the author-omitted arm must route through the accessor's \
17887             None-return unchanged (got slots={slots:?})",
17888        );
17889    }
17890
17891    #[test]
17892    fn supervisor_view_estrategia_arm_routes_through_accessor() {
17893        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
17894        // [`SupervisorSpec`] construction arm must key off
17895        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
17896        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
17897        // for every `:kind Supervisor` `Caixa` carrying an author-
17898        // declared `Some(RestartStrategy::_)` variant, the composed
17899        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
17900        // outer accessor's declared variant unchanged; and for a
17901        // `:kind Supervisor` `Caixa` carrying `None`, the composed
17902        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
17903        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
17904        // arm the flat-spread `unwrap_or_default()` fold projects to on
17905        // the author-omitted arm — this is the *composition* between the
17906        // outer `Option<RestartStrategy>` accessor's presence-bit
17907        // surface and the inner post-composition non-`Option`
17908        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
17909        // pins the accessor + supervisor_view composition: any future
17910        // silent detour that had the accessor promote `None` to
17911        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
17912        // projection) would silently collapse the two arms into one at
17913        // the accessor boundary and the [`Self::declared_supervisor_slots`]
17914        // presence probe would silently drift from the composition site.
17915        //
17916        // Peer of the sibling M2 supervisor-slot post-composition
17917        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
17918        // pin on the [`SupervisorSpec::validate`] altitude — this pin
17919        // extends that inner-altitude accessor-routing discipline onto
17920        // the pre-composition outer author-surface [`Caixa`] altitude,
17921        // pinning the composition edge between the flat-spread outer
17922        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
17923        // `RestartStrategy` axes.
17924        use crate::CaixaKind;
17925        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17926        for estrategia in [
17927            RestartStrategy::OneForOne,
17928            RestartStrategy::OneForAll,
17929            RestartStrategy::RestForOne,
17930            RestartStrategy::SimpleOneForOne,
17931        ] {
17932            let mut c = caixa_with_estrategia(Some(estrategia));
17933            c.kind = CaixaKind::Supervisor;
17934            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
17935            // shape partition through the [`gen_platform::IsVariant`]
17936            // derive-generated
17937            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
17938            // than the raw `matches!(estrategia, RestartStrategy::
17939            // SimpleOneForOne)` open-coded pattern-match — same closed-
17940            // set-typed-enum arm-discriminator dispatch discipline the
17941            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
17942            // convergence (915a934) extended onto its two paired positive
17943            // / negated `matches!` sites and the peer
17944            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
17945            // predicate convergence (766ec63) extended onto the M3 mesh-
17946            // slot per-`:placement` distribution-strategy discriminator
17947            // axis. See the sibling `supervisor::tests::
17948            // round_trip_all_strategies` and
17949            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17950            // fixtures — the three sites (all test-only,
17951            // acknowledged in 915a934's Prior-commits footnote as the
17952            // outstanding follow-up) now consult one typed dispatch on
17953            // the substrate primitive.
17954            c.children = if estrategia.is_simple_one_for_one() {
17955                Vec::new()
17956            } else {
17957                vec![ChildSpec {
17958                    caixa: "worker".into(),
17959                    versao: "^0.1".into(),
17960                    restart: RestartPolicy::Permanent,
17961                }]
17962            };
17963            let view = c.supervisor_view().expect(
17964                "supervisor_view must materialize a SupervisorSpec for a \
17965                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
17966            );
17967            assert_eq!(
17968                view.estrategia(),
17969                c.estrategia().unwrap(),
17970                "supervisor_view must carry the outer Caixa::estrategia() \
17971                 declared variant onto the composed SupervisorSpec.estrategia \
17972                 field verbatim on the Some arm (got {:?}, expected {:?})",
17973                view.estrategia(),
17974                c.estrategia().unwrap(),
17975            );
17976        }
17977        // The author-omitted arm: outer `None` → composed
17978        // `RestartStrategy::default()` through the flat-spread
17979        // `unwrap_or_default()` fold.
17980        let mut c = caixa_with_estrategia(None);
17981        c.kind = CaixaKind::Supervisor;
17982        // Populate children so the sibling supervisor slots are coherent
17983        // for the [`Self::supervisor_view`] projection; the `:estrategia`
17984        // arm still defers to [`RestartStrategy::default`] on the
17985        // author-omitted arm even when the sibling slots carry values.
17986        c.children = vec![ChildSpec {
17987            caixa: "worker".into(),
17988            versao: "^0.1".into(),
17989            restart: RestartPolicy::Permanent,
17990        }];
17991        let view = c.supervisor_view().expect(
17992            "supervisor_view must materialize a SupervisorSpec for a \
17993             :kind Supervisor Caixa carrying a None `:estrategia` slot",
17994        );
17995        assert_eq!(
17996            view.estrategia(),
17997            RestartStrategy::default(),
17998            "supervisor_view must project the outer Caixa::estrategia() \
17999             None arm onto RestartStrategy::default() through the flat-\
18000             spread unwrap_or_default() fold (got {:?}, expected {:?})",
18001            view.estrategia(),
18002            RestartStrategy::default(),
18003        );
18004        assert!(
18005            c.estrategia().is_none(),
18006            "Caixa::estrategia() must remain None on the author-omitted \
18007             arm — the supervisor_view fold must not mutate the outer \
18008             flat-spread presence bit",
18009        );
18010    }
18011
18012    #[test]
18013    fn estrategia_projects_option_by_copy() {
18014        // The by-`Copy` pin: [`Caixa::estrategia`] returns
18015        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
18016        // the accessor does not borrow `&self` past the call (no
18017        // lifetime on the return type), and calling the accessor twice
18018        // on the same [`Caixa`] must yield discriminant-equal values
18019        // (idempotent, no side effects on `&self`). Peer of the sibling
18020        // outer-`Caixa` `Option<&Composite>` by-borrow
18021        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
18022        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
18023        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
18024        // `placement_projects_option_ref_by_borrow` (4fb8074) /
18025        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
18026        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
18027        // extended here to the outer-`Caixa` `Option<Copy>`-return
18028        // flat-spread axis. The `Copy` discipline replaces the pointer-
18029        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
18030        // `Copy` discriminant is definitionally the same discriminant, so
18031        // the axis reduces to discriminant equality).
18032        //
18033        // Pins against a future silent detour that returned a fresh
18034        // `Option<&RestartStrategy>` (which would type-check but silently
18035        // introduce a borrow of `&self` past the call, collapsing the
18036        // load-bearing "no lifetime on the return type" `Copy` projection
18037        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
18038        // read side effect that flipped the outer discriminant on
18039        // successive calls, or an axis-remap projection that returned a
18040        // different variant than the field storage.
18041        use crate::supervisor::RestartStrategy;
18042        for estrategia in [
18043            Some(RestartStrategy::OneForOne),
18044            Some(RestartStrategy::OneForAll),
18045            Some(RestartStrategy::RestForOne),
18046            Some(RestartStrategy::SimpleOneForOne),
18047        ] {
18048            let c = caixa_with_estrategia(estrategia);
18049            let first = c.estrategia();
18050            let second = c.estrategia();
18051            assert_eq!(
18052                first, second,
18053                "Caixa::estrategia must be idempotent — two successive \
18054                 calls on the same &self must return the same \
18055                 Option<RestartStrategy>",
18056            );
18057            assert_eq!(
18058                first, estrategia,
18059                "Caixa::estrategia must return :estrategia verbatim by \
18060                 Copy — got {first:?}, expected {estrategia:?}",
18061            );
18062        }
18063        let c = caixa_with_estrategia(None);
18064        assert!(
18065            c.estrategia().is_none(),
18066            "Caixa::estrategia must return None when :estrategia is \
18067             absent — the author-omitted arm must project through the \
18068             accessor's Option::None unchanged",
18069        );
18070    }
18071
18072    // ── Caixa::max_restarts / Caixa::restart_window —
18073    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
18074    //    (Option<u32> / Option<&str>) folding on the ed04d3c
18075    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
18076
18077    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
18078        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18079        c.max_restarts = max_restarts;
18080        c
18081    }
18082
18083    fn caixa_supervisor_with_max_restarts_and_window(
18084        max_restarts: Option<u32>,
18085        restart_window: Option<&str>,
18086    ) -> Caixa {
18087        use crate::CaixaKind;
18088        use crate::supervisor::{ChildSpec, RestartPolicy};
18089        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
18090        c.kind = CaixaKind::Supervisor;
18091        c.max_restarts = max_restarts;
18092        c.restart_window = restart_window.map(str::to_string);
18093        c.children = vec![ChildSpec {
18094            caixa: "worker".into(),
18095            versao: "^0.1".into(),
18096            restart: RestartPolicy::Permanent,
18097        }];
18098        c
18099    }
18100
18101    #[test]
18102    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
18103        // Value-shape pin: [`Caixa::max_restarts`] returns the
18104        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
18105        // from the typed slot's own storage, byte-equal across the
18106        // author-omitted `None` arm (the "defer to the
18107        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
18108        // `{intensity, 5, 60}` default" partition every
18109        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
18110        // and each of the representative fixtures in the accept-set —
18111        // `0` (the zero-floor arm the peer
18112        // [`crate::supervisor::SupervisorSpec::validate`]
18113        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
18114        // the post-composition altitude — the accessor must ship the
18115        // raw slot verbatim so struct-literal fixtures continue to
18116        // expose the zero at the accessor boundary), the OTP-canonical
18117        // `5` default (`{intensity, 5, 60}` worker-supervisor from
18118        // Learn You Some Erlang), `1000` (the
18119        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
18120        // upper-bound gate accepts on the boundary), `u32::MAX` (a
18121        // past-the-cap sentinel that the substrate-primitive accessor
18122        // must still ship verbatim). Second outer top-level
18123        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
18124        // pin — folds on the sibling
18125        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
18126        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
18127        // onto the sibling `Option<u32>` restart-budget-count arm.
18128        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
18129        for max_restarts in fixtures {
18130            let c = caixa_with_max_restarts(max_restarts);
18131            assert_eq!(
18132                c.max_restarts(),
18133                max_restarts,
18134                "Caixa::max_restarts must return :max-restarts verbatim \
18135                 (got {:?}, expected {max_restarts:?})",
18136                c.max_restarts(),
18137            );
18138            assert_eq!(
18139                c.max_restarts(),
18140                c.max_restarts,
18141                "Caixa::max_restarts accessor and self.max_restarts \
18142                 field access must byte-equal — a presence-bit or count \
18143                 drift would silently split the paired \
18144                 Caixa::declared_supervisor_slots presence-probe arm \
18145                 from the Caixa::supervisor_view unwrap_or(5) fold's \
18146                 composition input",
18147            );
18148        }
18149    }
18150
18151    #[test]
18152    fn max_restarts_projects_option_by_copy() {
18153        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
18154        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
18155        // borrow `&self` past the call (no lifetime on the return type),
18156        // and calling the accessor twice on the same [`Caixa`] must
18157        // yield equal values (idempotent, no side effects). Peer of the
18158        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
18159        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
18160        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
18161            let c = caixa_with_max_restarts(max_restarts);
18162            let first = c.max_restarts();
18163            let second = c.max_restarts();
18164            assert_eq!(
18165                first, second,
18166                "Caixa::max_restarts must be idempotent — two successive \
18167                 calls on the same &self must return the same Option<u32>",
18168            );
18169            assert_eq!(
18170                first, max_restarts,
18171                "Caixa::max_restarts must return :max-restarts verbatim \
18172                 by Copy — got {first:?}, expected {max_restarts:?}",
18173            );
18174        }
18175    }
18176
18177    #[test]
18178    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
18179        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18180        // `:max-restarts` presence-probe arm must key off
18181        // [`Caixa::max_restarts`], not the raw
18182        // `self.max_restarts.is_some()` field-probe. Structurally: every
18183        // `Caixa { max_restarts: Some(_), .. }` variant must push
18184        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
18185        // list (the presence bit is `Some` for every representative
18186        // count, so the M2 kind-coherence gate must surface the slot as
18187        // "declared"), and a `Caixa { max_restarts: None, .. }` must
18188        // NOT push the label. Peer of the sibling
18189        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
18190        // (ed04d3c) composition pin — same routing-through-accessor
18191        // discipline extended onto the sibling flat-spread `Option<u32>`
18192        // arm.
18193        for max_restarts in [0u32, 5, 1000, u32::MAX] {
18194            let c = caixa_with_max_restarts(Some(max_restarts));
18195            let slots = c.declared_supervisor_slots();
18196            assert!(
18197                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
18198                "declared_supervisor_slots must push \
18199                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
18200                 is Some({max_restarts}) — the accessor and the \
18201                 enumerator gate must route through the same \
18202                 substrate-primitive typed dispatch on the outer \
18203                 :max-restarts presence bit (got slots={slots:?})",
18204            );
18205        }
18206        let c = caixa_with_max_restarts(None);
18207        let slots = c.declared_supervisor_slots();
18208        assert!(
18209            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
18210            "declared_supervisor_slots must NOT push \
18211             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
18212             None — the author-omitted arm must route through the \
18213             accessor's None-return unchanged (got slots={slots:?})",
18214        );
18215    }
18216
18217    #[test]
18218    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
18219        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
18220        // [`SupervisorSpec`] construction arm must key off
18221        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
18222        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
18223        // every `:kind Supervisor` `Caixa` carrying an author-declared
18224        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
18225        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
18226        // carrying `None`, the composed [`SupervisorSpec`]'s
18227        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
18228        // of the sibling
18229        // `supervisor_view_estrategia_arm_routes_through_accessor`
18230        // (ed04d3c) composition pin.
18231        for max_restarts in [1u32, 5, 1000] {
18232            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
18233            let view = c.supervisor_view().expect(
18234                "supervisor_view must materialize a SupervisorSpec for a \
18235                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
18236            );
18237            assert_eq!(
18238                view.max_restarts(),
18239                max_restarts,
18240                "supervisor_view must carry the outer \
18241                 Caixa::max_restarts() Some arm onto the composed \
18242                 SupervisorSpec.max_restarts field verbatim (got {}, \
18243                 expected {max_restarts})",
18244                view.max_restarts(),
18245            );
18246        }
18247        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18248        let view = c.supervisor_view().expect(
18249            "supervisor_view must materialize a SupervisorSpec for a \
18250             :kind Supervisor Caixa carrying a None :max-restarts",
18251        );
18252        assert_eq!(
18253            view.max_restarts(),
18254            5,
18255            "supervisor_view must project the outer \
18256             Caixa::max_restarts() None arm onto the OTP-canonical \
18257             {{intensity, 5, 60}} default (5) through the flat-spread \
18258             unwrap_or(5) fold (got {})",
18259            view.max_restarts(),
18260        );
18261        assert!(
18262            c.max_restarts().is_none(),
18263            "Caixa::max_restarts() must remain None on the author-\
18264             omitted arm — the supervisor_view fold must not mutate \
18265             the outer flat-spread presence bit",
18266        );
18267    }
18268
18269    #[test]
18270    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
18271        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
18272        // `:estrategia` arm must degrade onto the substrate-canonical
18273        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
18274        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
18275        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
18276        // worker-supervisor default — rather than the transitively-
18277        // derived [`crate::supervisor::RestartStrategy::default`] route
18278        // the prior `.unwrap_or_default()` fold reached for. Prior to the
18279        // lift the composition site carried `.unwrap_or_default()` with
18280        // no compile-time link back to the shared OTP-canonical strategy
18281        // default that the paired [`crate::supervisor::Default for
18282        // RestartStrategy`] impl and the [`crate::supervisor::Default for
18283        // SupervisorSpec`] impl's struct-literal `estrategia` field both
18284        // (now) route through the same lifted constant — so a future
18285        // rebrand of the OTP-canonical strategy default (an OTP
18286        // `rest_for_one` widening once the substrate discovers startup-
18287        // order-coupled child cohorts as the more common worker-
18288        // supervisor shape, a per-cluster overlay the operator pins
18289        // through the MESH-COMPOSITION §III.2 supervision-canary
18290        // `:estrategia-overrides` roadmap slot) would have had to migrate
18291        // the paired `MaxIntensity` + `Period` halves through the lifted
18292        // constants and the `one_for_one` half through a
18293        // `RestartStrategy::default()` route in lockstep or a
18294        // `:kind Supervisor` caixa carrying an author-omitted
18295        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
18296        // whose `estrategia` disagreed with the paired
18297        // `SupervisorSpec::default()` view. Byte-parity against the
18298        // lifted constant closes the split. Peer of the sibling
18299        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
18300        // composition pin on the paired `MaxIntensity` half + the
18301        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
18302        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
18303        // pins on the sibling entry points onto the shared substrate
18304        // constant.
18305        use crate::CaixaKind;
18306        use crate::supervisor::{ChildSpec, RestartPolicy};
18307        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
18308        c.kind = CaixaKind::Supervisor;
18309        c.estrategia = None;
18310        c.children = vec![ChildSpec {
18311            caixa: "worker".into(),
18312            versao: "^0.1".into(),
18313            restart: RestartPolicy::Permanent,
18314        }];
18315        let view = c.supervisor_view().expect(
18316            "supervisor_view must materialize a SupervisorSpec for a \
18317             :kind Supervisor Caixa carrying a None :estrategia",
18318        );
18319        assert_eq!(
18320            view.estrategia(),
18321            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
18322            "supervisor_view must degrade the outer \
18323             Caixa::estrategia() None arm onto the lifted \
18324             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
18325             expected {:?})",
18326            view.estrategia(),
18327            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
18328        );
18329    }
18330
18331    #[test]
18332    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
18333        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
18334        // `:max-restarts` arm must degrade onto the substrate-canonical
18335        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
18336        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
18337        // `MaxIntensity` default — rather than a raw `5` literal. Prior
18338        // to the lift the composition site carried an inline
18339        // `.unwrap_or(5)` with no compile-time link back to the shared
18340        // OTP-canonical default that the serde-side
18341        // `#[serde(default = "default_max_restarts")]` wire-format arm
18342        // and the [`Default for crate::supervisor::SupervisorSpec`]
18343        // struct-literal default arm both key off — so a future rebrand
18344        // of the OTP-canonical default (Elixir's `Supervisor` `3`
18345        // default, a per-cluster overlay the operator pins through the
18346        // MESH-COMPOSITION §III.2 supervision-canary
18347        // `:supervisor :max-restarts-overrides` roadmap slot) would
18348        // have had to be threaded through both the serde-side helper
18349        // and this view-construction arm in lockstep or a `:kind
18350        // Supervisor` caixa carrying `:max-restarts ()` would silently
18351        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
18352        // with the same fixture's serde-side `SupervisorSpec` view (an
18353        // author-omitted slot round-tripping through
18354        // `SupervisorSpec::default()` to the lifted constant, then
18355        // splitting to a stale literal past `supervisor_view`).
18356        // Byte-parity against the lifted constant closes the split.
18357        // Peer of the sibling
18358        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
18359        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
18360        // composition pins that close the same routing on the two
18361        // sibling entry points onto the shared substrate constant.
18362        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18363        let view = c.supervisor_view().expect(
18364            "supervisor_view must materialize a SupervisorSpec for a \
18365             :kind Supervisor Caixa carrying a None :max-restarts",
18366        );
18367        assert_eq!(
18368            view.max_restarts(),
18369            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18370            "supervisor_view must degrade the outer \
18371             Caixa::max_restarts() None arm onto the lifted \
18372             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
18373             expected {})",
18374            view.max_restarts(),
18375            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18376        );
18377    }
18378
18379    #[test]
18380    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
18381        // Value-shape pin: [`Caixa::restart_window`] returns the
18382        // `:restart-window` typed `Option<String>` verbatim as an
18383        // `Option<&str>`, borrowed from the typed slot's own storage,
18384        // byte-equal across the author-omitted `None` arm and each of
18385        // the representative fixtures in the accept-set — the canonical
18386        // `"60s"` from `{intensity, 5, 60}`, the sibling
18387        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
18388        // / `"0s"`) the shared codec's positive-set sweep pin covers,
18389        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
18390        // seconds drift the sibling [`Self::validate_restart_window`]
18391        // gate refuses; the accessor must ship the raw slot verbatim
18392        // so struct-literal fixtures continue to expose the drift at
18393        // the accessor boundary). Third outer top-level [`Caixa`]
18394        // supervisor-tree flat-spread pin — extends the sub-family onto
18395        // the sibling `Option<&str>` raw-duration-string arm.
18396        for window in [
18397            None,
18398            Some("60s"),
18399            Some("5m"),
18400            Some("1h"),
18401            Some("500ms"),
18402            Some("1.5s"),
18403            Some(""),
18404        ] {
18405            let c = caixa_with_restart_window(window);
18406            assert_eq!(
18407                c.restart_window(),
18408                window,
18409                "Caixa::restart_window must return :restart-window \
18410                 verbatim as Option<&str> (got {:?}, expected {window:?})",
18411                c.restart_window(),
18412            );
18413            assert_eq!(
18414                c.restart_window(),
18415                c.restart_window.as_deref(),
18416                "Caixa::restart_window accessor and \
18417                 self.restart_window.as_deref() field access must \
18418                 byte-equal — a byte-level drift would silently split \
18419                 the paired Caixa::declared_supervisor_slots \
18420                 presence-probe arm from the \
18421                 Caixa::validate_restart_window shared-codec gate and \
18422                 the Caixa::supervisor_view soft-swallowing fold",
18423            );
18424        }
18425    }
18426
18427    #[test]
18428    fn restart_window_projects_slice_by_borrow() {
18429        // The by-borrow pin: [`Caixa::restart_window`] returns
18430        // `Option<&str>` by borrow — the returned string slice borrows
18431        // the underlying `Option<String>` storage of the `:restart-window`
18432        // slot and the accessor must not clone on every call. Peer of
18433        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
18434        // by-borrow pins on the universal-axis scalar family
18435        // (`licenca_projects_option_ref_by_borrow` /
18436        // `descricao_projects_option_ref_by_borrow` and siblings) —
18437        // extended onto the M2 supervisor-tree flat-spread
18438        // `Option<&str>` raw-duration-string axis.
18439        for window in [None, Some("60s"), Some("5m"), Some("")] {
18440            let c = caixa_with_restart_window(window);
18441            let first = c.restart_window();
18442            let second = c.restart_window();
18443            assert_eq!(
18444                first, second,
18445                "Caixa::restart_window must be idempotent — two \
18446                 successive calls on the same &self must return the \
18447                 same Option<&str>",
18448            );
18449            if let (Some(a), Some(b)) = (first, second) {
18450                assert_eq!(
18451                    a.as_ptr(),
18452                    b.as_ptr(),
18453                    "Caixa::restart_window must borrow the underlying \
18454                     String storage — two successive Some-arm calls must \
18455                     return slices with the same backing pointer (a fresh \
18456                     String clone would change the pointer on every call)",
18457                );
18458            }
18459            assert_eq!(
18460                first, window,
18461                "Caixa::restart_window must return :restart-window \
18462                 verbatim by borrow — got {first:?}, expected {window:?}",
18463            );
18464        }
18465    }
18466
18467    #[test]
18468    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
18469        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18470        // `:restart-window` presence-probe arm must key off
18471        // [`Caixa::restart_window`], not the raw
18472        // `self.restart_window.is_some()` field-probe. Structurally:
18473        // every `Caixa { restart_window: Some(_), .. }` must push
18474        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
18475        // list, and a `Caixa { restart_window: None, .. }` must NOT
18476        // push the label. Peer of the sibling
18477        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
18478        // routing pin.
18479        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
18480            let c = caixa_with_restart_window(Some(window));
18481            let slots = c.declared_supervisor_slots();
18482            assert!(
18483                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18484                "declared_supervisor_slots must push \
18485                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
18486                 `:restart-window` is Some({window:?}) — the accessor \
18487                 and the enumerator gate must route through the same \
18488                 substrate-primitive typed dispatch on the outer \
18489                 :restart-window presence bit (got slots={slots:?})",
18490            );
18491        }
18492        let c = caixa_with_restart_window(None);
18493        let slots = c.declared_supervisor_slots();
18494        assert!(
18495            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18496            "declared_supervisor_slots must NOT push \
18497             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
18498             is None — the author-omitted arm must route through the \
18499             accessor's None-return unchanged (got slots={slots:?})",
18500        );
18501    }
18502
18503    #[test]
18504    fn validate_restart_window_arm_routes_through_accessor() {
18505        // Composition pin: [`Caixa::validate_restart_window`]'s
18506        // shared-codec fold arm must key off [`Caixa::restart_window`],
18507        // not the raw `self.restart_window.as_deref()` field-projection.
18508        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
18509        // express no reset" canonical shape); (2) a canonical `Some`
18510        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
18511        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
18512        // .. })` carrying the offending raw string verbatim. The three
18513        // arms jointly pin that the validator's raw-string binding is
18514        // the accessor's return, not a peer projection — any future
18515        // silent detour that had the accessor collapse `Some("")` to
18516        // `None` would silently absorb the empty-after-trim refusal
18517        // case at the accessor boundary.
18518        caixa_with_restart_window(None)
18519            .validate_restart_window()
18520            .expect("None :restart-window must validate through the accessor");
18521        caixa_with_restart_window(Some("60s"))
18522            .validate_restart_window()
18523            .expect("canonical :restart-window \"60s\" must validate through the accessor");
18524        let err = caixa_with_restart_window(Some("1.5s"))
18525            .validate_restart_window()
18526            .expect_err("fractional-seconds :restart-window must fail through the accessor");
18527        assert!(
18528            matches!(
18529                err,
18530                ManifestError::RestartWindowMalformed { ref restart_window, .. }
18531                    if restart_window == "1.5s"
18532            ),
18533            "validator must carry the offending raw string verbatim \
18534             from the accessor's borrowed &str (got {err:?})",
18535        );
18536    }
18537
18538    #[test]
18539    fn supervisor_view_restart_window_arm_routes_through_accessor() {
18540        // Composition pin: [`Caixa::supervisor_view`]'s
18541        // per-`:restart-window` [`SupervisorSpec`] construction arm
18542        // must key off [`Caixa::restart_window`]'s soft-swallowing
18543        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
18544        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
18545        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
18546        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
18547        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
18548        // (the shared codec's canonical parse); (3) codec-rejected
18549        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
18550        // (the soft-swallow preserving the view's best-effort shape).
18551        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18552        let view = c.supervisor_view().expect("Supervisor kind has a view");
18553        assert_eq!(
18554            view.restart_window(),
18555            None,
18556            "supervisor_view must project outer None :restart-window \
18557             onto None on the composed SupervisorSpec (never-reset \
18558             sentinel) through the accessor's None-return unchanged",
18559        );
18560
18561        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
18562        let view = c.supervisor_view().expect("Supervisor kind has a view");
18563        assert_eq!(
18564            view.restart_window(),
18565            Some(std::time::Duration::from_secs(60)),
18566            "supervisor_view must fold outer Some(\"60s\") through the \
18567             shared duration_codec into Duration::from_secs(60) on the \
18568             composed SupervisorSpec (accessor's Some(&str) → codec \
18569             parse → Some(Duration))",
18570        );
18571
18572        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
18573        let view = c.supervisor_view().expect("Supervisor kind has a view");
18574        assert_eq!(
18575            view.restart_window(),
18576            None,
18577            "supervisor_view must soft-swallow the shared-codec parse \
18578             failure to None (the view's best-effort shape the sibling \
18579             manifest-level validate_restart_window surfaces as \
18580             RestartWindowMalformed); the accessor's raw-string return \
18581             is the single input every downstream consumer keys off",
18582        );
18583    }
18584
18585    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
18586
18587    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
18588        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18589        c.upgrade_from = upgrade_from;
18590        c
18591    }
18592
18593    #[test]
18594    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
18595        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
18596        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
18597        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
18598        // typed `Vec<UpgradeFromEntry>` verbatim as a
18599        // `&[UpgradeFromEntry]` slice-view over the same backing
18600        // buffer the raw `self.upgrade_from.as_slice()` field access
18601        // borrows from, element-equal across every representative
18602        // fixture in the accept-set — `[]` (the "no hot-upgrade path
18603        // declared" arm every `defcaixa` without an `:upgrade-from`
18604        // block carries; `#[serde(default)]` folds an omitted slot
18605        // onto `Vec::new()`), a canonical single-entry `Restart`
18606        // fixture (the shape most Servicos carry — a single prior
18607        // version with the fallback strategy), a canonical multi-
18608        // entry list carrying every typed instruction variant
18609        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
18610        // `Restart`), and a past-the-guard sentinel — a duplicate-
18611        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
18612        // ([`crate::upgrade::validate_upgrade_from`] rejects through
18613        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
18614        // ship the raw slot verbatim so struct-literal fixtures
18615        // continue to expose the duplicate at the accessor boundary).
18616        //
18617        // Pins against a future silent detour that returned an owned
18618        // `Vec<UpgradeFromEntry>` (which would type-check but silently
18619        // clone on every accessor call, breaking the zero-cost
18620        // projection every peer sibling slice accessor carries), a
18621        // `[dup, dup] → [dup]` dedup collapse (which would silently
18622        // absorb the `DuplicateFrom` refusal case at the accessor
18623        // boundary and the [`crate::StandardLayout::verify`] cross-
18624        // entry gate would silently accept a struct-literal `Caixa`
18625        // carrying the drift), a reference to an operator-resolved
18626        // overlay (the future per-cluster `:upgrade-overrides` slot
18627        // — its resolution must land at exactly this accessor body,
18628        // not silently divert the raw slot away from a second
18629        // consumer), or an axis-shuffled projection (a future detour
18630        // that reordered entries through the accessor would silently
18631        // split the paired [`crate::StandardLayout::verify`] per-
18632        // `:upgrade-from` shape gate's traversal input from the peer
18633        // [`crate::render::servico_m2_overlay`] emitter's projection
18634        // input, since the operator's hot-upgrade dispatch matches
18635        // per-`:from` and axis reordering would silently split the
18636        // per-entry script-path existence probe's iteration order
18637        // from the M2 overlay emitter's serialized-entry order).
18638        //
18639        // First outer top-level [`Caixa`] `&[Composite]`-return
18640        // slice accessor pin on the substrate primitive for M2 / M3
18641        // typed-slot vec-carry axes — opens the outer-`Caixa`
18642        // `&[Composite]` composite-slice projection pattern the
18643        // sibling `:children` [`crate::supervisor::ChildSpec`] /
18644        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
18645        // [`crate::aplicacao::WitContract`] future outer-composite-
18646        // slice pins fold on. Peer of the closed outer-`Caixa`
18647        // scalar `Option<&Composite>` composite-reference family the
18648        // sibling `limits` / `behavior` / `politicas` / `placement`
18649        // / `entrada` `..._returns_..._option_ref_verbatim_across_
18650        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
18651        // the "byte-equal, borrow-shared" outer-accessor discipline
18652        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
18653        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18654        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
18655            vec![],
18656            vec![UpgradeFromEntry {
18657                from: "0.0.1".into(),
18658                instructions: vec![UpgradeInstruction::Restart],
18659            }],
18660            vec![
18661                UpgradeFromEntry {
18662                    from: "0.0.1".into(),
18663                    instructions: vec![
18664                        UpgradeInstruction::LoadModule {
18665                            module: "demo".into(),
18666                        },
18667                        UpgradeInstruction::SoftPurge {
18668                            module: "demo".into(),
18669                        },
18670                    ],
18671                },
18672                UpgradeFromEntry {
18673                    from: "0.0.2".into(),
18674                    instructions: vec![
18675                        UpgradeInstruction::StateChange {
18676                            script: "servicos/upgrade.lisp".into(),
18677                        },
18678                        UpgradeInstruction::Purge {
18679                            module: "demo".into(),
18680                        },
18681                        UpgradeInstruction::Restart,
18682                    ],
18683                },
18684            ],
18685            vec![
18686                UpgradeFromEntry {
18687                    from: "0.1.0".into(),
18688                    instructions: vec![UpgradeInstruction::Restart],
18689                },
18690                UpgradeFromEntry {
18691                    from: "0.1.0".into(),
18692                    instructions: vec![UpgradeInstruction::Restart],
18693                },
18694            ],
18695        ];
18696        for upgrade_from in fixtures {
18697            let c = caixa_with_upgrade_from(upgrade_from.clone());
18698            assert_eq!(
18699                c.upgrade_from(),
18700                upgrade_from.as_slice(),
18701                "Caixa::upgrade_from must return :upgrade-from \
18702                 verbatim (got {:?}, expected {upgrade_from:?})",
18703                c.upgrade_from(),
18704            );
18705            assert_eq!(
18706                c.upgrade_from(),
18707                c.upgrade_from.as_slice(),
18708                "Caixa::upgrade_from must element-equal the raw \
18709                 `self.upgrade_from.as_slice()` field access across \
18710                 every value in the Vec<UpgradeFromEntry> accept-set",
18711            );
18712            assert_eq!(
18713                c.upgrade_from().is_empty(),
18714                c.upgrade_from.is_empty(),
18715                "Caixa::upgrade_from().is_empty() must byte-equal \
18716                 self.upgrade_from.is_empty() — a presence-bit drift \
18717                 would silently split the paired \
18718                 Caixa::declared_servico_slots M2 declared-slot \
18719                 enumerator's presence probe from the peer \
18720                 crate::render::servico_m2_overlay M2 overlay \
18721                 emitter's presence gate",
18722            );
18723        }
18724    }
18725
18726    #[test]
18727    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
18728        // Composition pin: [`Caixa::declared_servico_slots`]'s
18729        // `:upgrade-from` presence-probe arm must key off
18730        // [`Caixa::upgrade_from`], not the raw
18731        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
18732        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
18733        // instructions: vec![Restart] }], .. }` must push
18734        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
18735        // (the presence bit is non-empty, so the M2 kind-coherence
18736        // gate must surface the slot as "declared"), and a `Caixa {
18737        // upgrade_from: vec![], .. }` must NOT push the label (the
18738        // "author omitted the slot entirely" arm — the empty-slice
18739        // partition the serde-default folds onto). The pair jointly
18740        // pins the accessor + declared-slot enumerator composition:
18741        // any future silent detour that had the accessor collapse
18742        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
18743        // is_empty())` projection) would silently absorb the
18744        // "declared but degenerate" arm at the accessor boundary and
18745        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
18746        // coherence gate would silently accept a struct-literal
18747        // `Caixa` carrying the drift.
18748        //
18749        // Peer of the sibling
18750        // `declared_servico_slots_limits_arm_routes_through_accessor`
18751        // (b2bd9d7) and
18752        // `declared_servico_slots_behavior_arm_routes_through_accessor`
18753        // (35d8b52) composition pins on the sibling `:limits` /
18754        // `:behavior` outer-`Option<&Composite>` arms — same "the
18755        // enumerator gate must route through the substrate-primitive
18756        // typed dispatch" discipline extended onto the third M2
18757        // Servico-runtime slot axis, closing the enumerator's routing
18758        // invariant on every M2 arm.
18759        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18760        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18761            from: "0.0.1".into(),
18762            instructions: vec![UpgradeInstruction::Restart],
18763        }]);
18764        let slots = c.declared_servico_slots();
18765        assert!(
18766            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18767            "declared_servico_slots must push \
18768             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18769             non-empty — the accessor and the enumerator gate must \
18770             route through the same substrate-primitive typed \
18771             dispatch on the outer :upgrade-from presence bit (got \
18772             slots={slots:?})",
18773        );
18774        let c = caixa_with_upgrade_from(vec![]);
18775        let slots = c.declared_servico_slots();
18776        assert!(
18777            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18778            "declared_servico_slots must NOT push \
18779             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18780             empty — the author-omitted arm must route through the \
18781             accessor's empty-slice return unchanged (got \
18782             slots={slots:?})",
18783        );
18784    }
18785
18786    #[test]
18787    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
18788        // Composition pin: [`crate::render::servico_m2_overlay`]'s
18789        // per-`:upgrade-from` M2 overlay emit arm must key off
18790        // [`Caixa::upgrade_from`], not the raw
18791        // `!caixa.upgrade_from.is_empty()` presence gate + the
18792        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
18793        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
18794        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
18795        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
18796        // sequence in the overlay (the emitter fans onto the serde
18797        // slice-serialization), and a `Caixa { upgrade_from: vec![],
18798        // .. }` must omit the key entirely (the empty-slice
18799        // partition — the `!.is_empty()` outer gate elides the key
18800        // when the author omitted the slot). The pair jointly pins
18801        // the accessor + M2 overlay emitter composition: any future
18802        // silent detour that had the accessor return a fresh-cloned
18803        // `Vec<UpgradeFromEntry>` copy would silently break the
18804        // reference-identity pin the peer per-entry
18805        // `serde_yaml::to_value(caixa.upgrade_from())` projection
18806        // reads from — the projection would clone once per accessor
18807        // call instead of borrowing the storage buffer verbatim.
18808        //
18809        // Peer of the sibling
18810        // `servico_m2_overlay_limits_arm_routes_through_accessor`
18811        // (b2bd9d7) and
18812        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
18813        // (35d8b52) composition pins on the sibling `:limits` /
18814        // `:behavior` outer-`Option<&Composite>` arms — same "the
18815        // M2 overlay emitter must route through the substrate-
18816        // primitive typed dispatch" discipline extended onto the
18817        // third M2 Servico-runtime slot axis, closing the overlay
18818        // emitter's routing invariant on every M2 arm.
18819        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
18820        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18821        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18822            from: "0.0.1".into(),
18823            instructions: vec![UpgradeInstruction::Restart],
18824        }]);
18825        let overlay = servico_m2_overlay(&c).unwrap();
18826        assert!(
18827            overlay.contains_key(M2_KEY_UPGRADE_FROM),
18828            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
18829             `:upgrade-from` is non-empty — the accessor and the M2 \
18830             overlay emitter must route through the same substrate- \
18831             primitive typed dispatch on the outer :upgrade-from \
18832             slice (got overlay={overlay:?})",
18833        );
18834        let c = caixa_with_upgrade_from(vec![]);
18835        let overlay = servico_m2_overlay(&c).unwrap();
18836        assert!(
18837            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
18838            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
18839             `:upgrade-from` is empty — the empty-slice partition \
18840             must route through the accessor's empty-slice return \
18841             unchanged (got overlay={overlay:?})",
18842        );
18843    }
18844
18845    #[test]
18846    fn upgrade_from_projects_slice_by_borrow() {
18847        // The by-borrow pin: [`Caixa::upgrade_from`] returns
18848        // `&[UpgradeFromEntry]` by borrow — the returned slice
18849        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
18850        // the `:upgrade-from` slot and the accessor must not clone
18851        // the backing `Vec` on every call. Peer of the sibling
18852        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
18853        // (`autores_projects_slice_by_borrow` b5d813f,
18854        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18855        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18856        // `exe_projects_slice_by_borrow` 65d9527,
18857        // `servicos_projects_slice_by_borrow` 611f78b,
18858        // `deps_projects_slice_by_borrow` ad34b4e,
18859        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
18860        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
18861        // axes — extended here to the first outer-`Caixa`
18862        // composite-element `&[Composite]` axis: the accessor's
18863        // returned slice must borrow from `&self` (the returned
18864        // reference's lifetime is tied to `&self`), and calling the
18865        // accessor twice on the same [`Caixa`] must yield slices
18866        // that are pointer-equal (the underlying byte-buffer is the
18867        // storage `Vec`'s allocation, not a fresh copy) as well as
18868        // value-equal (idempotent, no side effects on `&self`).
18869        //
18870        // Pins against a future silent detour that returned an owned
18871        // `Vec<UpgradeFromEntry>` (which would type-check but
18872        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
18873        // return (which would leak the backing `Vec`'s
18874        // grow/push/reserve surface no downstream consumer reaches
18875        // for), or a one-arm-only accessor that returned a
18876        // saturating value on some sentinel input.
18877        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18878        for upgrade_from in [
18879            vec![],
18880            vec![UpgradeFromEntry {
18881                from: "0.0.1".into(),
18882                instructions: vec![UpgradeInstruction::Restart],
18883            }],
18884            vec![
18885                UpgradeFromEntry {
18886                    from: "0.0.1".into(),
18887                    instructions: vec![UpgradeInstruction::Restart],
18888                },
18889                UpgradeFromEntry {
18890                    from: "0.0.2".into(),
18891                    instructions: vec![UpgradeInstruction::SoftPurge {
18892                        module: "demo".into(),
18893                    }],
18894                },
18895            ],
18896        ] {
18897            let c = caixa_with_upgrade_from(upgrade_from.clone());
18898            let first = c.upgrade_from();
18899            let second = c.upgrade_from();
18900            assert_eq!(
18901                first, second,
18902                "Caixa::upgrade_from must be idempotent — two \
18903                 successive calls on the same &self must return the \
18904                 same &[UpgradeFromEntry]",
18905            );
18906            assert_eq!(
18907                first.as_ptr(),
18908                second.as_ptr(),
18909                "Caixa::upgrade_from must borrow the underlying \
18910                 Vec<UpgradeFromEntry> storage — two successive calls \
18911                 must return slices with the same backing pointer (a \
18912                 fresh Vec<UpgradeFromEntry> clone would change the \
18913                 pointer on every call)",
18914            );
18915            assert_eq!(
18916                first,
18917                upgrade_from.as_slice(),
18918                "Caixa::upgrade_from must return :upgrade-from \
18919                 verbatim by borrow — got {first:?}, expected \
18920                 {upgrade_from:?}",
18921            );
18922        }
18923    }
18924
18925    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
18926
18927    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
18928        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18929        c.children = children;
18930        c
18931    }
18932
18933    #[test]
18934    fn children_returns_children_slice_verbatim_across_permutations() {
18935        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
18936        // outer-composite `&[ChildSpec]`-return slice-shape pin:
18937        // [`Caixa::children`] must return the `:children` typed
18938        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
18939        // the same backing buffer the raw `self.children.as_slice()`
18940        // field access borrows from, element-equal across every
18941        // representative fixture in the accept-set — `[]` (the "no
18942        // static children declared" arm every non-`Supervisor`-kind
18943        // `defcaixa` carries by `#[serde(default)]` and every
18944        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
18945        // a canonical single-child `Permanent` fixture (the shape
18946        // most `OneForOne` supervisors carry — a single long-running
18947        // worker child), a canonical multi-child list carrying every
18948        // typed restart-policy variant (`Permanent` / `Transient` /
18949        // `Temporary`), and a past-the-guard sentinel — a duplicate
18950        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
18951        // ([`crate::SupervisorSpec::validate`] rejects through
18952        // `DuplicateChildNome { nome: "w" }` but the accessor must
18953        // ship the raw slot verbatim so struct-literal fixtures
18954        // continue to expose the duplicate at the accessor boundary).
18955        //
18956        // Pins against a future silent detour that returned an owned
18957        // `Vec<ChildSpec>` (which would type-check but silently clone
18958        // on every accessor call, breaking the zero-cost projection
18959        // every peer sibling slice accessor carries), a `[dup, dup] →
18960        // [dup]` dedup collapse (which would silently absorb the
18961        // `DuplicateChildNome` refusal case at the accessor boundary
18962        // and the [`crate::StandardLayout::verify`] cross-child gate
18963        // would silently accept a struct-literal `Caixa` carrying the
18964        // drift), a reference to an operator-resolved overlay (the
18965        // future per-cluster `:children-overrides` slot — its
18966        // resolution must land at exactly this accessor body, not
18967        // silently divert the raw slot away from a second consumer),
18968        // or an axis-shuffled projection (a future detour that
18969        // reordered children through the accessor would silently
18970        // split the paired [`crate::StandardLayout::verify`] per-
18971        // supervisor gate's traversal input from the peer
18972        // [`Self::supervisor_view`] fold-in path's clone-order input,
18973        // since the OTP `RestForOne` restart strategy dispatches on
18974        // declared child order and axis reordering would silently
18975        // split the operator's per-cluster restart-fan-out order
18976        // from the caixa.lisp source-order).
18977        //
18978        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
18979        // accessor pin on the substrate primitive for M2 / M3 typed-
18980        // slot vec-carry axes — folds on the outer-`Caixa`
18981        // `&[Composite]` composite-slice sub-family the sibling
18982        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18983        // (2a1f907) pin opened, peer at the outer altitude of the
18984        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
18985        // (bc92bce) accessor on the same OTP-supervisor static-child-
18986        // list axis.
18987        use crate::supervisor::{ChildSpec, RestartPolicy};
18988        let fixtures: Vec<Vec<ChildSpec>> = vec![
18989            vec![],
18990            vec![ChildSpec {
18991                caixa: "worker".into(),
18992                versao: "^0.1".into(),
18993                restart: RestartPolicy::Permanent,
18994            }],
18995            vec![
18996                ChildSpec {
18997                    caixa: "worker-a".into(),
18998                    versao: "^0.1".into(),
18999                    restart: RestartPolicy::Permanent,
19000                },
19001                ChildSpec {
19002                    caixa: "worker-b".into(),
19003                    versao: "^0.1".into(),
19004                    restart: RestartPolicy::Transient,
19005                },
19006                ChildSpec {
19007                    caixa: "worker-c".into(),
19008                    versao: "^0.1".into(),
19009                    restart: RestartPolicy::Temporary,
19010                },
19011            ],
19012            vec![
19013                ChildSpec {
19014                    caixa: "w".into(),
19015                    versao: "^0.1".into(),
19016                    restart: RestartPolicy::Permanent,
19017                },
19018                ChildSpec {
19019                    caixa: "w".into(),
19020                    versao: "^0.1".into(),
19021                    restart: RestartPolicy::Permanent,
19022                },
19023            ],
19024        ];
19025        for children in fixtures {
19026            let c = caixa_with_children(children.clone());
19027            assert_eq!(
19028                c.children(),
19029                children.as_slice(),
19030                "Caixa::children must return :children verbatim \
19031                 (got {:?}, expected {children:?})",
19032                c.children(),
19033            );
19034            assert_eq!(
19035                c.children(),
19036                c.children.as_slice(),
19037                "Caixa::children must element-equal the raw \
19038                 `self.children.as_slice()` field access across \
19039                 every value in the Vec<ChildSpec> accept-set",
19040            );
19041            assert_eq!(
19042                c.children().is_empty(),
19043                c.children.is_empty(),
19044                "Caixa::children().is_empty() must byte-equal \
19045                 self.children.is_empty() — a presence-bit drift \
19046                 would silently split the paired \
19047                 Caixa::declared_supervisor_slots supervisor-tree \
19048                 declared-slot enumerator's presence probe from the \
19049                 peer Caixa::supervisor_view typed-view composer's \
19050                 fold-in path",
19051            );
19052        }
19053    }
19054
19055    #[test]
19056    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
19057        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
19058        // `:children` presence-probe arm must key off
19059        // [`Caixa::children`], not the raw
19060        // `!self.children.is_empty()` field-probe. Structurally: a
19061        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
19062        // "^0.1", restart: Permanent }], .. }` must push
19063        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
19064        // (the presence bit is non-empty, so the supervisor-tree
19065        // kind-coherence gate must surface the slot as "declared"),
19066        // and a `Caixa { children: vec![], .. }` must NOT push the
19067        // label (the "author omitted the slot entirely" arm — the
19068        // empty-slice partition the serde-default folds onto). The
19069        // pair jointly pins the accessor + declared-slot enumerator
19070        // composition: any future silent detour that had the accessor
19071        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
19072        // "__reserved__")` projection) would silently absorb the
19073        // "declared but degenerate" arm at the accessor boundary and
19074        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
19075        // kind-coherence gate would silently accept a struct-literal
19076        // `Caixa` carrying the drift.
19077        //
19078        // Peer of the sibling
19079        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19080        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
19081        // same "the enumerator gate must route through the substrate-
19082        // primitive typed dispatch" discipline extended onto the
19083        // supervisor-tree `:children` composite-slice arm.
19084        use crate::supervisor::{ChildSpec, RestartPolicy};
19085        let c = caixa_with_children(vec![ChildSpec {
19086            caixa: "w".into(),
19087            versao: "^0.1".into(),
19088            restart: RestartPolicy::Permanent,
19089        }]);
19090        let slots = c.declared_supervisor_slots();
19091        assert!(
19092            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
19093            "declared_supervisor_slots must push \
19094             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
19095             non-empty — the accessor and the enumerator gate must \
19096             route through the same substrate-primitive typed \
19097             dispatch on the outer :children presence bit (got \
19098             slots={slots:?})",
19099        );
19100        let c = caixa_with_children(vec![]);
19101        let slots = c.declared_supervisor_slots();
19102        assert!(
19103            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
19104            "declared_supervisor_slots must NOT push \
19105             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
19106             empty — the author-omitted arm must route through the \
19107             accessor's empty-slice return unchanged (got \
19108             slots={slots:?})",
19109        );
19110    }
19111
19112    #[test]
19113    fn supervisor_view_children_arm_routes_through_accessor() {
19114        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
19115        // fold-in arm must key off [`Caixa::children`], not the raw
19116        // `self.children.clone()` field-clone. Structurally: a `Caixa {
19117        // kind: Supervisor, estrategia: Some(OneForOne), children:
19118        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
19119        // per-child list through the accessor into the typed
19120        // [`SupervisorSpec`] view's `children` field verbatim — every
19121        // entry the accessor surfaces must land in the view's
19122        // `children` slot in the same order. The pair jointly pins the
19123        // accessor + view-composer composition: any future silent
19124        // detour that had the accessor return a fresh-cloned
19125        // `Vec<ChildSpec>` copy would silently break the reference-
19126        // identity pin the peer `supervisor_view` fold-in path reads
19127        // from — the fold would clone once more per accessor call
19128        // instead of borrowing the storage buffer verbatim once.
19129        //
19130        // Peer of the sibling
19131        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
19132        // family) composition pin on the peer kind-gate arm — same
19133        // "the view composer must route through the substrate-
19134        // primitive typed dispatch" discipline extended onto the
19135        // per-`:children` fold-in arm, closing the supervisor-view
19136        // composer's routing invariant on the composite-slice input.
19137        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19138        let mut c = caixa_with_children(vec![
19139            ChildSpec {
19140                caixa: "worker-a".into(),
19141                versao: "^0.1".into(),
19142                restart: RestartPolicy::Permanent,
19143            },
19144            ChildSpec {
19145                caixa: "worker-b".into(),
19146                versao: "^0.1".into(),
19147                restart: RestartPolicy::Transient,
19148            },
19149        ]);
19150        c.kind = crate::CaixaKind::Supervisor;
19151        c.estrategia = Some(RestartStrategy::OneForOne);
19152        let view = c
19153            .supervisor_view()
19154            .expect("Supervisor kind must produce a supervisor_view");
19155        assert_eq!(
19156            view.children(),
19157            c.children(),
19158            "supervisor_view must fold Caixa::children verbatim into \
19159             SupervisorSpec::children — the accessor and the view \
19160             composer must route through the same substrate-primitive \
19161             typed dispatch on the outer :children slice (got view \
19162             children={:?}, expected {:?})",
19163            view.children(),
19164            c.children(),
19165        );
19166    }
19167
19168    #[test]
19169    fn children_projects_slice_by_borrow() {
19170        // The by-borrow pin: [`Caixa::children`] returns
19171        // `&[ChildSpec]` by borrow — the returned slice borrows the
19172        // underlying `Vec<ChildSpec>` storage of the `:children` slot
19173        // and the accessor must not clone the backing `Vec` on every
19174        // call. Peer of the sibling outer top-level [`Caixa`]
19175        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
19176        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
19177        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19178        // `exe_projects_slice_by_borrow` 65d9527,
19179        // `servicos_projects_slice_by_borrow` 611f78b,
19180        // `deps_projects_slice_by_borrow` ad34b4e,
19181        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19182        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
19183        // sibling outer top-level [`Caixa`] scalar-element and
19184        // composite-element `&[T]` axes — folds on the outer-`Caixa`
19185        // composite-element `&[Composite]` axis: the accessor's
19186        // returned slice must borrow from `&self` (the returned
19187        // reference's lifetime is tied to `&self`), and calling the
19188        // accessor twice on the same [`Caixa`] must yield slices
19189        // that are pointer-equal (the underlying byte-buffer is the
19190        // storage `Vec`'s allocation, not a fresh copy) as well as
19191        // value-equal (idempotent, no side effects on `&self`).
19192        //
19193        // Pins against a future silent detour that returned an owned
19194        // `Vec<ChildSpec>` (which would type-check but silently clone
19195        // on every call), a `&Vec<ChildSpec>` return (which would leak
19196        // the backing `Vec`'s grow/push/reserve surface no downstream
19197        // consumer reaches for), or a one-arm-only accessor that
19198        // returned a saturating value on some sentinel input.
19199        use crate::supervisor::{ChildSpec, RestartPolicy};
19200        for children in [
19201            vec![],
19202            vec![ChildSpec {
19203                caixa: "w".into(),
19204                versao: "^0.1".into(),
19205                restart: RestartPolicy::Permanent,
19206            }],
19207            vec![
19208                ChildSpec {
19209                    caixa: "worker-a".into(),
19210                    versao: "^0.1".into(),
19211                    restart: RestartPolicy::Permanent,
19212                },
19213                ChildSpec {
19214                    caixa: "worker-b".into(),
19215                    versao: "^0.1".into(),
19216                    restart: RestartPolicy::Transient,
19217                },
19218            ],
19219        ] {
19220            let c = caixa_with_children(children.clone());
19221            let first = c.children();
19222            let second = c.children();
19223            assert_eq!(
19224                first, second,
19225                "Caixa::children must be idempotent — two successive \
19226                 calls on the same &self must return the same \
19227                 &[ChildSpec]",
19228            );
19229            assert_eq!(
19230                first.as_ptr(),
19231                second.as_ptr(),
19232                "Caixa::children must borrow the underlying \
19233                 Vec<ChildSpec> storage — two successive calls must \
19234                 return slices with the same backing pointer (a fresh \
19235                 Vec<ChildSpec> clone would change the pointer on \
19236                 every call)",
19237            );
19238            assert_eq!(
19239                first,
19240                children.as_slice(),
19241                "Caixa::children must return :children verbatim by \
19242                 borrow — got {first:?}, expected {children:?}",
19243            );
19244        }
19245    }
19246
19247    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
19248
19249    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
19250        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19251        c.kind = CaixaKind::Aplicacao;
19252        c.membros = membros;
19253        c
19254    }
19255
19256    #[test]
19257    fn membros_returns_membros_slice_verbatim_across_permutations() {
19258        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
19259        // composite `&[Membro]`-return slice-shape pin:
19260        // [`Caixa::membros`] must return the `:membros` typed
19261        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
19262        // same backing buffer the raw `self.membros.as_slice()` field
19263        // access borrows from, element-equal across every
19264        // representative fixture in the accept-set — `[]` (the "no
19265        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
19266        // carries by `#[serde(default)]` and every partially-authored
19267        // Aplicacao carries before the
19268        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
19269        // canonical single-member fixture (the shape a minimal
19270        // Aplicacao carries — one Servico wrapping one contained
19271        // computation), a canonical multi-member list carrying three
19272        // distinct entries (the canonical checkout-shape Aplicacao —
19273        // cart / pricing / auth — every canonical example carries), and
19274        // a past-the-guard sentinel — a duplicate `:caixa`
19275        // `[("cart", ...), ("cart", ...)]` entry pair
19276        // ([`crate::AplicacaoSpec::validate`] rejects through
19277        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
19278        // the raw slot verbatim so struct-literal fixtures continue to
19279        // expose the duplicate at the accessor boundary).
19280        //
19281        // Pins against a future silent detour that returned an owned
19282        // `Vec<Membro>` (which would type-check but silently clone on
19283        // every accessor call, breaking the zero-cost projection every
19284        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
19285        // dedup collapse (which would silently absorb the
19286        // `DuplicateMembro` refusal case at the accessor boundary and
19287        // the [`crate::StandardLayout::verify`] cross-member gate would
19288        // silently accept a struct-literal `Caixa` carrying the drift),
19289        // a reference to an operator-resolved overlay (the future per-
19290        // cluster `:membros-overrides` slot — its resolution must land
19291        // at exactly this accessor body, not silently divert the raw
19292        // slot away from a second consumer), or an axis-shuffled
19293        // projection (a future detour that reordered members through
19294        // the accessor would silently split the paired
19295        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19296        // traversal input from the peer [`Self::aplicacao_view`] fold-
19297        // in path's clone-order input, since the canonical `:contratos`
19298        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
19299        // read the member set through the same slice).
19300        //
19301        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
19302        // accessor pin on the substrate primitive for M2 / M3 typed-
19303        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
19304        // arm of the `&[Composite]` composite-slice sub-family the
19305        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19306        // (2a1f907) and
19307        // `children_returns_children_slice_verbatim_across_permutations`
19308        // (c17b51e) pins opened, peer at the outer altitude of the
19309        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
19310        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
19311        // list axis.
19312        use crate::aplicacao::Membro;
19313        let fixtures: Vec<Vec<Membro>> = vec![
19314            vec![],
19315            vec![Membro {
19316                caixa: "cart".into(),
19317                versao: "^0.1".into(),
19318            }],
19319            vec![
19320                Membro {
19321                    caixa: "cart".into(),
19322                    versao: "^0.1".into(),
19323                },
19324                Membro {
19325                    caixa: "pricing".into(),
19326                    versao: "^0.2".into(),
19327                },
19328                Membro {
19329                    caixa: "auth".into(),
19330                    versao: "^1.0".into(),
19331                },
19332            ],
19333            vec![
19334                Membro {
19335                    caixa: "cart".into(),
19336                    versao: "^0.1".into(),
19337                },
19338                Membro {
19339                    caixa: "cart".into(),
19340                    versao: "^0.1".into(),
19341                },
19342            ],
19343        ];
19344        for membros in fixtures {
19345            let c = caixa_aplicacao_with_membros(membros.clone());
19346            assert_eq!(
19347                c.membros(),
19348                membros.as_slice(),
19349                "Caixa::membros must return :membros verbatim \
19350                 (got {:?}, expected {membros:?})",
19351                c.membros(),
19352            );
19353            assert_eq!(
19354                c.membros(),
19355                c.membros.as_slice(),
19356                "Caixa::membros must element-equal the raw \
19357                 `self.membros.as_slice()` field access across every \
19358                 value in the Vec<Membro> accept-set",
19359            );
19360            assert_eq!(
19361                c.membros().is_empty(),
19362                c.membros.is_empty(),
19363                "Caixa::membros().is_empty() must byte-equal \
19364                 self.membros.is_empty() — a presence-bit drift would \
19365                 silently split the paired Caixa::declared_mesh_slots \
19366                 mesh declared-slot enumerator's presence probe from \
19367                 the peer Caixa::aplicacao_view typed-view composer's \
19368                 fold-in path",
19369            );
19370        }
19371    }
19372
19373    #[test]
19374    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
19375        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
19376        // presence-probe arm must key off [`Caixa::membros`], not the
19377        // raw `!self.membros.is_empty()` field-probe. Structurally: a
19378        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
19379        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
19380        // declared-slot list (the presence bit is non-empty, so the
19381        // mesh kind-coherence gate must surface the slot as
19382        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
19383        // push the label (the "author omitted the slot entirely" arm
19384        // — the empty-slice partition the serde-default folds onto).
19385        // The pair jointly pins the accessor + declared-slot
19386        // enumerator composition: any future silent detour that had
19387        // the accessor collapse `[Membro { .. }]` to `[]` (a
19388        // `.filter(|m| m.nome() != "__reserved__")` projection) would
19389        // silently absorb the "declared but degenerate" arm at the
19390        // accessor boundary and the
19391        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19392        // coherence gate would silently accept a struct-literal
19393        // `Caixa` carrying the drift.
19394        //
19395        // Peer of the sibling
19396        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19397        // (2a1f907) and
19398        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19399        // (c17b51e) composition pins on the M2 `:upgrade-from` /
19400        // `:children` composite-slice arms — same "the enumerator gate
19401        // must route through the substrate-primitive typed dispatch"
19402        // discipline extended onto the M3 `:membros` composite-slice
19403        // arm, opening the M3 arm of the declared-slot enumerator's
19404        // routing invariant.
19405        use crate::aplicacao::Membro;
19406        let c = caixa_aplicacao_with_membros(vec![Membro {
19407            caixa: "cart".into(),
19408            versao: "^0.1".into(),
19409        }]);
19410        let slots = c.declared_mesh_slots();
19411        assert!(
19412            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19413            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
19414             `:membros` is non-empty — the accessor and the enumerator \
19415             gate must route through the same substrate-primitive \
19416             typed dispatch on the outer :membros presence bit (got \
19417             slots={slots:?})",
19418        );
19419        let c = caixa_aplicacao_with_membros(vec![]);
19420        let slots = c.declared_mesh_slots();
19421        assert!(
19422            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19423            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
19424             when `:membros` is empty — the author-omitted arm must \
19425             route through the accessor's empty-slice return unchanged \
19426             (got slots={slots:?})",
19427        );
19428    }
19429
19430    #[test]
19431    fn aplicacao_view_membros_arm_routes_through_accessor() {
19432        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
19433        // fold-in arm must key off [`Caixa::membros`], not the raw
19434        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
19435        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
19436        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
19437        // member list through the accessor into the typed
19438        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
19439        // every entry the accessor surfaces must land in the view's
19440        // `membros` slot in the same order. The pair jointly pins the
19441        // accessor + view-composer composition: any future silent
19442        // detour that had the accessor return a fresh-cloned
19443        // `Vec<Membro>` copy would silently break the reference-
19444        // identity pin the peer `aplicacao_view` fold-in path reads
19445        // from — the fold would clone once more per accessor call
19446        // instead of borrowing the storage buffer verbatim once.
19447        //
19448        // Peer of the sibling
19449        // `aplicacao_view_politicas_arm_folds_through_accessor`
19450        // (5d23d29) /
19451        // `aplicacao_view_placement_arm_folds_through_accessor`
19452        // (4fb8074) /
19453        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
19454        // composition pins on the M3 `:politicas` / `:placement` /
19455        // `:entrada` outer-`Option<&Composite>` arms — extended here to
19456        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
19457        // closing the aplicacao-view composer's routing invariant on
19458        // the composite-slice input.
19459        use crate::aplicacao::Membro;
19460        let c = caixa_aplicacao_with_membros(vec![
19461            Membro {
19462                caixa: "cart".into(),
19463                versao: "^0.1".into(),
19464            },
19465            Membro {
19466                caixa: "pricing".into(),
19467                versao: "^0.2".into(),
19468            },
19469        ]);
19470        let view = c
19471            .aplicacao_view()
19472            .expect("Aplicacao kind must produce an aplicacao_view");
19473        assert_eq!(
19474            view.membros(),
19475            c.membros(),
19476            "aplicacao_view must fold Caixa::membros verbatim into \
19477             AplicacaoSpec::membros — the accessor and the view \
19478             composer must route through the same substrate-primitive \
19479             typed dispatch on the outer :membros slice (got view \
19480             membros={:?}, expected {:?})",
19481            view.membros(),
19482            c.membros(),
19483        );
19484    }
19485
19486    #[test]
19487    fn membros_projects_slice_by_borrow() {
19488        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
19489        // borrow — the returned slice borrows the underlying
19490        // `Vec<Membro>` storage of the `:membros` slot and the
19491        // accessor must not clone the backing `Vec` on every call.
19492        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19493        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19494        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19495        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19496        // `exe_projects_slice_by_borrow` 65d9527,
19497        // `servicos_projects_slice_by_borrow` 611f78b,
19498        // `deps_projects_slice_by_borrow` ad34b4e,
19499        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19500        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19501        // `children_projects_slice_by_borrow` c17b51e) on the sibling
19502        // outer top-level [`Caixa`] scalar-element and composite-
19503        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
19504        // slot composite-element `&[Composite]` axis: the accessor's
19505        // returned slice must borrow from `&self` (the returned
19506        // reference's lifetime is tied to `&self`), and calling the
19507        // accessor twice on the same [`Caixa`] must yield slices that
19508        // are pointer-equal (the underlying byte-buffer is the storage
19509        // `Vec`'s allocation, not a fresh copy) as well as value-equal
19510        // (idempotent, no side effects on `&self`).
19511        //
19512        // Pins against a future silent detour that returned an owned
19513        // `Vec<Membro>` (which would type-check but silently clone on
19514        // every call), a `&Vec<Membro>` return (which would leak the
19515        // backing `Vec`'s grow/push/reserve surface no downstream
19516        // consumer reaches for), or a one-arm-only accessor that
19517        // returned a saturating value on some sentinel input.
19518        use crate::aplicacao::Membro;
19519        for membros in [
19520            vec![],
19521            vec![Membro {
19522                caixa: "cart".into(),
19523                versao: "^0.1".into(),
19524            }],
19525            vec![
19526                Membro {
19527                    caixa: "cart".into(),
19528                    versao: "^0.1".into(),
19529                },
19530                Membro {
19531                    caixa: "pricing".into(),
19532                    versao: "^0.2".into(),
19533                },
19534            ],
19535        ] {
19536            let c = caixa_aplicacao_with_membros(membros.clone());
19537            let first = c.membros();
19538            let second = c.membros();
19539            assert_eq!(
19540                first, second,
19541                "Caixa::membros must be idempotent — two successive \
19542                 calls on the same &self must return the same &[Membro]",
19543            );
19544            assert_eq!(
19545                first.as_ptr(),
19546                second.as_ptr(),
19547                "Caixa::membros must borrow the underlying Vec<Membro> \
19548                 storage — two successive calls must return slices with \
19549                 the same backing pointer (a fresh Vec<Membro> clone \
19550                 would change the pointer on every call)",
19551            );
19552            assert_eq!(
19553                first,
19554                membros.as_slice(),
19555                "Caixa::membros must return :membros verbatim by borrow \
19556                 — got {first:?}, expected {membros:?}",
19557            );
19558        }
19559    }
19560
19561    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
19562
19563    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
19564        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19565        c.kind = CaixaKind::Aplicacao;
19566        c.contratos = contratos;
19567        c
19568    }
19569
19570    fn contrato_http_for_test(
19571        de: &str,
19572        para: &str,
19573        endpoint: &str,
19574    ) -> crate::aplicacao::WitContract {
19575        crate::aplicacao::WitContract {
19576            de: de.into(),
19577            para: para.into(),
19578            wit: "wasi:http/proxy".into(),
19579            endpoint: Some(endpoint.into()),
19580            subject: None,
19581            slot: None,
19582        }
19583    }
19584
19585    #[test]
19586    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
19587        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
19588        // composite `&[WitContract]`-return slice-shape pin:
19589        // [`Caixa::contratos`] must return the `:contratos` typed
19590        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
19591        // over the same backing buffer the raw
19592        // `self.contratos.as_slice()` field access borrows from,
19593        // element-equal across every representative fixture in the
19594        // accept-set — `[]` (the "no contracts declared" arm every
19595        // non-`Aplicacao`-kind `defcaixa` carries by
19596        // `#[serde(default)]` and every leaf-Aplicacao with a single
19597        // member carries), a canonical single-edge fixture (the
19598        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
19599        // edge), and a canonical multi-edge fixture with three distinct
19600        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
19601        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
19602        //
19603        // Pins against a future silent detour that returned an owned
19604        // `Vec<WitContract>` (which would type-check but silently clone
19605        // on every accessor call, breaking the zero-cost projection
19606        // every peer sibling slice accessor carries), an axis-shuffled
19607        // projection (a future detour that reordered edges through the
19608        // accessor would silently split the paired
19609        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19610        // traversal input from the peer [`Self::aplicacao_view`] fold-
19611        // in path's clone-order input, since every canonical
19612        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
19613        // seed dispatch reads the edge set through the same slice),
19614        // or a reference to an operator-resolved overlay (the future
19615        // per-cluster `:contratos-overrides` slot — its resolution
19616        // must land at exactly this accessor body, not silently divert
19617        // the raw slot away from a second consumer).
19618        //
19619        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
19620        // accessor pin on the substrate primitive for M2 / M3 typed-
19621        // slot vec-carry axes — closes the outer-`Caixa`
19622        // `&[Composite]` composite-slice sub-family the sibling M2
19623        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19624        // (2a1f907) and
19625        // `children_returns_children_slice_verbatim_across_permutations`
19626        // (c17b51e) pins opened and the M3
19627        // `membros_returns_membros_slice_verbatim_across_permutations`
19628        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
19629        // slot arm of the composite-slice sub-family. Peer at the outer
19630        // altitude of the closed inner-
19631        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
19632        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
19633        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
19634            vec![],
19635            vec![contrato_http_for_test("cart", "catalog", "/items")],
19636            vec![
19637                contrato_http_for_test("cart", "catalog", "/items"),
19638                contrato_http_for_test("cart", "pricing", "/price"),
19639                contrato_http_for_test("cart", "auth", "/whoami"),
19640            ],
19641        ];
19642        for contratos in fixtures {
19643            let c = caixa_aplicacao_with_contratos(contratos.clone());
19644            assert_eq!(
19645                c.contratos(),
19646                contratos.as_slice(),
19647                "Caixa::contratos must return :contratos verbatim \
19648                 (got {:?}, expected {contratos:?})",
19649                c.contratos(),
19650            );
19651            assert_eq!(
19652                c.contratos(),
19653                c.contratos.as_slice(),
19654                "Caixa::contratos must element-equal the raw \
19655                 `self.contratos.as_slice()` field access across every \
19656                 value in the Vec<WitContract> accept-set",
19657            );
19658            assert_eq!(
19659                c.contratos().is_empty(),
19660                c.contratos.is_empty(),
19661                "Caixa::contratos().is_empty() must byte-equal \
19662                 self.contratos.is_empty() — a presence-bit drift would \
19663                 silently split the paired Caixa::declared_mesh_slots \
19664                 mesh declared-slot enumerator's presence probe from \
19665                 the peer Caixa::aplicacao_view typed-view composer's \
19666                 fold-in path",
19667            );
19668        }
19669    }
19670
19671    #[test]
19672    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
19673        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
19674        // presence-probe arm must key off [`Caixa::contratos`], not the
19675        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
19676        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
19677        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
19678        // presence bit is non-empty, so the mesh kind-coherence gate
19679        // must surface the slot as "declared"), and a `Caixa {
19680        // contratos: vec![], .. }` must NOT push the label (the "author
19681        // omitted the slot entirely" arm — the empty-slice partition
19682        // the serde-default folds onto). The pair jointly pins the
19683        // accessor + declared-slot enumerator composition: any future
19684        // silent detour that had the accessor collapse
19685        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
19686        // "__reserved__")` projection) would silently absorb the
19687        // "declared but degenerate" arm at the accessor boundary and
19688        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19689        // coherence gate would silently accept a struct-literal
19690        // `Caixa` carrying the drift.
19691        //
19692        // Peer of the sibling
19693        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19694        // (2a1f907),
19695        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19696        // (c17b51e), and
19697        // `declared_mesh_slots_membros_arm_routes_through_accessor`
19698        // (0f26987) composition pins on the M2 `:upgrade-from` /
19699        // `:children` / M3 `:membros` composite-slice arms — same "the
19700        // enumerator gate must route through the substrate-primitive
19701        // typed dispatch" discipline extended onto the M3 `:contratos`
19702        // composite-slice arm, closing the M3 mesh-slot arm of the
19703        // declared-slot enumerator's routing invariant on the
19704        // composite-slice inputs.
19705        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
19706            "cart", "catalog", "/items",
19707        )]);
19708        let slots = c.declared_mesh_slots();
19709        assert!(
19710            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19711            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
19712             `:contratos` is non-empty — the accessor and the enumerator \
19713             gate must route through the same substrate-primitive \
19714             typed dispatch on the outer :contratos presence bit (got \
19715             slots={slots:?})",
19716        );
19717        let c = caixa_aplicacao_with_contratos(vec![]);
19718        let slots = c.declared_mesh_slots();
19719        assert!(
19720            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19721            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
19722             when `:contratos` is empty — the author-omitted arm must \
19723             route through the accessor's empty-slice return unchanged \
19724             (got slots={slots:?})",
19725        );
19726    }
19727
19728    #[test]
19729    fn aplicacao_view_contratos_arm_routes_through_accessor() {
19730        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
19731        // fold-in arm must key off [`Caixa::contratos`], not the raw
19732        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
19733        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
19734        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
19735        // per-edge list through the accessor into the typed
19736        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
19737        // every entry the accessor surfaces must land in the view's
19738        // `contratos` slot in the same order. The pair jointly pins
19739        // the accessor + view-composer composition: a future silent
19740        // detour that had the accessor shuffle or drop an edge would
19741        // silently split the paired declared-slot enumerator's
19742        // presence bit from the typed-view composer's edge-list, a
19743        // two-consumer split at the enumerator and the view composer
19744        // far from the source `caixa.lisp`.
19745        //
19746        // Peer of the sibling
19747        // `aplicacao_view_membros_arm_routes_through_accessor`
19748        // (0f26987) composition pin on the M3 `:membros` outer-
19749        // `&[Composite]` composite-slice arm, closing the aplicacao-
19750        // view composer's routing invariant on the composite-slice
19751        // inputs at the outer altitude.
19752        let c = caixa_aplicacao_with_contratos(vec![
19753            contrato_http_for_test("cart", "catalog", "/items"),
19754            contrato_http_for_test("cart", "pricing", "/price"),
19755        ]);
19756        let view = c
19757            .aplicacao_view()
19758            .expect("Aplicacao kind must produce an aplicacao_view");
19759        assert_eq!(
19760            view.contratos(),
19761            c.contratos(),
19762            "aplicacao_view must fold Caixa::contratos verbatim into \
19763             AplicacaoSpec::contratos — the accessor and the view \
19764             composer must route through the same substrate-primitive \
19765             typed dispatch on the outer :contratos slice (got view \
19766             contratos={:?}, expected {:?})",
19767            view.contratos(),
19768            c.contratos(),
19769        );
19770    }
19771
19772    #[test]
19773    fn contratos_projects_slice_by_borrow() {
19774        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
19775        // by borrow — the returned slice borrows the underlying
19776        // `Vec<WitContract>` storage of the `:contratos` slot and the
19777        // accessor must not clone the backing `Vec` on every call.
19778        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19779        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19780        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19781        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19782        // `exe_projects_slice_by_borrow` 65d9527,
19783        // `servicos_projects_slice_by_borrow` 611f78b,
19784        // `deps_projects_slice_by_borrow` ad34b4e,
19785        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19786        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19787        // `children_projects_slice_by_borrow` c17b51e,
19788        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
19789        // outer top-level [`Caixa`] scalar-element and composite-
19790        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
19791        // composite-element `&[Composite]` axis on the by-borrow pin:
19792        // the accessor's returned slice must borrow from `&self` (the
19793        // returned reference's lifetime is tied to `&self`), and
19794        // calling the accessor twice on the same [`Caixa`] must yield
19795        // slices that are pointer-equal (the underlying byte-buffer is
19796        // the storage `Vec`'s allocation, not a fresh copy) as well as
19797        // value-equal (idempotent, no side effects on `&self`).
19798        //
19799        // Pins against a future silent detour that returned an owned
19800        // `Vec<WitContract>` (which would type-check but silently clone
19801        // on every call), a `&Vec<WitContract>` return (which would
19802        // leak the backing `Vec`'s grow/push/reserve surface no
19803        // downstream consumer reaches for), or a one-arm-only accessor
19804        // that returned a saturating value on some sentinel input.
19805        for contratos in [
19806            vec![],
19807            vec![contrato_http_for_test("cart", "catalog", "/items")],
19808            vec![
19809                contrato_http_for_test("cart", "catalog", "/items"),
19810                contrato_http_for_test("cart", "pricing", "/price"),
19811            ],
19812        ] {
19813            let c = caixa_aplicacao_with_contratos(contratos.clone());
19814            let first = c.contratos();
19815            let second = c.contratos();
19816            assert_eq!(
19817                first, second,
19818                "Caixa::contratos must be idempotent — two successive \
19819                 calls on the same &self must return the same \
19820                 &[WitContract]",
19821            );
19822            assert_eq!(
19823                first.as_ptr(),
19824                second.as_ptr(),
19825                "Caixa::contratos must borrow the underlying \
19826                 Vec<WitContract> storage — two successive calls must \
19827                 return slices with the same backing pointer (a fresh \
19828                 Vec<WitContract> clone would change the pointer on \
19829                 every call)",
19830            );
19831            assert_eq!(
19832                first,
19833                contratos.as_slice(),
19834                "Caixa::contratos must return :contratos verbatim by \
19835                 borrow — got {first:?}, expected {contratos:?}",
19836            );
19837        }
19838    }
19839
19840    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
19841
19842    #[test]
19843    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
19844        // Load-bearing invariant: every multi-word top-level [`Caixa`]
19845        // serde-derived JSON key routes through a lifted `&'static str`
19846        // const. The Rust field names are `snake_case`
19847        // (`deps_dev` / `upgrade_from` / `max_restarts` /
19848        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
19849        // "camelCase")]` derive attribute maps each to the camelCase
19850        // byte-string the [`Caixa::to_lisp`] round-trip's
19851        // `serde_json::to_value(self)` step lands under before
19852        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
19853        // to the kebab-case `:deps-dev` / `:upgrade-from` /
19854        // `:max-restarts` / `:restart-window` author surface. Serialize
19855        // a fully-populated [`Caixa`] and pin that each canonical
19856        // byte-sequence appears verbatim in the JSON — a future
19857        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
19858        // verbatim-field-name flip at the derive attribute (any of
19859        // which would silently break every [`Caixa::to_lisp`]
19860        // round-trip and the future M4 operator-side manifest ingest's
19861        // `Value::get(<key>)` navigation) surfaces here as a build-time
19862        // test failure at `manifest.rs`, not as an apply-time
19863        // `.get(<stale-canonical-const>)` returning `None` far from the
19864        // derive-attr drift's commit. Same discipline the sibling
19865        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
19866        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
19867        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
19868        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
19869        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
19870        // [`UpgradeFromEntry`] per-entry axes — extended here to the
19871        // enclosing M0 [`Caixa`] top-level axis so the last of the four
19872        // multi-word top-level [`Caixa`] serde-derived JSON keys
19873        // (`depsDev`) joins the substrate's "one canonical byte-string
19874        // per typed serialized-key axis" discipline.
19875        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19876        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
19877        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19878        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
19879        c.upgrade_from = vec![UpgradeFromEntry {
19880            from: "0.0.1".into(),
19881            instructions: vec![UpgradeInstruction::Restart],
19882        }];
19883        c.estrategia = Some(RestartStrategy::OneForOne);
19884        c.max_restarts = Some(3);
19885        c.restart_window = Some("60s".into());
19886        c.children = vec![ChildSpec {
19887            caixa: "child".into(),
19888            versao: "^0.1".into(),
19889            restart: RestartPolicy::Permanent,
19890        }];
19891        let json = serde_json::to_string(&c).unwrap();
19892        for key in [
19893            crate::render::CAIXA_KEY_DEPS_DEV,
19894            crate::render::M2_KEY_UPGRADE_FROM,
19895            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19896            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19897        ] {
19898            let quoted = format!("\"{key}\"");
19899            assert!(
19900                json.contains(&quoted),
19901                "serialized Caixa must carry the lifted top-level \
19902                 multi-word byte-sequence {quoted} verbatim in the JSON \
19903                 emission (got: {json})",
19904            );
19905        }
19906    }
19907
19908    #[test]
19909    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
19910        // Cross-axis drift-detection pin: a future collapse of the four
19911        // canonical [`Caixa`] top-level multi-word byte-strings onto the
19912        // same value (e.g. an accidental copy-paste flip of
19913        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
19914        // `"upgradeFrom"`) would silently reroute every downstream
19915        // `Value::get(<key>)` probe on one axis onto the sibling axis's
19916        // top-level entry and pass every propagation-probe test that
19917        // expected only the stale axis's value. Peer of the sibling
19918        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
19919        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
19920        let all = [
19921            crate::render::CAIXA_KEY_DEPS_DEV,
19922            crate::render::M2_KEY_UPGRADE_FROM,
19923            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19924            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19925        ];
19926        for (i, a) in all.iter().enumerate() {
19927            for b in all.iter().skip(i + 1) {
19928                assert_ne!(
19929                    a, b,
19930                    "Caixa top-level multi-word key consts must be \
19931                     pairwise-distinct canonical byte-sequences — got \
19932                     `{a}` == `{b}`",
19933                );
19934            }
19935        }
19936    }
19937
19938    #[test]
19939    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
19940        // Shape-pin: every [`Caixa`] top-level multi-word key const must
19941        // be a lowerCamelCase byte-sequence (no `snake_case`
19942        // underscores, no `kebab-case` hyphens, no leading colon, no
19943        // `PascalCase` leading capital, no whitespace / dots) — the
19944        // canonical shape the `#[serde(rename_all = "camelCase")]`
19945        // derive produces on [`Caixa`]. A future flip to a
19946        // non-camelCase attribute at the derive surfaces both here
19947        // (this test fails on the stale-constant shape) and at
19948        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19949        // (that test fails on the mismatch between const and derive).
19950        // Peer with `membro_key_consts_are_lower_camel_case_shape`
19951        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
19952        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
19953        for key in [
19954            crate::render::CAIXA_KEY_DEPS_DEV,
19955            crate::render::M2_KEY_UPGRADE_FROM,
19956            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19957            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19958        ] {
19959            assert!(
19960                !key.is_empty(),
19961                "Caixa top-level multi-word key const must be non-empty \
19962                 (got {key:?})"
19963            );
19964            let first = key.chars().next().unwrap();
19965            assert!(
19966                first.is_ascii_lowercase(),
19967                "Caixa top-level multi-word key const must lead with an \
19968                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
19969            );
19970            assert!(
19971                key.chars().all(|c| c.is_ascii_alphanumeric()),
19972                "Caixa top-level multi-word key const must be \
19973                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
19974                 whitespace (got {key:?})",
19975            );
19976        }
19977    }
19978
19979    #[test]
19980    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
19981        // Scalar-value pin: the byte-string the
19982        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
19983        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
19984        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
19985        // → `depsTest` matching a hypothetical per-test-target
19986        // vocabulary flip) lands as an edit to exactly one const AND
19987        // one derive attribute — the sibling
19988        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19989        // pin already ties the const to the derive attribute, so a
19990        // rebrand that touches only one side of the pair fails at
19991        // caixa-core build time. Same "scalar-value pin per const"
19992        // discipline the sibling
19993        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
19994        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
19995        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
19996        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
19997    }
19998
19999    #[test]
20000    fn caixa_key_deps_pins_canonical_byte_string() {
20001        // Scalar-value pin: the byte-string the
20002        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
20003        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
20004        // on the two-list dep-graph serialized-key axis — the sibling
20005        // pin covers the multi-word `deps_dev → depsDev` camelCase
20006        // arm, this pin covers the single-word `deps → deps` no-op arm
20007        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
20008        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
20009        // axis and the emitted JSON key equals the source-side field
20010        // name byte-for-byte). A future [`crate::Caixa::deps`] field
20011        // rename (`deps` → `dependencies` matching Cargo's verbatim
20012        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
20013        // hypothetical per-runtime-target vocabulary flip) OR an added
20014        // `#[serde(rename = "…")]` explicit override lands as an edit
20015        // to exactly one const AND one derive-attr / field name — the
20016        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
20017        // pin ties the const to the emitted JSON key, so a rebrand
20018        // that touches only one side of the pair fails at caixa-core
20019        // build time.
20020        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
20021    }
20022
20023    #[test]
20024    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
20025        // Load-bearing invariant on the single-word `deps` top-level
20026        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
20027        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
20028        // `serde_json::to_value(self)` step emits. Serialize a
20029        // populated [`Caixa`] whose `:deps` slot carries at least one
20030        // entry (the `#[serde(default)]` attribute on the field emits
20031        // an empty `[]` even without members, but a non-empty vec
20032        // additionally covers the codec's per-`Dep`-entry emission
20033        // path) and pin that `"deps"` appears verbatim in the JSON
20034        // emission — a future accidental `rename_all = "snake_case"` /
20035        // `"kebab-case"` flip at the derive attribute (or an added
20036        // `#[serde(rename = "…")]` explicit override on the field, or
20037        // a Rust field rename) would break every [`Caixa::to_lisp`]
20038        // round-trip and the future M4 operator-side manifest ingest's
20039        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
20040        // build-time test failure at `manifest.rs`, not as an
20041        // apply-time `.get(<stale-canonical-const>)` returning `None`
20042        // far from the drift's commit. Peer of the sibling
20043        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
20044        // multi-word pin on the same M0 [`Caixa`] top-level
20045        // serialized-key axis, extended here to the single-word arm
20046        // the multi-word test's `rename_all = "camelCase"` sweep can't
20047        // reach (single-word `deps → deps` is a no-op the multi-word
20048        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
20049        // `\"restartWindow\"` byte-scan can never observe).
20050        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20051        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
20052        let json = serde_json::to_string(&c).unwrap();
20053        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
20054        assert!(
20055            json.contains(&quoted),
20056            "serialized Caixa must carry the lifted top-level `deps` \
20057             byte-sequence {quoted} verbatim in the JSON emission (got: \
20058             {json})",
20059        );
20060    }
20061
20062    #[test]
20063    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
20064        // Cross-axis drift-detection pin on the two-list dep-graph
20065        // renderer-side wire-key axis: a future collapse of the
20066        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
20067        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
20068        // same value (e.g. an accidental copy-paste flip of
20069        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
20070        // reroute every downstream `Value::get(<key>)` probe on one
20071        // axis onto the sibling axis's dep-list and pass every
20072        // propagation-probe test that expected only the stale axis's
20073        // value — a dev-only dep would land in the runtime closure at
20074        // publish time, or a runtime dep would be excluded from the
20075        // published lacre. Peer of the sibling four-way distinct pin
20076        // on the top-level multi-word tetrad
20077        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
20078        // and the two-way pin on the sibling
20079        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
20080        // author-facing arm (4da6fba's test), extended here to the
20081        // renderer-side wire-key arm of the same two-list dep-graph
20082        // axis so both halves of the "one canonical byte-string per
20083        // typed axis per (author, wire)" grid carry the same
20084        // distinct-ness discipline.
20085        assert_ne!(
20086            crate::render::CAIXA_KEY_DEPS,
20087            crate::render::CAIXA_KEY_DEPS_DEV,
20088            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
20089             canonical byte-sequences on the two-list dep-graph \
20090             renderer-side wire-key axis"
20091        );
20092    }
20093
20094    // ── DepList / Caixa::push_dep pin ────────────────────────────────
20095    //
20096    // The compounding pin: the two-arm closed-set typed enum
20097    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
20098    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
20099    // consumer of the top-level manifest's dep-mutation surface reads
20100    // through, and the typed dispatch [`Caixa::push_dep`] on the
20101    // substrate primitive folds the "select list → check within-list
20102    // dup → push" cascade onto one method call. Prior to this landing
20103    // the two axes lived across two `&'static str` constants
20104    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
20105    // set type carrying the pair; the `feira add` mutation site's
20106    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
20107    // caixa.deps }` dispatch expressed no compile-time link back to
20108    // the substrate primitive, and a future third dep-list axis would
20109    // have silently split at every open-coded mutation site.
20110
20111    #[test]
20112    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
20113        // Every arm returns the same `&'static str` the substrate's
20114        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
20115        // constants carry. A future rebrand on either constant reaches
20116        // the enum through one edit; a regression to inline literals
20117        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
20118        // quotes from the wire-format constants every consumer routes
20119        // through and this pin flags it at build time.
20120        assert_eq!(
20121            crate::dep::DepList::Prod.as_str(),
20122            crate::render::DEP_AUTHOR_KEY_DEPS
20123        );
20124        assert_eq!(
20125            crate::dep::DepList::Dev.as_str(),
20126            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20127        );
20128    }
20129
20130    #[test]
20131    fn dep_list_display_routes_through_as_str() {
20132        // Same as-str-through-Display convergence discipline the
20133        // sibling closed-set typed enums carry — a `format!("{list}")`
20134        // call must land byte-for-byte on the accessor's return so a
20135        // future consumer that formats the enum for a diagnostic line
20136        // reaches the same wire-format constant the wire-format
20137        // producers do.
20138        assert_eq!(
20139            format!("{}", crate::dep::DepList::Prod),
20140            crate::dep::DepList::Prod.as_str()
20141        );
20142        assert_eq!(
20143            format!("{}", crate::dep::DepList::Dev),
20144            crate::dep::DepList::Dev.as_str()
20145        );
20146    }
20147
20148    #[test]
20149    fn dep_list_all_enumerates_every_variant_once() {
20150        // Exhaustive-iteration pin — every arm appears exactly once in
20151        // `ALL`, matching the closed set the compiler enforces on the
20152        // sibling `match self` arms. A future variant addition that
20153        // extends only one method's match without extending `ALL`
20154        // would silently drop the new arm from every consumer that
20155        // iterates the slice.
20156        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
20157        assert!(variants.contains(&crate::dep::DepList::Prod));
20158        assert!(variants.contains(&crate::dep::DepList::Dev));
20159        assert_eq!(variants.len(), 2);
20160    }
20161
20162    #[test]
20163    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
20164        // Reverse projection on the two-list dep-graph axis: the
20165        // author-surface wire tag the sibling `as_str` emitter walks
20166        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
20167        // `Some(DepList::Prod)`. A regression that hand-rolled the
20168        // per-arm match without routing through the lifted
20169        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
20170        // future wire-tag rebrand and this pin flags it at build time.
20171        assert_eq!(
20172            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
20173            Some(crate::dep::DepList::Prod)
20174        );
20175    }
20176
20177    #[test]
20178    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
20179        // Peer of the `Prod`-arm pin on the dev-only axis: the
20180        // author-surface wire tag the sibling `as_str` emitter walks
20181        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
20182        // back to `Some(DepList::Dev)`. Same drift-detection posture
20183        // as the peer arm — the sibling method `match` arms are
20184        // compiler-checked exhaustive so a future variant addition
20185        // trips at build time.
20186        assert_eq!(
20187            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
20188            Some(crate::dep::DepList::Dev)
20189        );
20190    }
20191
20192    #[test]
20193    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
20194        // Every input outside the closed-set arm-string set the
20195        // sibling `as_str` emitter walks lands on the terminal `None`
20196        // fallback — no silent-accept surface. Sweeps a set of
20197        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
20198        // rebrand candidates, foreign wire tags, empty string) so a
20199        // future variant addition that widened one wire form without
20200        // extending the emitter's arm-set would trip the sibling
20201        // round-trip pin below rather than silently accepting the new
20202        // form here.
20203        for candidate in [
20204            "",
20205            "deps",
20206            "deps-dev",
20207            ":deps ",
20208            ":Deps",
20209            ":DEPS",
20210            ":build-dep",
20211            ":tool-dep",
20212            "prod",
20213            "dev",
20214        ] {
20215            assert_eq!(
20216                crate::dep::DepList::from_wire(candidate),
20217                None,
20218                "from_wire({candidate:?}) must return None; every input outside \
20219                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
20220                 the sibling as_str emitter walks lands on the terminal fallback",
20221            );
20222        }
20223    }
20224
20225    #[test]
20226    fn dep_list_round_trips_through_as_str_and_from_wire() {
20227        // Load-bearing round-trip pin: every arm the `ALL` iteration
20228        // exposes survives the `as_str` → `from_wire` composition
20229        // byte-for-byte. Same discipline the sibling closed-set enums
20230        // carry — `CaixaKind` /
20231        // `RestartStrategy` / `RestartPolicy` /
20232        // `PlacementStrategy` — extended onto the two-list dep-graph
20233        // axis. A future variant addition that extends `ALL` +
20234        // `as_str` without extending `from_wire` (or vice versa)
20235        // trips at build time on this iteration because the compiler
20236        // enforces exhaustiveness on the sibling `match self` arms.
20237        for &list in crate::dep::DepList::ALL {
20238            assert_eq!(
20239                crate::dep::DepList::from_wire(list.as_str()),
20240                Some(list),
20241                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
20242                 a silent split between the forward emitter and the reverse parser \
20243                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
20244            );
20245        }
20246    }
20247
20248    #[test]
20249    fn push_dep_routes_to_deps_slot_on_prod_arm() {
20250        // The `Prod` arm dispatches to the runtime-closure `:deps`
20251        // slot every downstream lacre-pipeline consumer resolves at
20252        // build time. A future arm that regressed to inline `&mut
20253        // self.deps_dev` on the `Prod` path would silently reroute
20254        // every runtime dep into the dev-only closure at publish time
20255        // — this pin refuses that regression.
20256        let src = Caixa::template("host");
20257        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20258        let before_deps = caixa.deps().len();
20259        let before_deps_dev = caixa.deps_dev().len();
20260        let dep = Dep {
20261            nome: "caixa-teia".to_string(),
20262            versao: "^0.1".to_string(),
20263            fonte: None,
20264            opcional: false,
20265            caracteristicas: Vec::new(),
20266        };
20267        caixa
20268            .push_dep(crate::dep::DepList::Prod, dep)
20269            .expect("first push into :deps succeeds");
20270        assert_eq!(caixa.deps().len(), before_deps + 1);
20271        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
20272        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
20273    }
20274
20275    #[test]
20276    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
20277        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
20278        // must dispatch to the dev-only-closure `:deps-dev` slot every
20279        // downstream test-facing artifact resolver reads. A future
20280        // regression that inverted the two arms would silently route
20281        // every dev-only dep into the runtime closure at publish time
20282        // and this pin catches it before the drift ships.
20283        let src = Caixa::template("host");
20284        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20285        let dep = Dep {
20286            nome: "tatara-check".to_string(),
20287            versao: "*".to_string(),
20288            fonte: None,
20289            opcional: false,
20290            caracteristicas: Vec::new(),
20291        };
20292        caixa
20293            .push_dep(crate::dep::DepList::Dev, dep)
20294            .expect("first push into :deps-dev succeeds");
20295        assert!(caixa.deps().is_empty());
20296        assert_eq!(caixa.deps_dev().len(), 1);
20297        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
20298    }
20299
20300    #[test]
20301    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
20302        // Within-list dup check routes through the canonical
20303        // [`DepError::DuplicateNome`] carrier — the substrate's typed
20304        // diagnostic for the same axis [`Caixa::validate_deps`]'s
20305        // parse-time [`crate::render::insert_first_seen`] walk raises
20306        // on. Prior to the lift the mutation site's inline
20307        // `bail!("dep '{}' already declared", …)` string-diagnostic
20308        // path expressed no through-line back to the typed error;
20309        // routing every dep-list refusal through one carrier means an
20310        // author reading a `feira add` refusal and a `feira build`
20311        // refusal reaches for the same corrective surface without
20312        // switching diagnostic idioms.
20313        let src = Caixa::template("host");
20314        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20315        let dep = Dep {
20316            nome: "caixa-teia".to_string(),
20317            versao: "^0.1".to_string(),
20318            fonte: None,
20319            opcional: false,
20320            caracteristicas: Vec::new(),
20321        };
20322        caixa
20323            .push_dep(crate::dep::DepList::Prod, dep.clone())
20324            .expect("first push succeeds");
20325        let dup = Dep {
20326            nome: "caixa-teia".to_string(),
20327            versao: "^0.2".to_string(),
20328            fonte: None,
20329            opcional: false,
20330            caracteristicas: Vec::new(),
20331        };
20332        let err = caixa
20333            .push_dep(crate::dep::DepList::Prod, dup)
20334            .expect_err("second push with same :nome refuses");
20335        assert_eq!(
20336            err,
20337            DepError::DuplicateNome {
20338                nome: "caixa-teia".to_string(),
20339                list: crate::render::DEP_AUTHOR_KEY_DEPS,
20340            }
20341        );
20342        // The refused mutation must not corrupt the target list —
20343        // exactly one entry lives past the refusal, matching the
20344        // canonical single-source-of-truth invariant `Caixa::deps()`
20345        // carries.
20346        assert_eq!(caixa.deps().len(), 1);
20347    }
20348
20349    #[test]
20350    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
20351        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
20352        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
20353        // `list` payload so a future author reading the refusal grep's
20354        // for the correct `:deps-dev` block in their `caixa.lisp`,
20355        // not the sibling `:deps` block the runtime closure resolves.
20356        let src = Caixa::template("host");
20357        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20358        let dep = Dep {
20359            nome: "tatara-check".to_string(),
20360            versao: "*".to_string(),
20361            fonte: None,
20362            opcional: false,
20363            caracteristicas: Vec::new(),
20364        };
20365        caixa
20366            .push_dep(crate::dep::DepList::Dev, dep.clone())
20367            .expect("first push succeeds");
20368        let err = caixa
20369            .push_dep(crate::dep::DepList::Dev, dep)
20370            .expect_err("second push with same :nome refuses");
20371        assert!(matches!(
20372            err,
20373            DepError::DuplicateNome {
20374                ref nome,
20375                list,
20376            } if nome == "tatara-check"
20377                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20378        ));
20379    }
20380
20381    #[test]
20382    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
20383        // The within-list dup check is scoped to the target arm — a
20384        // caixa may legitimately carry the same `:nome` under both
20385        // `:deps` and `:deps-dev` (though the substrate's peer
20386        // [`crate::Caixa::validate_deps`] walk still refuses the
20387        // shape at parse time; the mutation-site refusal is scoped to
20388        // the mutation-site's list to match the peer parse-time
20389        // per-list [`crate::render::insert_first_seen`] discipline).
20390        // The two arms hold independent seen-sets.
20391        let src = Caixa::template("host");
20392        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20393        let dep_prod = Dep {
20394            nome: "shared".to_string(),
20395            versao: "^0.1".to_string(),
20396            fonte: None,
20397            opcional: false,
20398            caracteristicas: Vec::new(),
20399        };
20400        let dep_dev = Dep {
20401            nome: "shared".to_string(),
20402            versao: "*".to_string(),
20403            fonte: None,
20404            opcional: false,
20405            caracteristicas: Vec::new(),
20406        };
20407        caixa
20408            .push_dep(crate::dep::DepList::Prod, dep_prod)
20409            .expect("push into :deps succeeds");
20410        caixa
20411            .push_dep(crate::dep::DepList::Dev, dep_dev)
20412            .expect("push same :nome into :deps-dev succeeds");
20413        assert_eq!(caixa.deps().len(), 1);
20414        assert_eq!(caixa.deps_dev().len(), 1);
20415    }
20416
20417    #[test]
20418    fn deps_of_prod_returns_the_deps_slot_verbatim() {
20419        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
20420        // accessor must project onto the runtime-closure `:deps` slot —
20421        // element-equal and length-equal to the sibling per-slot
20422        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
20423        // A future arm that regressed to `self.deps_dev()` on the `Prod`
20424        // path would silently reroute every downstream typed-dispatch
20425        // walker (the [`Caixa::validate_deps`] per-list
20426        // [`crate::render::insert_first_seen`] dedup walk, any future
20427        // per-axis-parametrised consumer) into the sibling dev-only
20428        // closure and this pin refuses that regression.
20429        let src = Caixa::template("host");
20430        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20431        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20432        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
20433        let dep = Dep {
20434            nome: "caixa-teia".to_string(),
20435            versao: "^0.1".to_string(),
20436            fonte: None,
20437            opcional: false,
20438            caracteristicas: Vec::new(),
20439        };
20440        caixa
20441            .push_dep(crate::dep::DepList::Prod, dep.clone())
20442            .expect("push into :deps succeeds");
20443        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20444        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
20445        assert_eq!(
20446            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
20447            "caixa-teia"
20448        );
20449    }
20450
20451    #[test]
20452    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
20453        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
20454        // [`Caixa::deps_of`] must project onto the dev-only-closure
20455        // `:deps-dev` slot, element-equal and length-equal to the
20456        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
20457        // future regression that inverted the two arms would silently
20458        // route every dev-list walker onto the runtime closure and this
20459        // pin catches it before the drift ships.
20460        let src = Caixa::template("host");
20461        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20462        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20463        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
20464        let dep = Dep {
20465            nome: "tatara-check".to_string(),
20466            versao: "*".to_string(),
20467            fonte: None,
20468            opcional: false,
20469            caracteristicas: Vec::new(),
20470        };
20471        caixa
20472            .push_dep(crate::dep::DepList::Dev, dep)
20473            .expect("push into :deps-dev succeeds");
20474        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20475        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
20476        assert_eq!(
20477            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
20478            "tatara-check"
20479        );
20480    }
20481
20482    #[test]
20483    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
20484        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
20485        // [`Caixa::deps_of`] must land on the same two-slot partition the
20486        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
20487        // expose — the canonical dispatch a future per-axis-parametrised
20488        // walker (a future `feira app graph` per-list dep summary, a
20489        // future M4 per-cluster dev-closure-audit overlay the CR
20490        // materializer resolves per-CR) reads through. Prior to the
20491        // lift the two-block iteration lived open-coded at every walker,
20492        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
20493        // §I) would have had to grow a third block at every consumer.
20494        // A regression that dropped the `Dev` arm from `ALL` would flip
20495        // the collected pairs to `[(":deps", &[])]` alone and this pin
20496        // refuses that shape.
20497        let src = Caixa::template("host");
20498        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20499        let prod_dep = Dep {
20500            nome: "caixa-teia".to_string(),
20501            versao: "^0.1".to_string(),
20502            fonte: None,
20503            opcional: false,
20504            caracteristicas: Vec::new(),
20505        };
20506        let dev_dep = Dep {
20507            nome: "tatara-check".to_string(),
20508            versao: "*".to_string(),
20509            fonte: None,
20510            opcional: false,
20511            caracteristicas: Vec::new(),
20512        };
20513        caixa
20514            .push_dep(crate::dep::DepList::Prod, prod_dep)
20515            .expect("push into :deps succeeds");
20516        caixa
20517            .push_dep(crate::dep::DepList::Dev, dev_dep)
20518            .expect("push into :deps-dev succeeds");
20519        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
20520            .iter()
20521            .map(|&list| {
20522                let slice = caixa.deps_of(list);
20523                (list.as_str(), slice.len(), slice[0].nome())
20524            })
20525            .collect();
20526        assert_eq!(
20527            collected,
20528            vec![
20529                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
20530                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
20531            ]
20532        );
20533    }
20534
20535    #[test]
20536    fn caixa_deps_of_is_const_fn() {
20537        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
20538        // `const`-eval-surface posture. The typed-dispatch read
20539        // accessor forwards through the sibling `pub const fn`
20540        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
20541        // accessors on the two [`crate::dep::DepList`] enum arms —
20542        // every operator in the body is already `const`-callable
20543        // (`DepList` is a plain `#[derive(Copy)]` closed-set
20544        // discriminator so the `match` arms are const-evaluable, and
20545        // each arm dispatches through the sibling `pub const fn`
20546        // slice accessor). Any future accidental downgrade to
20547        // non-`const` fails the `deps_of_via_const_fn` wrapper below
20548        // at caixa-core build time with E0015 (`cannot call non-const
20549        // method`), strictly stronger than a runtime `assert!` and
20550        // side-stepping the destructor-in-const restriction the
20551        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
20552        // rule out on the direct-`const _: () = assert!(...)`
20553        // residence.
20554        //
20555        // Peer of the sibling outer-`Caixa` accessor family pins
20556        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
20557        // on the `&[String]` universal-axis surface,
20558        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
20559        // on the outer `&[T]` composite-slice surface,
20560        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
20561        // on the outer `Option<&Composite>` surface) — this pin
20562        // extends the `const`-eval-surface discipline onto the outer-
20563        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
20564        // dep-list axis, closing the outer-`Caixa` accessor family's
20565        // last unlifted `pub fn` on the read side.
20566        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
20567            c.deps_of(list)
20568        }
20569        let src = Caixa::template("host");
20570        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20571        // Empty-list arm: both `Prod` and `Dev` degenerate to the
20572        // empty slice with no silent `None` collapse — the
20573        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
20574        // that omits the slot lands on.
20575        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
20576        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
20577        assert_eq!(
20578            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20579            caixa.deps()
20580        );
20581        assert_eq!(
20582            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20583            caixa.deps_dev()
20584        );
20585        // Populated arms: each list carries its own entry, and the
20586        // wrapper / direct dispatches agree byte-for-byte on the
20587        // slice-view under both non-empty arms.
20588        let prod_dep = Dep {
20589            nome: "caixa-teia".to_string(),
20590            versao: "^0.1".to_string(),
20591            fonte: None,
20592            opcional: false,
20593            caracteristicas: Vec::new(),
20594        };
20595        let dev_dep = Dep {
20596            nome: "tatara-check".to_string(),
20597            versao: "*".to_string(),
20598            fonte: None,
20599            opcional: false,
20600            caracteristicas: Vec::new(),
20601        };
20602        caixa
20603            .push_dep(crate::dep::DepList::Prod, prod_dep)
20604            .expect("push into :deps succeeds");
20605        caixa
20606            .push_dep(crate::dep::DepList::Dev, dev_dep)
20607            .expect("push into :deps-dev succeeds");
20608        assert_eq!(
20609            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20610            caixa.deps()
20611        );
20612        assert_eq!(
20613            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20614            caixa.deps_dev()
20615        );
20616        assert_eq!(
20617            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
20618            "caixa-teia"
20619        );
20620        assert_eq!(
20621            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
20622            "tatara-check"
20623        );
20624    }
20625
20626    #[test]
20627    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
20628        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
20629        // must route its per-list [`crate::render::insert_first_seen`]
20630        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
20631        // rather than the pre-lift open-coded two-block iteration over
20632        // `self.deps()` + `self.deps_dev()`. A regression that dropped
20633        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
20634        // stop refusing within-list dups on the sibling arm; a
20635        // regression that flipped the arm-to-list-key mapping
20636        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
20637        // diagnostic surface. Both drifts surface here through a paired
20638        // duplicate-name refusal per arm plus an offending-list-key
20639        // check on the emitted [`DepError::DuplicateNome`] carrier.
20640        for &list in crate::dep::DepList::ALL {
20641            let src = Caixa::template("host");
20642            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20643            let dup = Dep {
20644                nome: "twin".to_string(),
20645                versao: "^0.1".to_string(),
20646                fonte: None,
20647                opcional: false,
20648                caracteristicas: Vec::new(),
20649            };
20650            match list {
20651                crate::dep::DepList::Prod => {
20652                    caixa.deps.push(dup.clone());
20653                    caixa.deps.push(dup);
20654                }
20655                crate::dep::DepList::Dev => {
20656                    caixa.deps_dev.push(dup.clone());
20657                    caixa.deps_dev.push(dup);
20658                }
20659            }
20660            let err = caixa
20661                .validate_deps()
20662                .expect_err("within-list duplicate :nome must refuse");
20663            assert_eq!(
20664                err,
20665                DepError::DuplicateNome {
20666                    nome: "twin".to_string(),
20667                    list: list.as_str(),
20668                },
20669                "validate_deps on {list} arm must emit \
20670                 DepError::DuplicateNome carrying the arm's own \
20671                 as_str() diagnostic — the arm-to-list-key mapping \
20672                 flowed through DepList::ALL + Caixa::deps_of"
20673            );
20674        }
20675    }
20676
20677    #[test]
20678    fn caixa_licenca_default_pins_canonical_mit_byte() {
20679        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
20680        // canonical SPDX-`"MIT"` byte today, the same license expression
20681        // every peer substrate-side consumer of the author-omitted
20682        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
20683        // `caixa-helm/src/lib.rs`, the future M4
20684        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
20685        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
20686        // crate's [`Caixa::validate_licenca`] docstring roadmap already
20687        // names as the second consumer) fills into its per-consumer
20688        // README/annotation emit site. Pin the literal here (peer with the
20689        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
20690        // [`crate::version::DEFAULT_GIT_REMOTE`] /
20691        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
20692        // on the sibling lifted-constant surfaces) so a future
20693        // substrate-side license-fallback rebrand surfaces here as a
20694        // coordinated edit-point: the sibling caixa-helm
20695        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
20696        // pinning test already pins the equality at the renderer-emit
20697        // axis; this pin closes the second coordinate of the pair by
20698        // anchoring the lifted constant's current byte to the canonical
20699        // CAIXA-SDLC §I license scaffold's documented shape.
20700        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
20701    }
20702
20703    // ── Caixa::validate_upgrade_from — compound per-Caixa entry gate on ──
20704    // ── the M2 `:upgrade-from` slot: folds the three top-level        ──
20705    // ── `crate::upgrade` validators (per-entry + cross-entry           ──
20706    // ── duplicate-`:from`, cross-slot `:from < :versao` precedence,   ──
20707    // ── cross-slot `:state-change` ↔ `:on-state-change` composition)  ──
20708    // ── onto one substrate primitive. Byte-for-byte equivalent to the ──
20709    // ── pre-fold three-block cascade at                               ──
20710    // ── `crate::layout::StandardLayout::verify` under the same        ──
20711    // ── canonical dispatch order.                                     ──
20712
20713    #[test]
20714    fn validate_upgrade_from_folds_per_entry_arm_matches_gate() {
20715        // Fail-before-pass-after per-arm equivalence pin on the
20716        // per-entry + cross-entry axis: a fixture whose `:upgrade-from`
20717        // carries a per-entry-invalid `:from` (git-tag shape `"v0.1.0"`,
20718        // which `semver::Version::parse` rejects) surfaces the same
20719        // [`crate::UpgradeError`] through the compound gate
20720        // [`Caixa::validate_upgrade_from`] and the standalone per-entry
20721        // gate [`crate::upgrade::validate_upgrade_from`] on the same
20722        // [`Caixa::upgrade_from`] slice. Pins the fold — a silent
20723        // regression that de-folded the per-entry arm would surface here
20724        // as a mismatch between the two dispatches. Sibling in shape to
20725        // the peer per-slot-≡-standalone equivalence pins the
20726        // [`crate::AplicacaoSpec::validate_contratos`] /
20727        // [`crate::MeshPolicy::validate`] /
20728        // [`crate::SupervisorSpec::validate_children`] compound gates
20729        // each carry on their axes.
20730        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20731        c.upgrade_from = vec![crate::UpgradeFromEntry {
20732            from: "v0.1.0".into(),
20733            instructions: vec![crate::UpgradeInstruction::Restart],
20734        }];
20735        let via_method = c.validate_upgrade_from().unwrap_err();
20736        let via_standalone = crate::upgrade::validate_upgrade_from(c.upgrade_from()).unwrap_err();
20737        assert_eq!(
20738            via_method, via_standalone,
20739            "Caixa::validate_upgrade_from must surface the per-entry \
20740             axis's diagnostic byte-equal to the standalone \
20741             `crate::upgrade::validate_upgrade_from` on the same \
20742             upgrade_from() slice"
20743        );
20744        assert!(
20745            matches!(
20746                via_method,
20747                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.1.0"
20748            ),
20749            "expected FromInvalid on the git-tag-shape `:from`, got {via_method:?}"
20750        );
20751    }
20752
20753    #[test]
20754    fn validate_upgrade_from_folds_versao_arm_matches_gate() {
20755        // Per-arm equivalence pin on the cross-slot `:from ↔ :versao`
20756        // precedence axis: a fixture with a well-formed `:from` (so the
20757        // per-entry arm passes) whose parsed semver is >= the caixa's
20758        // `:versao` under SemVer-2 precedence surfaces the same
20759        // [`crate::UpgradeError::FromNotBeforeVersao`] through both the
20760        // compound gate and the standalone
20761        // [`crate::upgrade::validate_upgrade_from_against_versao`] gate
20762        // keyed off the same `(upgrade_from, versao)` pair. Pins the
20763        // fold's second arm — reaching this arm through the compound
20764        // gate requires the per-entry arm to pass first, which itself
20765        // pins the per-arm cross-arm ordering.
20766        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20767        c.versao = "0.1.0".into();
20768        c.upgrade_from = vec![crate::UpgradeFromEntry {
20769            from: "0.2.0".into(),
20770            instructions: vec![crate::UpgradeInstruction::Restart],
20771        }];
20772        let via_method = c.validate_upgrade_from().unwrap_err();
20773        let via_standalone =
20774            crate::upgrade::validate_upgrade_from_against_versao(c.upgrade_from(), c.versao())
20775                .unwrap_err();
20776        assert_eq!(
20777            via_method, via_standalone,
20778            "Caixa::validate_upgrade_from must surface the \
20779             `:from >= :versao` diagnostic byte-equal to the standalone \
20780             `crate::upgrade::validate_upgrade_from_against_versao` on \
20781             the same (upgrade_from, versao) pair"
20782        );
20783        assert!(
20784            matches!(
20785                via_method,
20786                crate::UpgradeError::FromNotBeforeVersao { ref from, ref versao }
20787                    if from == "0.2.0" && versao == "0.1.0"
20788            ),
20789            "expected FromNotBeforeVersao carrying the offending pair, got {via_method:?}"
20790        );
20791    }
20792
20793    #[test]
20794    fn validate_upgrade_from_folds_behavior_arm_matches_gate() {
20795        // Per-arm equivalence pin on the cross-slot `:state-change ↔
20796        // :on-state-change` composition axis: a fixture with a
20797        // well-formed `:from` strictly less than `:versao` (so the
20798        // per-entry and versao arms both pass) whose `:instructions`
20799        // list carries a `(:state-change …)` instruction with no
20800        // `:behavior :on-state-change` callback declared surfaces the
20801        // same [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
20802        // through both the compound gate and the standalone
20803        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
20804        // gate keyed off the same `(upgrade_from, behavior)` pair.
20805        // Reaching this arm through the compound gate requires both
20806        // prior arms to pass first — the ordering pin below pins the
20807        // per-arm dispatch order explicitly.
20808        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20809        c.versao = "0.2.0".into();
20810        c.behavior = None;
20811        c.upgrade_from = vec![crate::UpgradeFromEntry {
20812            from: "0.1.0".into(),
20813            instructions: vec![
20814                crate::UpgradeInstruction::LoadModule {
20815                    module: "demo".into(),
20816                },
20817                crate::UpgradeInstruction::StateChange {
20818                    script: std::path::PathBuf::from("lib/m.lisp"),
20819                },
20820                crate::UpgradeInstruction::SoftPurge {
20821                    module: "demo-old".into(),
20822                },
20823            ],
20824        }];
20825        let via_method = c.validate_upgrade_from().unwrap_err();
20826        let via_standalone =
20827            crate::upgrade::validate_upgrade_from_against_behavior(c.upgrade_from(), c.behavior())
20828                .unwrap_err();
20829        assert_eq!(
20830            via_method, via_standalone,
20831            "Caixa::validate_upgrade_from must surface the \
20832             `:state-change` ↔ `:on-state-change` composition \
20833             diagnostic byte-equal to the standalone \
20834             `crate::upgrade::validate_upgrade_from_against_behavior` \
20835             on the same (upgrade_from, behavior) pair"
20836        );
20837        assert!(
20838            matches!(
20839                via_method,
20840                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback {
20841                    ref from,
20842                    ref script,
20843                } if from == "0.1.0" && script == &std::path::PathBuf::from("lib/m.lisp")
20844            ),
20845            "expected StateChangeWithoutOnStateChangeCallback carrying \
20846             the offending (from, script) pair, got {via_method:?}"
20847        );
20848    }
20849
20850    #[test]
20851    fn validate_upgrade_from_per_entry_arm_fires_before_versao_arm() {
20852        // Cross-arm ordering pin between the first two arms of the
20853        // fold: a fixture carrying BOTH a per-entry-invalid `:from`
20854        // (`"v0.0.5"` — git-tag shape rejected by
20855        // [`crate::upgrade::validate_upgrade_from`]) AND a would-be
20856        // versao-precedence violation on a second entry (`"0.2.0" >=
20857        // :versao "0.1.0"`) surfaces the per-entry diagnostic first
20858        // through the compound gate. Sanity assertion: the second
20859        // entry alone under the same `:versao` trips the versao arm
20860        // on its own via the standalone
20861        // [`crate::upgrade::validate_upgrade_from_against_versao`], so
20862        // the per-entry-first surfacing is a real ordering property,
20863        // not a case where the versao arm silently accepts the
20864        // fixture. Pins the pre-fold layout wire-up's canonical
20865        // dispatch order (per-entry → versao → behavior) as a
20866        // property of the substrate primitive rather than a
20867        // convention of the layout call site.
20868        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20869        c.versao = "0.1.0".into();
20870        c.upgrade_from = vec![
20871            crate::UpgradeFromEntry {
20872                from: "v0.0.5".into(),
20873                instructions: vec![crate::UpgradeInstruction::Restart],
20874            },
20875            crate::UpgradeFromEntry {
20876                from: "0.2.0".into(),
20877                instructions: vec![crate::UpgradeInstruction::Restart],
20878            },
20879        ];
20880        let err = c.validate_upgrade_from().unwrap_err();
20881        assert!(
20882            matches!(
20883                err,
20884                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.0.5"
20885            ),
20886            "per-entry arm must fire before versao arm — expected \
20887             FromInvalid on `v0.0.5`, got {err:?}"
20888        );
20889        // Sanity: the versao-violating second entry alone under the
20890        // same `:versao` trips the versao arm on its own — proves the
20891        // per-entry-first surfacing above is a real ordering property.
20892        let sanity = crate::upgrade::validate_upgrade_from_against_versao(
20893            &[crate::UpgradeFromEntry {
20894                from: "0.2.0".into(),
20895                instructions: vec![crate::UpgradeInstruction::Restart],
20896            }],
20897            "0.1.0",
20898        )
20899        .unwrap_err();
20900        assert!(
20901            matches!(sanity, crate::UpgradeError::FromNotBeforeVersao { .. }),
20902            "sanity: the versao-violating fixture alone must trip the \
20903             versao arm — got {sanity:?}"
20904        );
20905    }
20906
20907    #[test]
20908    fn validate_upgrade_from_versao_arm_fires_before_behavior_arm() {
20909        // Cross-arm ordering pin between the second and third arms of
20910        // the fold: a fixture carrying BOTH a versao-precedence
20911        // violation (`:from "0.2.0" >= :versao "0.1.0"`) AND a
20912        // would-be missing-callback violation (a `(:state-change …)`
20913        // instruction with no `:behavior :on-state-change`) surfaces
20914        // the versao diagnostic first through the compound gate.
20915        // Sanity assertion: the missing-callback fixture alone (with
20916        // the versao-precedence violation removed by bumping
20917        // `:versao` past `:from`) trips the behavior arm on its own
20918        // via the standalone
20919        // [`crate::upgrade::validate_upgrade_from_against_behavior`],
20920        // so the versao-first surfacing is a real ordering property,
20921        // not a case where the behavior arm silently accepts the
20922        // fixture.
20923        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20924        c.versao = "0.1.0".into();
20925        c.behavior = None;
20926        c.upgrade_from = vec![crate::UpgradeFromEntry {
20927            from: "0.2.0".into(),
20928            instructions: vec![
20929                crate::UpgradeInstruction::LoadModule {
20930                    module: "demo".into(),
20931                },
20932                crate::UpgradeInstruction::StateChange {
20933                    script: std::path::PathBuf::from("lib/m.lisp"),
20934                },
20935            ],
20936        }];
20937        let err = c.validate_upgrade_from().unwrap_err();
20938        assert!(
20939            matches!(
20940                err,
20941                crate::UpgradeError::FromNotBeforeVersao { ref from, .. } if from == "0.2.0"
20942            ),
20943            "versao arm must fire before behavior arm — expected \
20944             FromNotBeforeVersao on `0.2.0`, got {err:?}"
20945        );
20946        // Sanity: the same instructions under a `:versao` that
20947        // accepts the `:from` (so the versao arm passes) trips the
20948        // behavior arm — proves the versao-first surfacing above is a
20949        // real ordering property.
20950        let sanity = crate::upgrade::validate_upgrade_from_against_behavior(
20951            &[crate::UpgradeFromEntry {
20952                from: "0.2.0".into(),
20953                instructions: vec![
20954                    crate::UpgradeInstruction::LoadModule {
20955                        module: "demo".into(),
20956                    },
20957                    crate::UpgradeInstruction::StateChange {
20958                        script: std::path::PathBuf::from("lib/m.lisp"),
20959                    },
20960                ],
20961            }],
20962            None,
20963        )
20964        .unwrap_err();
20965        assert!(
20966            matches!(
20967                sanity,
20968                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback { .. }
20969            ),
20970            "sanity: the missing-callback fixture alone must trip the \
20971             behavior arm — got {sanity:?}"
20972        );
20973    }
20974
20975    #[test]
20976    fn validate_upgrade_from_accepts_clean_fixture() {
20977        // Positive control: a well-formed `:upgrade-from` (single entry
20978        // with `:from` strictly less than `:versao`, no
20979        // `:state-change` instruction so the behavior arm is vacuous)
20980        // passes the compound gate cleanly. A future tightening of any
20981        // one arm's accepted set surfaces here as a test failure
20982        // first. Mirrors the peer `validate_versao_accepts_canonical_forms`
20983        // positive-control posture on the sibling per-Caixa gate.
20984        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20985        c.versao = "0.2.0".into();
20986        c.upgrade_from = vec![crate::UpgradeFromEntry {
20987            from: "0.1.0".into(),
20988            instructions: vec![crate::UpgradeInstruction::Restart],
20989        }];
20990        c.validate_upgrade_from()
20991            .expect("clean fixture must pass the compound `:upgrade-from` gate");
20992    }
20993
20994    #[test]
20995    fn validate_upgrade_from_accepts_empty_upgrade_from() {
20996        // Positive control on the empty-list arm: a caixa without any
20997        // `:upgrade-from` block (the default `Vec::new()`
20998        // `#[serde(default)]` folds an omitted slot onto) passes the
20999        // compound gate cleanly regardless of `:versao` or `:behavior`
21000        // — each of the three standalone validators is vacuous on the
21001        // empty entry list. Pins the identity element of the fold on
21002        // the empty-slot side.
21003        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21004        assert!(
21005            c.upgrade_from().is_empty(),
21006            "template caixa must carry an empty :upgrade-from — got {:?}",
21007            c.upgrade_from()
21008        );
21009        c.validate_upgrade_from()
21010            .expect("empty :upgrade-from must pass the compound gate cleanly");
21011    }
21012
21013    // ── Caixa::validate_limits — compound per-Caixa entry gate on   ──
21014    // ── the M2 `:limits` slot: folds the                            ──
21015    // ── [`crate::LimitsSpec::validate`] four-axis cascade on the    ──
21016    // ── present-slot arm and the `Option::None` identity element on ──
21017    // ── the absent-slot arm onto one substrate primitive.           ──
21018    // ── Byte-for-byte equivalent to the pre-fold                    ──
21019    // ── `if let Some(l) = caixa.limits() { l.validate() }`          ──
21020    // ── unwrap-and-dispatch pattern at                              ──
21021    // ── `crate::layout::StandardLayout::verify` (`layout.rs`).      ──
21022
21023    #[test]
21024    fn validate_limits_folds_arm_matches_gate() {
21025        // Fail-before-pass-after per-arm equivalence pin on the
21026        // present-slot arm: a fixture whose `:limits` carries a
21027        // zero-floor-violating `:fuel` (`Some(0)`, which
21028        // [`crate::LimitsSpec::validate`] rejects through
21029        // [`crate::LimitsError::FuelZero`]) surfaces the same
21030        // [`crate::LimitsError`] byte-equal through both the compound
21031        // gate [`Caixa::validate_limits`] and the standalone
21032        // [`crate::LimitsSpec::validate`] gate on the same `LimitsSpec`
21033        // value. Pins the fold — a silent regression that de-folded
21034        // the present-slot arm would surface here as a mismatch
21035        // between the two dispatches. Sibling in shape to the peer
21036        // per-arm equivalence pins the
21037        // [`crate::AplicacaoSpec::validate_contratos`] /
21038        // [`crate::MeshPolicy::validate`] /
21039        // [`crate::SupervisorSpec::validate_children`] /
21040        // [`Caixa::validate_upgrade_from`] compound gates each carry
21041        // on their axes.
21042        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21043        let l = crate::LimitsSpec {
21044            memory: None,
21045            fuel: Some(0),
21046            wall_clock: None,
21047            cpu: None,
21048        };
21049        c.limits = Some(l);
21050        let via_method = c.validate_limits().unwrap_err();
21051        let via_standalone = l.validate().unwrap_err();
21052        assert_eq!(
21053            via_method, via_standalone,
21054            "Caixa::validate_limits must surface the present-slot \
21055             arm's diagnostic byte-equal to the standalone \
21056             `LimitsSpec::validate` on the same `LimitsSpec` value"
21057        );
21058        assert!(
21059            matches!(via_method, crate::LimitsError::FuelZero),
21060            "expected FuelZero on the zero-floor-violating `:fuel`, \
21061             got {via_method:?}"
21062        );
21063    }
21064
21065    #[test]
21066    fn validate_limits_accepts_none() {
21067        // Positive control on the absent-slot arm (the fold's identity
21068        // element): a caixa without any `:limits` block (the
21069        // canonical "no bound declared — engine-default applies"
21070        // author shape [`crate::LimitsSpec::is_empty`]'s per-axis
21071        // `None` cascade reads, and the shape the [`Caixa::template`]
21072        // scaffold emits by construction) passes the compound gate
21073        // cleanly, regardless of any per-axis defect a subsequent
21074        // `Some(_)` binding would surface. Pins the identity element
21075        // of the fold on the absent-slot side, matching the peer
21076        // `validate_upgrade_from_accepts_empty_upgrade_from` positive-
21077        // control posture on the sibling M2 slot.
21078        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21079        assert!(
21080            c.limits().is_none(),
21081            "template caixa must carry an absent :limits — got {:?}",
21082            c.limits()
21083        );
21084        c.validate_limits()
21085            .expect("absent :limits must pass the compound gate cleanly");
21086    }
21087
21088    #[test]
21089    fn validate_limits_accepts_clean_fixture() {
21090        // Positive control on the present-slot arm: a caixa whose
21091        // `:limits` is `Some(LimitsSpec::default())` (all four axes
21092        // `None` — every axis absent under the outer `Some(_)`
21093        // binding, so every present-slot arm on
21094        // [`crate::LimitsSpec::validate`] is vacuous) passes the
21095        // compound gate cleanly. A future tightening of any one axis
21096        // that surfaces a diagnostic on the all-`None` `LimitsSpec`
21097        // would land here as a test failure first. Pins the
21098        // present-slot arm's accept-shape on the canonical
21099        // "declared-but-empty" author fixture the
21100        // `limits_round_trip_via_json` peer already round-trips
21101        // (`caixa-core/src/manifest.rs:6971`).
21102        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21103        c.limits = Some(crate::LimitsSpec::default());
21104        c.validate_limits()
21105            .expect("Some(LimitsSpec::default()) must pass the compound gate cleanly");
21106    }
21107
21108    // ── Caixa::validate_behavior — compound per-Caixa entry gate on ──
21109    // ── the M2 `:behavior` slot's pure value-shape surface: folds   ──
21110    // ── the [`crate::BehaviorSpec::validate`] six-slot cascade on   ──
21111    // ── the present-slot arm and the `Option::None` identity        ──
21112    // ── element on the absent-slot arm onto one substrate primitive.──
21113    // ── Byte-for-byte equivalent to the pre-fold                    ──
21114    // ── `if let Some(b) = caixa.behavior() { b.validate() }`        ──
21115    // ── unwrap-and-dispatch pattern at                              ──
21116    // ── `crate::layout::StandardLayout::verify` (`layout.rs`). The  ──
21117    // ── on-disk callback-path existence walk stays open-coded at    ──
21118    // ── the layout altitude because it needs the                    ──
21119    // ── [`crate::layout::LayoutInvariants::exists`] filesystem       ──
21120    // ── oracle the pure typed-shape surface has no reference to —   ──
21121    // ── mirror of the peer M2 `:upgrade-from` per-instruction       ──
21122    // ── script-path existence probe that stayed at the layout       ──
21123    // ── altitude after the [`Caixa::validate_upgrade_from`] lift    ──
21124    // ── (d6801df) for the same reason.                              ──
21125
21126    #[test]
21127    fn validate_behavior_folds_arm_matches_gate() {
21128        // Fail-before-pass-after per-arm equivalence pin on the
21129        // present-slot arm: a fixture whose `:behavior` carries an
21130        // absolute-path `:on-init` (`"/etc/passwd"`, which
21131        // [`crate::BehaviorSpec::validate`] rejects through
21132        // [`crate::BehaviorError::AbsolutePath`]) surfaces the same
21133        // [`crate::BehaviorError`] byte-equal through both the
21134        // compound gate [`Caixa::validate_behavior`] and the standalone
21135        // [`crate::BehaviorSpec::validate`] gate on the same
21136        // `BehaviorSpec` value. Pins the fold — a silent regression
21137        // that de-folded the present-slot arm would surface here as a
21138        // mismatch between the two dispatches. Sibling in shape to the
21139        // peer per-arm equivalence pins the
21140        // [`Caixa::validate_limits`] (baa4688),
21141        // [`Caixa::validate_upgrade_from`] (d6801df),
21142        // [`crate::MeshPolicy::validate`],
21143        // [`crate::AplicacaoSpec::validate_contratos`], and
21144        // [`crate::SupervisorSpec::validate_children`] compound gates
21145        // each carry on their axes.
21146        use crate::BehaviorSpec;
21147        use std::path::PathBuf;
21148        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21149        let b = BehaviorSpec {
21150            on_init: Some(PathBuf::from("/etc/passwd")),
21151            ..Default::default()
21152        };
21153        c.behavior = Some(b.clone());
21154        let via_method = c.validate_behavior().unwrap_err();
21155        let via_standalone = b.validate().unwrap_err();
21156        assert_eq!(
21157            via_method, via_standalone,
21158            "Caixa::validate_behavior must surface the present-slot \
21159             arm's diagnostic byte-equal to the standalone \
21160             `BehaviorSpec::validate` on the same `BehaviorSpec` value"
21161        );
21162        assert!(
21163            matches!(via_method, crate::BehaviorError::AbsolutePath { .. }),
21164            "expected AbsolutePath on the absolute `:on-init` path, \
21165             got {via_method:?}"
21166        );
21167    }
21168
21169    #[test]
21170    fn validate_behavior_accepts_none() {
21171        // Positive control on the absent-slot arm (the fold's identity
21172        // element): a caixa without any `:behavior` block (the
21173        // canonical "no callback declared — the runtime falls back to
21174        // the wasm-engine's default per arm" author shape
21175        // [`crate::BehaviorSpec::is_empty`]'s per-slot `None` cascade
21176        // reads, and the shape the [`Caixa::template`] scaffold emits
21177        // by construction) passes the compound gate cleanly,
21178        // regardless of any per-slot defect a subsequent `Some(_)`
21179        // binding would surface. Pins the identity element of the fold
21180        // on the absent-slot side, matching the peer
21181        // `validate_limits_accepts_none` (baa4688) and
21182        // `validate_upgrade_from_accepts_empty_upgrade_from` (d6801df)
21183        // positive-control postures on the sibling M2 slots.
21184        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21185        assert!(
21186            c.behavior().is_none(),
21187            "template caixa must carry an absent :behavior — got {:?}",
21188            c.behavior()
21189        );
21190        c.validate_behavior()
21191            .expect("absent :behavior must pass the compound gate cleanly");
21192    }
21193
21194    #[test]
21195    fn validate_behavior_accepts_clean_fixture() {
21196        // Positive control on the present-slot arm: a caixa whose
21197        // `:behavior` is `Some(BehaviorSpec::default())` (all six
21198        // slots `None` — every slot absent under the outer `Some(_)`
21199        // binding, so every present-slot arm on
21200        // [`crate::BehaviorSpec::validate`] is vacuous) passes the
21201        // compound gate cleanly. A future tightening of any one arm
21202        // that surfaces a diagnostic on the all-`None` `BehaviorSpec`
21203        // would land here as a test failure first. Pins the
21204        // present-slot arm's accept-shape on the canonical
21205        // "declared-but-empty" author fixture the sibling
21206        // `empty_behavior_round_trip` peer already round-trips
21207        // (`caixa-core/src/behavior.rs` tests). Mirror of the peer
21208        // `validate_limits_accepts_clean_fixture` (baa4688)
21209        // positive-control posture on the sibling M2 `:limits` slot.
21210        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21211        c.behavior = Some(crate::BehaviorSpec::default());
21212        c.validate_behavior()
21213            .expect("Some(BehaviorSpec::default()) must pass the compound gate cleanly");
21214    }
21215
21216    // ── Caixa::validate_deps — compound per-Caixa entry gate on the ──
21217    // ── dep-graph axis: folds the two standalone validators         ──
21218    // ── (per-entry + within-list duplicate walk that this method    ──
21219    // ── opened on, cross-slot self-edge via                         ──
21220    // ── `crate::dep::validate_no_self_dep`) onto one substrate      ──
21221    // ── primitive. Byte-for-byte equivalent to the pre-fold         ──
21222    // ── two-block cascade at                                        ──
21223    // ── `crate::layout::StandardLayout::verify` under the same      ──
21224    // ── canonical dispatch order (per-entry → self-edge).           ──
21225
21226    #[test]
21227    fn validate_deps_folds_per_entry_arm_matches_gate() {
21228        // Fail-before-pass-after per-arm equivalence pin on the
21229        // per-entry + within-list duplicate axis: a fixture whose
21230        // `:deps` carries a per-entry-invalid `:versao` (`"^bad"`,
21231        // which [`crate::parse_requirement`] rejects) surfaces the
21232        // same [`crate::DepError`] through the compound gate
21233        // [`Caixa::validate_deps`] and the standalone per-entry walk
21234        // ([`Dep::validate`]) on the offending entry. Pins the
21235        // fold — a silent regression that de-folded the per-entry arm
21236        // would surface here as a mismatch between the two
21237        // dispatches. Sibling in shape to the peer
21238        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21239        // per-arm equivalence pin (d6801df) on the M2
21240        // `:upgrade-from` compound gate's per-entry arm, extended
21241        // here onto the universal-axis `:deps` compound gate's
21242        // per-entry arm.
21243        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21244        c.deps = vec![Dep::simple("d", "^bad")];
21245        let via_method = c.validate_deps().unwrap_err();
21246        let via_standalone = c.deps()[0].validate().unwrap_err();
21247        assert_eq!(
21248            via_method, via_standalone,
21249            "Caixa::validate_deps must surface the per-entry arm's \
21250             diagnostic byte-equal to the standalone \
21251             `Dep::validate` on the same offending entry",
21252        );
21253        assert!(
21254            matches!(
21255                via_method,
21256                DepError::VersaoInvalid { ref nome, .. } if nome == "d"
21257            ),
21258            "expected VersaoInvalid on the malformed :versao, got {via_method:?}",
21259        );
21260    }
21261
21262    #[test]
21263    fn validate_deps_folds_self_edge_arm_matches_gate() {
21264        // Per-arm equivalence pin on the cross-slot self-edge axis:
21265        // a fixture whose `:deps` lists the caixa's own `:nome`
21266        // (a self-dep, which
21267        // [`crate::dep::validate_no_self_dep`] rejects as a
21268        // structurally-invalid one-node cycle in the lacre closure's
21269        // dep-graph) surfaces the same [`crate::DepError::DepIsSelf`]
21270        // through both the compound gate and the standalone
21271        // [`crate::dep::validate_no_self_dep`] gate keyed off the
21272        // same `(deps, deps_dev, nome)` triple. Pins the fold's
21273        // second arm — reaching this arm through the compound gate
21274        // requires the per-entry + within-list duplicate walk to
21275        // pass first, which itself pins one cross-arm ordering step.
21276        // Sibling in shape to the peer
21277        // `validate_upgrade_from_folds_versao_arm_matches_gate` /
21278        // `_folds_behavior_arm_matches_gate` cross-slot equivalence
21279        // pins (d6801df) on the M2 `:upgrade-from` compound gate's
21280        // cross-slot arms.
21281        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21282        c.deps = vec![Dep::simple("demo", "^0.1")];
21283        let via_method = c.validate_deps().unwrap_err();
21284        let via_standalone =
21285            crate::dep::validate_no_self_dep(c.deps(), c.deps_dev(), c.nome()).unwrap_err();
21286        assert_eq!(
21287            via_method, via_standalone,
21288            "Caixa::validate_deps must surface the cross-slot \
21289             self-edge diagnostic byte-equal to the standalone \
21290             `crate::dep::validate_no_self_dep` on the same \
21291             (deps, deps_dev, nome) triple",
21292        );
21293        assert!(
21294            matches!(
21295                via_method,
21296                DepError::DepIsSelf { ref nome, list }
21297                    if nome == "demo" && list == crate::render::DEP_AUTHOR_KEY_DEPS
21298            ),
21299            "expected DepIsSelf carrying (nome=\"demo\", list=\":deps\"), got {via_method:?}",
21300        );
21301    }
21302
21303    #[test]
21304    fn validate_deps_per_entry_arm_fires_before_self_edge_arm() {
21305        // Cross-arm ordering pin between the two arms of the fold:
21306        // a fixture carrying BOTH a per-entry-invalid `:versao`
21307        // (`"^bad"` — [`crate::parse_requirement`] rejects the
21308        // requirement grammar) on a non-self-dep entry AND a
21309        // would-be self-edge violation on a second entry (the
21310        // caixa's own `:nome` "demo") surfaces the per-entry
21311        // diagnostic first through the compound gate. Sanity
21312        // assertion: the second entry alone under the same parent
21313        // `:nome` trips the self-edge arm on its own via the
21314        // standalone [`crate::dep::validate_no_self_dep`], so the
21315        // per-entry-first surfacing is a real ordering property,
21316        // not a case where the self-edge arm silently accepts the
21317        // fixture. Pins the pre-fold layout wire-up's canonical
21318        // dispatch order (per-entry + within-list duplicate →
21319        // self-edge) as a property of the substrate primitive
21320        // rather than a convention of the layout call site. Sibling
21321        // in shape to
21322        // `validate_upgrade_from_per_entry_arm_fires_before_versao_arm`
21323        // (d6801df) on the M2 `:upgrade-from` compound gate's
21324        // per-arm ordering property.
21325        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21326        c.deps = vec![
21327            Dep::simple("orquestra", "^bad"),
21328            Dep::simple("demo", "^0.1"),
21329        ];
21330        let err = c.validate_deps().unwrap_err();
21331        assert!(
21332            matches!(
21333                err,
21334                DepError::VersaoInvalid { ref nome, .. } if nome == "orquestra"
21335            ),
21336            "per-entry arm must fire before self-edge arm — expected \
21337             VersaoInvalid on \"orquestra\", got {err:?}",
21338        );
21339        // Sanity: the self-referential entry alone under the same
21340        // parent `:nome` trips the self-edge arm on its own — proves
21341        // the per-entry-first surfacing above is a real ordering
21342        // property, not a case where the self-edge arm silently
21343        // accepts the fixture.
21344        let sanity = crate::dep::validate_no_self_dep(&[Dep::simple("demo", "^0.1")], &[], "demo")
21345            .unwrap_err();
21346        assert!(
21347            matches!(sanity, DepError::DepIsSelf { ref nome, .. } if nome == "demo"),
21348            "sanity: the self-referential entry alone must trip the \
21349             self-edge arm — got {sanity:?}",
21350        );
21351    }
21352
21353    #[test]
21354    fn validate_deps_accepts_clean_fixture() {
21355        // Positive control: a well-formed dep-graph (one `:deps`
21356        // entry naming a non-self DNS-1123 nome + Cargo-shaped
21357        // requirement, one `:deps-dev` entry on a distinct non-self
21358        // nome) passes the compound gate cleanly. A future
21359        // tightening of either arm's accepted set surfaces here as
21360        // a test failure first. Mirrors the peer
21361        // `validate_upgrade_from_accepts_clean_fixture` positive-
21362        // control posture on the sibling per-Caixa compound gate.
21363        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21364        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
21365        c.deps_dev = vec![Dep::simple("caixa-lint", "^0.2")];
21366        c.validate_deps()
21367            .expect("clean fixture must pass the compound `:deps` gate");
21368    }
21369
21370    #[test]
21371    fn validate_deps_accepts_empty_deps_lists() {
21372        // Positive control on the empty-list arm: a caixa without
21373        // any `:deps` or `:deps-dev` entries (the default
21374        // `Vec::new()` `#[serde(default)]` folds an omitted slot
21375        // onto) passes the compound gate cleanly regardless of
21376        // `:nome` — both the per-entry walk and the self-edge walk
21377        // are vacuous on the empty entry list. Pins the identity
21378        // element of the fold on the empty-slot side, peer with the
21379        // `validate_upgrade_from_accepts_empty_upgrade_from` empty-
21380        // arm positive control (d6801df) on the sibling
21381        // `:upgrade-from` compound gate.
21382        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21383        assert!(
21384            c.deps().is_empty(),
21385            "template caixa must carry an empty :deps — got {:?}",
21386            c.deps(),
21387        );
21388        assert!(
21389            c.deps_dev().is_empty(),
21390            "template caixa must carry an empty :deps-dev — got {:?}",
21391            c.deps_dev(),
21392        );
21393        c.validate_deps()
21394            .expect("empty :deps / :deps-dev must pass the compound gate cleanly");
21395    }
21396
21397    // ── Caixa::validate_aplicacao_shape — compound per-Caixa gate ────────
21398
21399    /// Build a minimal well-formed Aplicacao fixture on top of the
21400    /// canonical template. Every arm of the compound gate then patches
21401    /// exactly one axis away from clean so its per-arm diagnostic
21402    /// surfaces without collateral noise from a peer slot.
21403    fn aplicacao_fixture(nome: &str) -> Caixa {
21404        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
21405        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21406        c.kind = CaixaKind::Aplicacao;
21407        c.bibliotecas = vec![];
21408        c.membros = vec![
21409            Membro {
21410                caixa: "checkout".into(),
21411                versao: "^0.1".into(),
21412            },
21413            Membro {
21414                caixa: "cart".into(),
21415                versao: "^0.1".into(),
21416            },
21417        ];
21418        // `:placement` defaults to `Replicated` with an empty
21419        // `:clusters` list which
21420        // [`crate::AplicacaoSpec::validate_placement`] refuses; every
21421        // per-strategy variant needs at least one named cluster (per
21422        // MESH-COMPOSITION §II.1). Pin a single-cluster `SingleNode`
21423        // placement so the typed-shape cascade passes cleanly and the
21424        // per-arm fixtures below can each patch exactly one axis.
21425        c.placement = Some(Placement {
21426            estrategia: PlacementStrategy::SingleNode,
21427            clusters: vec!["rio".into()],
21428            shard_key: None,
21429            affinity: None,
21430        });
21431        c
21432    }
21433
21434    #[test]
21435    fn validate_aplicacao_shape_folds_view_arm_matches_gate() {
21436        // Fail-before-pass-after per-arm equivalence pin on the
21437        // typed-shape cascade arm: a fixture whose typed
21438        // [`crate::AplicacaoSpec`] view fails
21439        // [`crate::AplicacaoSpec::validate`] (here — empty `:membros`,
21440        // which [`crate::AplicacaoSpec::validate_membros`] rejects as
21441        // [`crate::AplicacaoError::NoMembros`] at the first per-slot
21442        // gate) surfaces the same [`crate::AplicacaoError`] diagnostic
21443        // through both the compound gate
21444        // [`Caixa::validate_aplicacao_shape`] and the standalone
21445        // [`crate::AplicacaoSpec::validate`] on the same folded view.
21446        // Pins the fold — a silent regression that de-folded the
21447        // typed-shape arm would surface here as a mismatch between the
21448        // two dispatches. Sibling in shape to the peer
21449        // `validate_deps_folds_per_entry_arm_matches_gate` (b5dd55e) /
21450        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21451        // (d6801df) per-arm equivalence pins on the sibling per-slot
21452        // compound gates.
21453        let mut c = aplicacao_fixture("demo");
21454        c.membros = vec![];
21455        let via_method = c.validate_aplicacao_shape().unwrap_err();
21456        let via_standalone = c.aplicacao_view().unwrap().validate().unwrap_err();
21457        assert_eq!(
21458            via_method, via_standalone,
21459            "Caixa::validate_aplicacao_shape must surface the typed-\
21460             shape arm's diagnostic byte-equal to the standalone \
21461             `AplicacaoSpec::validate` on the same folded view",
21462        );
21463        assert!(
21464            matches!(via_method, crate::AplicacaoError::NoMembros),
21465            "expected NoMembros on the empty :membros, got {via_method:?}",
21466        );
21467    }
21468
21469    #[test]
21470    fn validate_aplicacao_shape_folds_self_membership_arm_matches_gate() {
21471        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21472        // fixture whose `:membros` names the Aplicacao's own `:nome`
21473        // (which [`crate::aplicacao::validate_no_self_membership`]
21474        // rejects as [`crate::AplicacaoError::MembroIsSelfAplicacao`],
21475        // a one-node lacre-closure recursion in the Aplicacao's
21476        // mesh-graph) surfaces the same
21477        // [`crate::AplicacaoError::MembroIsSelfAplicacao`] through both
21478        // the compound gate and the standalone
21479        // [`crate::aplicacao::validate_no_self_membership`] keyed off
21480        // the same `(membros, nome)` pair. Pins the fold's second arm
21481        // — reaching this arm through the compound gate requires the
21482        // typed-shape cascade to pass first, which itself pins one
21483        // cross-arm ordering step. Sibling in shape to the peer
21484        // `validate_deps_folds_self_edge_arm_matches_gate` (b5dd55e)
21485        // cross-slot equivalence pin on the sibling per-slot compound
21486        // gate.
21487        use crate::aplicacao::Membro;
21488        let mut c = aplicacao_fixture("demo");
21489        c.membros = vec![Membro {
21490            caixa: "demo".into(),
21491            versao: "^0.1".into(),
21492        }];
21493        let via_method = c.validate_aplicacao_shape().unwrap_err();
21494        let via_standalone =
21495            crate::aplicacao::validate_no_self_membership(c.membros(), c.nome()).unwrap_err();
21496        assert_eq!(
21497            via_method, via_standalone,
21498            "Caixa::validate_aplicacao_shape must surface the cross-\
21499             slot self-edge diagnostic byte-equal to the standalone \
21500             `aplicacao::validate_no_self_membership` on the same \
21501             (membros, nome) pair",
21502        );
21503        assert!(
21504            matches!(
21505                via_method,
21506                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa } if caixa == "demo"
21507            ),
21508            "expected MembroIsSelfAplicacao carrying (caixa=\"demo\"), \
21509             got {via_method:?}",
21510        );
21511    }
21512
21513    #[test]
21514    fn validate_aplicacao_shape_view_arm_fires_before_self_membership_arm() {
21515        // Cross-arm ordering pin between the two arms of the fold: a
21516        // fixture carrying BOTH a typed-shape violation (a `:contratos`
21517        // edge whose `:para` is not a declared member — rejected by
21518        // [`crate::AplicacaoSpec::validate_contratos`] as
21519        // [`crate::AplicacaoError::ContratoMemberMissing`]) AND a
21520        // would-be self-edge violation (a `:membros` entry naming the
21521        // caixa's own `:nome`) surfaces the typed-shape diagnostic
21522        // first through the compound gate. Sanity assertion: the
21523        // self-referential `:membros` entry alone under the same
21524        // parent `:nome` trips the self-edge arm on its own via the
21525        // standalone [`crate::aplicacao::validate_no_self_membership`],
21526        // so the typed-shape-first surfacing is a real ordering
21527        // property, not a case where the self-edge arm silently
21528        // accepts the fixture. Pins the pre-fold layout wire-up's
21529        // canonical dispatch order (typed-shape cascade → cross-slot
21530        // self-edge) as a property of the substrate primitive rather
21531        // than a convention of the layout call site. Sibling in shape
21532        // to `validate_deps_per_entry_arm_fires_before_self_edge_arm`
21533        // (b5dd55e) on the sibling per-slot compound gate's per-arm
21534        // ordering property.
21535        use crate::aplicacao::{Membro, WitContract};
21536        let mut c = aplicacao_fixture("demo");
21537        c.membros = vec![Membro {
21538            caixa: "demo".into(),
21539            versao: "^0.1".into(),
21540        }];
21541        c.contratos = vec![WitContract {
21542            de: "demo".into(),
21543            para: "orphan".into(),
21544            wit: "wasi:http/proxy".into(),
21545            endpoint: Some("/x".into()),
21546            subject: None,
21547            slot: None,
21548        }];
21549        let err = c.validate_aplicacao_shape().unwrap_err();
21550        assert!(
21551            matches!(
21552                err,
21553                crate::AplicacaoError::ContratoMemberMissing { ref caixa }
21554                    if caixa == "orphan"
21555            ),
21556            "typed-shape arm must fire before self-edge arm — expected \
21557             ContratoMemberMissing on \"orphan\", got {err:?}",
21558        );
21559        // Sanity: the self-referential `:membros` entry alone under
21560        // the same parent `:nome` trips the self-edge arm on its own
21561        // — proves the typed-shape-first surfacing above is a real
21562        // ordering property, not a case where the self-edge arm
21563        // silently accepts the fixture.
21564        let sanity = crate::aplicacao::validate_no_self_membership(
21565            &[Membro {
21566                caixa: "demo".into(),
21567                versao: "^0.1".into(),
21568            }],
21569            "demo",
21570        )
21571        .unwrap_err();
21572        assert!(
21573            matches!(
21574                sanity,
21575                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa }
21576                    if caixa == "demo"
21577            ),
21578            "sanity: the self-referential :membros entry alone must \
21579             trip the self-edge arm — got {sanity:?}",
21580        );
21581    }
21582
21583    #[test]
21584    fn validate_aplicacao_shape_accepts_non_aplicacao_kind() {
21585        // Positive control on the identity-element arm: every non-
21586        // Aplicacao kind passes the compound gate trivially — the
21587        // paired [`Caixa::aplicacao_view`] accessor returns `None`
21588        // off the Aplicacao arm (by construction, keyed on
21589        // `caixa.kind().is_aplicacao()`), so the fold short-circuits
21590        // to `Ok(())` without touching the mesh slots. Pins the
21591        // identity element on every non-Aplicacao kind — a future
21592        // refactor that made the mesh-slot cascade fire on the wrong
21593        // kind (say, on a `Servico` whose mesh slots happen to be
21594        // populated in a mis-authored manifest, which the peer
21595        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
21596        // coherence gate would refuse upstream anyway) surfaces here
21597        // as a test failure first. Peer with the
21598        // `validate_limits_accepts_none` / `validate_behavior_accepts_none`
21599        // identity-element pins on the sibling M2 `Option`-shaped
21600        // per-Caixa compound gates.
21601        for kind in [
21602            CaixaKind::Biblioteca,
21603            CaixaKind::Binario,
21604            CaixaKind::Servico,
21605            CaixaKind::Supervisor,
21606            CaixaKind::Acao,
21607        ] {
21608            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21609            c.kind = kind;
21610            assert!(
21611                c.aplicacao_view().is_none(),
21612                "aplicacao_view must return None off the Aplicacao arm \
21613                 for kind {kind:?}",
21614            );
21615            c.validate_aplicacao_shape().expect(
21616                "non-Aplicacao kinds must pass the compound gate as the fold's identity element",
21617            );
21618        }
21619    }
21620
21621    #[test]
21622    fn validate_aplicacao_shape_accepts_clean_fixture() {
21623        // Positive control: a well-formed Aplicacao (two DNS-1123
21624        // members with valid semver constraints, no `:contratos` /
21625        // `:entrada` / `:placement` / `:politicas` set — every
21626        // per-slot gate accepts the vacuous / omitted arm) passes the
21627        // compound gate cleanly. A future tightening of either arm's
21628        // accepted set surfaces here as a test failure first. Mirrors
21629        // the peer `validate_deps_accepts_clean_fixture` (b5dd55e) /
21630        // `validate_upgrade_from_accepts_clean_fixture` (d6801df)
21631        // positive-control postures on the sibling per-Caixa
21632        // compound gates.
21633        let c = aplicacao_fixture("demo");
21634        c.validate_aplicacao_shape()
21635            .expect("clean Aplicacao fixture must pass the compound gate");
21636    }
21637
21638    // ── Caixa::validate_supervisor_shape — compound per-Caixa gate ───────
21639
21640    /// Build a minimal well-formed Supervisor fixture on top of the
21641    /// canonical template. Every arm of the compound gate then patches
21642    /// exactly one axis away from clean so its per-arm diagnostic
21643    /// surfaces without collateral noise from a peer slot. Peer of
21644    /// [`aplicacao_fixture`] on the sibling per-Aplicacao compound
21645    /// gate's pin family.
21646    fn supervisor_fixture(nome: &str) -> Caixa {
21647        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
21648        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21649        c.kind = CaixaKind::Supervisor;
21650        // Supervisors don't run code — clear the biblioteca slot the
21651        // template seeds so the fold's per-arm diagnostics surface
21652        // without the peer `SupervisorOwnsCode` kind-coherence gate
21653        // firing upstream at the layout altitude.
21654        c.bibliotecas = vec![];
21655        // `:estrategia` defaults to `OneForOne` at the typed view level,
21656        // and `OneForOne` requires at least one `:children` entry — pin
21657        // a single-child `Permanent` worker so the typed-shape cascade
21658        // passes cleanly and the per-arm fixtures below can each patch
21659        // exactly one axis.
21660        c.estrategia = Some(RestartStrategy::OneForOne);
21661        c.children = vec![ChildSpec {
21662            caixa: "worker".into(),
21663            versao: "^0.1".into(),
21664            restart: RestartPolicy::Permanent,
21665        }];
21666        c
21667    }
21668
21669    #[test]
21670    fn validate_supervisor_shape_folds_view_arm_matches_gate() {
21671        // Fail-before-pass-after per-arm equivalence pin on the
21672        // typed-shape cascade arm: a fixture whose typed
21673        // [`crate::SupervisorSpec`] view fails
21674        // [`crate::SupervisorSpec::validate`] (here — a duplicate
21675        // `:children` `:caixa` entry, which
21676        // [`crate::SupervisorSpec::validate`]'s set-not-multiset gate
21677        // rejects as [`crate::SupervisorError::DuplicateChildCaixa`])
21678        // surfaces the same [`crate::SupervisorError`] diagnostic
21679        // through both the compound gate
21680        // [`Caixa::validate_supervisor_shape`] and the standalone
21681        // [`crate::SupervisorSpec::validate`] on the same folded view.
21682        // Pins the fold — a silent regression that de-folded the
21683        // typed-shape arm would surface here as a mismatch between the
21684        // two dispatches. Sibling in shape to the peer
21685        // `validate_aplicacao_shape_folds_view_arm_matches_gate`
21686        // (949a7a0) on the sibling per-Aplicacao compound gate.
21687        use crate::supervisor::{ChildSpec, RestartPolicy};
21688        let mut c = supervisor_fixture("demo");
21689        c.children = vec![
21690            ChildSpec {
21691                caixa: "worker".into(),
21692                versao: "^0.1".into(),
21693                restart: RestartPolicy::Permanent,
21694            },
21695            ChildSpec {
21696                caixa: "worker".into(),
21697                versao: "^0.1".into(),
21698                restart: RestartPolicy::Permanent,
21699            },
21700        ];
21701        let via_method = c.validate_supervisor_shape().unwrap_err();
21702        let via_standalone = c.supervisor_view().unwrap().validate().unwrap_err();
21703        assert_eq!(
21704            via_method, via_standalone,
21705            "Caixa::validate_supervisor_shape must surface the typed-\
21706             shape arm's diagnostic byte-equal to the standalone \
21707             `SupervisorSpec::validate` on the same folded view",
21708        );
21709        assert!(
21710            matches!(
21711                via_method,
21712                crate::SupervisorError::DuplicateChildCaixa { ref caixa }
21713                    if caixa == "worker"
21714            ),
21715            "expected DuplicateChildCaixa on the duplicate 'worker' \
21716             child, got {via_method:?}",
21717        );
21718    }
21719
21720    #[test]
21721    fn validate_supervisor_shape_folds_self_supervision_arm_matches_gate() {
21722        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21723        // fixture whose `:children :caixa` names the Supervisor's own
21724        // `:nome` (which
21725        // [`crate::supervisor::validate_no_self_supervision`] rejects
21726        // as [`crate::SupervisorError::ChildSupervisesSelf`], a
21727        // one-node reconciliation cycle in the supervisor's
21728        // supervision-tree) surfaces the same
21729        // [`crate::SupervisorError::ChildSupervisesSelf`] through both
21730        // the compound gate and the standalone
21731        // [`crate::supervisor::validate_no_self_supervision`] keyed
21732        // off the same `(children, nome)` pair. Pins the fold's
21733        // second arm — reaching this arm through the compound gate
21734        // requires the typed-shape cascade to pass first, which itself
21735        // pins one cross-arm ordering step. Sibling in shape to the
21736        // peer
21737        // `validate_aplicacao_shape_folds_self_membership_arm_matches_gate`
21738        // (949a7a0) cross-slot equivalence pin on the sibling
21739        // per-Aplicacao compound gate.
21740        use crate::supervisor::{ChildSpec, RestartPolicy};
21741        let mut c = supervisor_fixture("demo");
21742        c.children = vec![ChildSpec {
21743            caixa: "demo".into(),
21744            versao: "^0.1".into(),
21745            restart: RestartPolicy::Permanent,
21746        }];
21747        let via_method = c.validate_supervisor_shape().unwrap_err();
21748        let via_standalone =
21749            crate::supervisor::validate_no_self_supervision(c.children(), c.nome()).unwrap_err();
21750        assert_eq!(
21751            via_method, via_standalone,
21752            "Caixa::validate_supervisor_shape must surface the cross-\
21753             slot self-edge diagnostic byte-equal to the standalone \
21754             `supervisor::validate_no_self_supervision` on the same \
21755             (children, nome) pair",
21756        );
21757        assert!(
21758            matches!(
21759                via_method,
21760                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21761            ),
21762            "expected ChildSupervisesSelf carrying (caixa=\"demo\"), \
21763             got {via_method:?}",
21764        );
21765    }
21766
21767    #[test]
21768    fn validate_supervisor_shape_view_arm_fires_before_self_supervision_arm() {
21769        // Cross-arm ordering pin between the two arms of the fold: a
21770        // fixture carrying BOTH a typed-shape violation (a per-child
21771        // empty `:caixa` name — rejected by
21772        // [`crate::SupervisorSpec::validate`] as
21773        // [`crate::SupervisorError::EmptyChildName`]) AND a would-be
21774        // self-edge violation (a `:children` entry naming the
21775        // supervisor's own `:nome`) surfaces the typed-shape
21776        // diagnostic first through the compound gate. Sanity
21777        // assertion: the self-referential `:children` entry alone
21778        // under the same parent `:nome` trips the self-edge arm on
21779        // its own via the standalone
21780        // [`crate::supervisor::validate_no_self_supervision`], so the
21781        // typed-shape-first surfacing is a real ordering property, not
21782        // a case where the self-edge arm silently accepts the fixture.
21783        // Pins the pre-fold layout wire-up's canonical dispatch order
21784        // (typed-shape cascade → cross-slot self-edge) as a property
21785        // of the substrate primitive rather than a convention of the
21786        // layout call site. Sibling in shape to
21787        // `validate_aplicacao_shape_view_arm_fires_before_self_membership_arm`
21788        // (949a7a0) on the sibling per-Aplicacao compound gate.
21789        use crate::supervisor::{ChildSpec, RestartPolicy};
21790        let mut c = supervisor_fixture("demo");
21791        c.children = vec![
21792            ChildSpec {
21793                caixa: String::new(),
21794                versao: "^0.1".into(),
21795                restart: RestartPolicy::Permanent,
21796            },
21797            ChildSpec {
21798                caixa: "demo".into(),
21799                versao: "^0.1".into(),
21800                restart: RestartPolicy::Permanent,
21801            },
21802        ];
21803        let err = c.validate_supervisor_shape().unwrap_err();
21804        assert!(
21805            matches!(err, crate::SupervisorError::EmptyChildName),
21806            "typed-shape arm must fire before self-edge arm — expected \
21807             EmptyChildName on the empty :caixa child, got {err:?}",
21808        );
21809        // Sanity: the self-referential `:children` entry alone under
21810        // the same parent `:nome` trips the self-edge arm on its own
21811        // — proves the typed-shape-first surfacing above is a real
21812        // ordering property, not a case where the self-edge arm
21813        // silently accepts the fixture.
21814        let sanity = crate::supervisor::validate_no_self_supervision(
21815            &[ChildSpec {
21816                caixa: "demo".into(),
21817                versao: "^0.1".into(),
21818                restart: RestartPolicy::Permanent,
21819            }],
21820            "demo",
21821        )
21822        .unwrap_err();
21823        assert!(
21824            matches!(
21825                sanity,
21826                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21827            ),
21828            "sanity: the self-referential :children entry alone must \
21829             trip the self-edge arm — got {sanity:?}",
21830        );
21831    }
21832
21833    #[test]
21834    fn validate_supervisor_shape_accepts_non_supervisor_kind() {
21835        // Positive control on the identity-element arm: every non-
21836        // Supervisor kind passes the compound gate trivially — the
21837        // paired [`Caixa::supervisor_view`] accessor returns `None`
21838        // off the Supervisor arm (by construction, keyed on
21839        // `caixa.kind().is_supervisor()`), so the fold short-circuits
21840        // to `Ok(())` without touching the supervision-tree slots.
21841        // Pins the identity element on every non-Supervisor kind — a
21842        // future refactor that made the supervision-tree cascade fire
21843        // on the wrong kind (say, on a `Servico` whose supervision
21844        // slots happen to be populated in a mis-authored manifest,
21845        // which the peer
21846        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
21847        // kind-coherence gate would refuse upstream anyway) surfaces
21848        // here as a test failure first. Peer with the
21849        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
21850        // (949a7a0) / `validate_limits_accepts_none` /
21851        // `validate_behavior_accepts_none` identity-element pins on
21852        // the sibling per-Caixa compound gates.
21853        for kind in [
21854            CaixaKind::Biblioteca,
21855            CaixaKind::Binario,
21856            CaixaKind::Servico,
21857            CaixaKind::Aplicacao,
21858            CaixaKind::Acao,
21859        ] {
21860            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21861            c.kind = kind;
21862            assert!(
21863                c.supervisor_view().is_none(),
21864                "supervisor_view must return None off the Supervisor \
21865                 arm for kind {kind:?}",
21866            );
21867            c.validate_supervisor_shape().expect(
21868                "non-Supervisor kinds must pass the compound gate as the fold's identity element",
21869            );
21870        }
21871    }
21872
21873    #[test]
21874    fn validate_supervisor_shape_accepts_clean_fixture() {
21875        // Positive control: a well-formed Supervisor (single
21876        // DNS-1123-valid `Permanent` worker child under the
21877        // `OneForOne` strategy — the OTP MaxIntensity/Period defaults
21878        // accept the vacuous `:max-restarts` / `:restart-window`
21879        // arms) passes the compound gate cleanly. A future tightening
21880        // of either arm's accepted set surfaces here as a test
21881        // failure first. Mirrors the peer
21882        // `validate_aplicacao_shape_accepts_clean_fixture` (949a7a0)
21883        // positive-control posture on the sibling per-Caixa compound
21884        // gate.
21885        let c = supervisor_fixture("demo");
21886        c.validate_supervisor_shape()
21887            .expect("clean Supervisor fixture must pass the compound gate");
21888    }
21889
21890    // ── Caixa::validate_acao_shape — compound per-Caixa gate ─────────────
21891
21892    /// Build a minimal well-formed `:kind Acao` fixture with a valid
21893    /// two-node acyclic `:ci` slot. Every arm of the compound gate
21894    /// then patches exactly one axis away from clean so its per-arm
21895    /// diagnostic surfaces without collateral noise from a peer slot.
21896    /// Peer of [`supervisor_fixture`] / [`aplicacao_fixture`] on the
21897    /// sibling per-kind compound gates' pin families.
21898    fn acao_fixture(nome: &str) -> Caixa {
21899        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21900        c.kind = CaixaKind::Acao;
21901        // Acaos don't run code — clear the biblioteca slot the template
21902        // seeds so the compound gate's per-arm diagnostics surface
21903        // without the peer `AcaoOwnsCode` kind-coherence gate firing
21904        // upstream at the layout altitude.
21905        c.bibliotecas = vec![];
21906        c.ci = Some(canteiro_types::CiRun {
21907            workspace: "pleme-io".into(),
21908            repo: "caixa".into(),
21909            nodes: vec![
21910                canteiro_types::CiNode::new(
21911                    "build",
21912                    canteiro_types::EnvClass::None,
21913                    canteiro_types::ActionRef {
21914                        name: "build".into(),
21915                        command: "true".into(),
21916                        args: vec![],
21917                    },
21918                    vec![],
21919                ),
21920                canteiro_types::CiNode::new(
21921                    "test",
21922                    canteiro_types::EnvClass::None,
21923                    canteiro_types::ActionRef {
21924                        name: "test".into(),
21925                        command: "true".into(),
21926                        args: vec![],
21927                    },
21928                    vec!["build".into()],
21929                ),
21930            ],
21931        });
21932        c
21933    }
21934
21935    #[test]
21936    fn validate_acao_shape_folds_decompose_arm_matches_gate() {
21937        // Fail-before-pass-after per-arm equivalence pin on the
21938        // decompose axis: a fixture whose `:ci` slot fails
21939        // [`canteiro_types::decompose`] (here — a minimal two-node
21940        // cycle `a → b → a`, which the sibling
21941        // [`crate::render::decompose_ci`] wraps as
21942        // [`crate::CiDecomposeFailure`] carrying
21943        // [`canteiro_types::DecomposeError::Cycle`]) surfaces the same
21944        // [`crate::CiDecomposeFailure`] diagnostic through both the
21945        // compound gate [`Caixa::validate_acao_shape`] and the
21946        // standalone [`crate::render::decompose_ci`] on the same
21947        // `(caixa, ci)` fixture. Pins the fold — a silent regression
21948        // that de-folded the decompose arm would surface here as a
21949        // mismatch between the two dispatches. Sibling in shape to the
21950        // peer `validate_supervisor_shape_folds_view_arm_matches_gate`
21951        // / `validate_aplicacao_shape_folds_view_arm_matches_gate` on
21952        // the sibling per-kind compound gates.
21953        //
21954        // [`crate::CiDecomposeFailure`] does not derive `PartialEq`
21955        // (its `#[source]` carrier [`canteiro_types::DecomposeError`]
21956        // does, but the wrapper deliberately does not), so the two
21957        // dispatches are compared through their field pair
21958        // (`nome` + `source`) rather than through `assert_eq!` on the
21959        // wrapper itself — every field on the wrapper is thereby
21960        // pinned byte-equal without depending on an implementation
21961        // detail of `CiDecomposeFailure`'s derive set.
21962        let mut c = acao_fixture("demo");
21963        c.ci = Some(canteiro_types::CiRun {
21964            workspace: "pleme-io".into(),
21965            repo: "caixa".into(),
21966            nodes: vec![
21967                canteiro_types::CiNode::new(
21968                    "a",
21969                    canteiro_types::EnvClass::None,
21970                    canteiro_types::ActionRef {
21971                        name: "a".into(),
21972                        command: "true".into(),
21973                        args: vec![],
21974                    },
21975                    vec!["b".into()],
21976                ),
21977                canteiro_types::CiNode::new(
21978                    "b",
21979                    canteiro_types::EnvClass::None,
21980                    canteiro_types::ActionRef {
21981                        name: "b".into(),
21982                        command: "true".into(),
21983                        args: vec![],
21984                    },
21985                    vec!["a".into()],
21986                ),
21987            ],
21988        });
21989        let via_method = c.validate_acao_shape().unwrap_err();
21990        let via_standalone =
21991            crate::render::decompose_ci(&c, c.ci().expect("fixture has a :ci")).unwrap_err();
21992        assert_eq!(
21993            via_method.nome, via_standalone.nome,
21994            "Caixa::validate_acao_shape must surface the decompose \
21995             failure's `nome` byte-equal to the standalone \
21996             `decompose_ci` on the same (caixa, ci) fixture",
21997        );
21998        assert_eq!(
21999            via_method.source, via_standalone.source,
22000            "Caixa::validate_acao_shape must surface the decompose \
22001             failure's `source` byte-equal to the standalone \
22002             `decompose_ci` on the same (caixa, ci) fixture",
22003        );
22004        assert_eq!(
22005            via_method.source,
22006            canteiro_types::DecomposeError::Cycle,
22007            "expected the two-node cycle `a → b → a` to surface as \
22008             DecomposeError::Cycle, got {source:?}",
22009            source = via_method.source,
22010        );
22011    }
22012
22013    #[test]
22014    fn validate_acao_shape_folds_duplicate_node_arm_matches_gate() {
22015        // Per-arm equivalence pin on the `DuplicateNode` decompose
22016        // arm — the sibling of `Cycle` on the substrate's
22017        // `canteiro_types::DecomposeError` enumeration. A fixture
22018        // whose `:ci` slot carries two nodes sharing one name
22019        // surfaces the same [`crate::CiDecomposeFailure`] through
22020        // both dispatches, pinned by field pair. The three
22021        // decompose arms (`DuplicateNode` / `UnknownDep` / `Cycle`)
22022        // together enumerate every failure mode
22023        // [`canteiro_types::decompose`] refuses, so the per-arm
22024        // pins collectively cover the whole decompose axis.
22025        let mut c = acao_fixture("demo");
22026        c.ci = Some(canteiro_types::CiRun {
22027            workspace: "pleme-io".into(),
22028            repo: "caixa".into(),
22029            nodes: vec![
22030                canteiro_types::CiNode::new(
22031                    "twin",
22032                    canteiro_types::EnvClass::None,
22033                    canteiro_types::ActionRef {
22034                        name: "twin".into(),
22035                        command: "true".into(),
22036                        args: vec![],
22037                    },
22038                    vec![],
22039                ),
22040                canteiro_types::CiNode::new(
22041                    "twin",
22042                    canteiro_types::EnvClass::None,
22043                    canteiro_types::ActionRef {
22044                        name: "twin".into(),
22045                        command: "true".into(),
22046                        args: vec![],
22047                    },
22048                    vec![],
22049                ),
22050            ],
22051        });
22052        let via_method = c.validate_acao_shape().unwrap_err();
22053        assert_eq!(
22054            via_method.source,
22055            canteiro_types::DecomposeError::DuplicateNode("twin".into()),
22056            "expected DuplicateNode on the two-\"twin\"-name fixture, \
22057             got {source:?}",
22058            source = via_method.source,
22059        );
22060    }
22061
22062    #[test]
22063    fn validate_acao_shape_folds_unknown_dep_arm_matches_gate() {
22064        // Per-arm equivalence pin on the `UnknownDep` decompose arm —
22065        // the third and last arm on `canteiro_types::DecomposeError`
22066        // after `Cycle` and `DuplicateNode`. A fixture whose `:ci`
22067        // slot names a `deps` entry no declared node satisfies
22068        // surfaces the same [`crate::CiDecomposeFailure`] through
22069        // both dispatches. Pins the third decompose arm at the
22070        // compound gate.
22071        let mut c = acao_fixture("demo");
22072        c.ci = Some(canteiro_types::CiRun {
22073            workspace: "pleme-io".into(),
22074            repo: "caixa".into(),
22075            nodes: vec![canteiro_types::CiNode::new(
22076                "orphan",
22077                canteiro_types::EnvClass::None,
22078                canteiro_types::ActionRef {
22079                    name: "orphan".into(),
22080                    command: "true".into(),
22081                    args: vec![],
22082                },
22083                vec!["ghost".into()],
22084            )],
22085        });
22086        let via_method = c.validate_acao_shape().unwrap_err();
22087        assert_eq!(
22088            via_method.source,
22089            canteiro_types::DecomposeError::UnknownDep {
22090                node: "orphan".into(),
22091                dep: "ghost".into(),
22092            },
22093            "expected UnknownDep on the orphan-node-depends-on-ghost \
22094             fixture, got {source:?}",
22095            source = via_method.source,
22096        );
22097    }
22098
22099    #[test]
22100    fn validate_acao_shape_accepts_non_acao_kind() {
22101        // Positive control on the identity-element arm: every non-
22102        // Acao kind passes the compound gate trivially — the paired
22103        // `caixa.kind().is_acao()` guard short-circuits before the
22104        // decompose gate ever fires, so the fold returns `Ok(())`
22105        // without touching the `:ci` slot even when a non-Acao
22106        // fixture happens to declare one (the sibling
22107        // [`crate::LayoutError::CiOnNonAcao`] kind-coherence gate
22108        // catches that at the layout altitude anyway). Pins the
22109        // identity element on every non-Acao kind. Peer with the
22110        // `validate_supervisor_shape_accepts_non_supervisor_kind` /
22111        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
22112        // identity-element pins on the sibling per-Caixa compound
22113        // gates.
22114        for kind in [
22115            CaixaKind::Biblioteca,
22116            CaixaKind::Binario,
22117            CaixaKind::Servico,
22118            CaixaKind::Supervisor,
22119            CaixaKind::Aplicacao,
22120        ] {
22121            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22122            c.kind = kind;
22123            c.validate_acao_shape().expect(
22124                "non-Acao kinds must pass the compound gate as the fold's identity element",
22125            );
22126        }
22127    }
22128
22129    #[test]
22130    fn validate_acao_shape_accepts_absent_ci_slot() {
22131        // Positive control on the second identity-element arm: a
22132        // `:kind Acao` caixa with `ci = None` passes the compound
22133        // gate trivially — the presence gate is the sibling axis
22134        // owned by [`crate::LayoutError::MissingCi`] /
22135        // [`crate::require_ci`] / [`crate::MissingCiSlot`], not by
22136        // the decompose gate. A caixa that carries no `:ci` slot
22137        // has no run to decompose, so the fold's `let Some(ci) = …
22138        // else { return Ok(()) }` arm short-circuits before the
22139        // decompose gate fires. Pins that the two axes stay
22140        // separately diagnosable at the layout altitude — a future
22141        // regression that collapsed the presence gate onto the
22142        // shape gate here would land a
22143        // [`crate::CiDecomposeFailure`] on the wrong axis and
22144        // surface an off-target diagnostic at `feira build` time.
22145        let mut c = acao_fixture("demo");
22146        c.ci = None;
22147        c.validate_acao_shape().expect(
22148            "an :kind Acao caixa with absent :ci must pass the compound gate — \
22149             the presence gate is layout's MissingCi axis, not the decompose gate",
22150        );
22151    }
22152
22153    #[test]
22154    fn validate_acao_shape_accepts_clean_fixture() {
22155        // Positive control: a well-formed Acao (a two-node acyclic
22156        // `:ci` run with `test` depending on `build`) passes the
22157        // compound gate cleanly. A future tightening of the
22158        // decompose gate's accepted set surfaces here as a test
22159        // failure first. Mirrors the peer
22160        // `validate_supervisor_shape_accepts_clean_fixture` /
22161        // `validate_aplicacao_shape_accepts_clean_fixture`
22162        // positive-control posture on the sibling per-Caixa
22163        // compound gates.
22164        let c = acao_fixture("demo");
22165        c.validate_acao_shape()
22166            .expect("clean Acao fixture must pass the compound gate");
22167    }
22168
22169    fn bare_servico_fixture(nome: &str) -> Caixa {
22170        // A minimal Servico caixa with no code and no typed slots —
22171        // the cross-family fold's identity element on every arm.
22172        // Clears the biblioteca slot the template seeds so the
22173        // per-arm patches below can each add exactly one typed slot
22174        // without a peer `ServicoOwnsCode` / layout-side kind-gate
22175        // firing upstream.
22176        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
22177        c.kind = CaixaKind::Servico;
22178        c.bibliotecas = vec![];
22179        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22180        c
22181    }
22182
22183    #[test]
22184    fn validate_kind_slot_coherence_folds_mesh_arm_matches_gate() {
22185        // Fail-before-pass-after per-arm equivalence pin on the M3
22186        // mesh-slot arm of the cross-family kind-coherence fold: a
22187        // non-Aplicacao caixa carrying a declared M3 mesh slot (here
22188        // a `:kind Servico` fixture with a single `:membros` entry —
22189        // the smallest possible M3 slot declaration on a foreign
22190        // kind) surfaces the same
22191        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] variant
22192        // through both the compound gate
22193        // [`Caixa::validate_kind_slot_coherence`] and the standalone
22194        // constructor [`crate::LayoutError::mesh_slots_on_non_aplicacao`]
22195        // dispatched on the same `declared_mesh_slots` list. Pins
22196        // the fold — a silent regression that de-folded the mesh
22197        // arm would surface here as a mismatch between the two
22198        // dispatches. Sibling in shape to the peer
22199        // `validate_aplicacao_shape_folds_view_arm_matches_gate` /
22200        // `validate_supervisor_shape_folds_view_arm_matches_gate` /
22201        // `validate_acao_shape_folds_decompose_arm_matches_gate`
22202        // per-arm equivalence pins on the sibling per-kind compound
22203        // gates.
22204        use crate::aplicacao::Membro;
22205        let mut c = bare_servico_fixture("demo");
22206        c.membros = vec![Membro {
22207            caixa: "cart".into(),
22208            versao: "^0.1".into(),
22209        }];
22210        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22211        let via_standalone =
22212            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22213        assert_eq!(
22214            via_method, via_standalone,
22215            "Caixa::validate_kind_slot_coherence must surface the M3 \
22216             mesh-slot arm's diagnostic byte-equal to the standalone \
22217             LayoutError::mesh_slots_on_non_aplicacao ctor on the same \
22218             declared_mesh_slots list",
22219        );
22220    }
22221
22222    #[test]
22223    fn validate_kind_slot_coherence_folds_supervisor_arm_matches_gate() {
22224        // Per-arm equivalence pin on the supervisor-tree arm — the
22225        // sibling of the mesh arm on the cross-family fold. A
22226        // non-Supervisor caixa carrying a declared supervisor slot
22227        // (a `:kind Servico` fixture with `:estrategia` set — the
22228        // smallest possible supervisor slot declaration on a
22229        // foreign kind) surfaces the same
22230        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
22231        // variant through both dispatches, pinned by field pair
22232        // through `PartialEq`.
22233        use crate::supervisor::RestartStrategy;
22234        let mut c = bare_servico_fixture("demo");
22235        c.estrategia = Some(RestartStrategy::OneForOne);
22236        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22237        let via_standalone = crate::LayoutError::supervisor_slots_on_non_supervisor(
22238            &c,
22239            c.declared_supervisor_slots(),
22240        );
22241        assert_eq!(
22242            via_method, via_standalone,
22243            "Caixa::validate_kind_slot_coherence must surface the \
22244             supervisor-tree arm's diagnostic byte-equal to the \
22245             standalone LayoutError::supervisor_slots_on_non_supervisor \
22246             ctor on the same declared_supervisor_slots list",
22247        );
22248    }
22249
22250    #[test]
22251    fn validate_kind_slot_coherence_folds_servico_arm_matches_gate() {
22252        // Per-arm equivalence pin on the M2 Servico-runtime arm —
22253        // the third and last arm on the cross-family fold. A
22254        // non-Servico caixa carrying a declared M2 slot (a `:kind
22255        // Biblioteca` fixture with `:limits` set — the smallest
22256        // possible M2 slot declaration on a foreign kind) surfaces
22257        // the same [`crate::LayoutError::ServicoSlotsOnNonServico`]
22258        // variant through both dispatches. The three arms together
22259        // enumerate every typed-slot family the substrate carries
22260        // whose "declared but ignored" footgun is gated at the
22261        // layout altitude by a `{ caixa, kind, slots }` wrap variant,
22262        // so the per-arm pins collectively cover the whole
22263        // cross-family kind-coherence axis.
22264        use crate::limits::LimitsSpec;
22265        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22266        c.kind = CaixaKind::Biblioteca;
22267        c.limits = Some(LimitsSpec {
22268            memory: Some(64 * 1024 * 1024),
22269            fuel: None,
22270            wall_clock: None,
22271            cpu: None,
22272        });
22273        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22274        let via_standalone =
22275            crate::LayoutError::servico_slots_on_non_servico(&c, c.declared_servico_slots());
22276        assert_eq!(
22277            via_method, via_standalone,
22278            "Caixa::validate_kind_slot_coherence must surface the M2 \
22279             Servico-runtime arm's diagnostic byte-equal to the \
22280             standalone LayoutError::servico_slots_on_non_servico ctor \
22281             on the same declared_servico_slots list",
22282        );
22283    }
22284
22285    #[test]
22286    fn validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm() {
22287        // Cross-arm ordering pin between the first two arms of the
22288        // fold: a fixture carrying BOTH a declared M3 mesh slot
22289        // (`:membros`) AND a declared supervisor-tree slot
22290        // (`:estrategia`) on a foreign kind (a `:kind Servico` here —
22291        // foreign to both the Aplicacao arm and the Supervisor arm)
22292        // surfaces the M3 mesh diagnostic first through the compound
22293        // gate. Pins the pre-fold layout wire-up's canonical
22294        // diagnostic sequence (mesh → supervisor → servico) as a
22295        // property of the substrate primitive rather than a
22296        // convention of the layout call site. A silent reordering
22297        // regression at the primitive would surface here as a
22298        // wrong-variant match before landing at a downstream
22299        // consumer's diagnostic-ordering expectation.
22300        use crate::aplicacao::Membro;
22301        use crate::supervisor::RestartStrategy;
22302        let mut c = bare_servico_fixture("demo");
22303        c.membros = vec![Membro {
22304            caixa: "cart".into(),
22305            versao: "^0.1".into(),
22306        }];
22307        c.estrategia = Some(RestartStrategy::OneForOne);
22308        let err = c.validate_kind_slot_coherence().unwrap_err();
22309        assert!(
22310            matches!(err, crate::LayoutError::MeshSlotsOnNonAplicacao { .. }),
22311            "expected MeshSlotsOnNonAplicacao to fire before \
22312             SupervisorSlotsOnNonSupervisor under the canonical \
22313             mesh → supervisor → servico order, got {err:?}",
22314        );
22315    }
22316
22317    #[test]
22318    fn validate_kind_slot_coherence_supervisor_arm_fires_before_servico_arm() {
22319        // Cross-arm ordering pin between the second and third arms
22320        // of the fold: a fixture carrying BOTH a declared
22321        // supervisor-tree slot (`:estrategia`) AND a declared M2 slot
22322        // (`:limits`) on a kind foreign to both (a `:kind Biblioteca`
22323        // here — foreign to both the Supervisor and the Servico
22324        // arms) surfaces the supervisor-tree diagnostic first
22325        // through the compound gate. Together with the peer
22326        // `_mesh_arm_fires_before_supervisor_arm` pin above this
22327        // pins the whole three-arm canonical order (mesh →
22328        // supervisor → servico) at the substrate primitive.
22329        use crate::limits::LimitsSpec;
22330        use crate::supervisor::RestartStrategy;
22331        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22332        c.kind = CaixaKind::Biblioteca;
22333        c.estrategia = Some(RestartStrategy::OneForOne);
22334        c.limits = Some(LimitsSpec {
22335            memory: Some(64 * 1024 * 1024),
22336            fuel: None,
22337            wall_clock: None,
22338            cpu: None,
22339        });
22340        let err = c.validate_kind_slot_coherence().unwrap_err();
22341        assert!(
22342            matches!(
22343                err,
22344                crate::LayoutError::SupervisorSlotsOnNonSupervisor { .. }
22345            ),
22346            "expected SupervisorSlotsOnNonSupervisor to fire before \
22347             ServicoSlotsOnNonServico under the canonical mesh → \
22348             supervisor → servico order, got {err:?}",
22349        );
22350    }
22351
22352    #[test]
22353    fn validate_kind_slot_coherence_accepts_owner_kind_on_every_arm() {
22354        // Positive control on the identity-element arm: the owner
22355        // kind of each typed-slot family passes the compound gate
22356        // even when it declares the full slot set that family owns.
22357        // Aplicacao with `:membros` populated passes the mesh arm;
22358        // Supervisor with `:estrategia` populated passes the
22359        // supervisor arm; Servico with `:limits` populated passes
22360        // the servico arm. Pins the fold's identity element on
22361        // every owner kind — a silent regression that dropped the
22362        // paired `!kind().is_<owner>()` short-circuit guard would
22363        // surface here as a false-positive rejection of every
22364        // native-slot declaration. Peer with the
22365        // `validate_<kind>_shape_accepts_non_<kind>_kind` identity-
22366        // element pins on the sibling per-Caixa compound gates.
22367        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
22368        use crate::limits::LimitsSpec;
22369        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
22370
22371        let mut apli = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22372        apli.kind = CaixaKind::Aplicacao;
22373        apli.bibliotecas = vec![];
22374        apli.membros = vec![Membro {
22375            caixa: "cart".into(),
22376            versao: "^0.1".into(),
22377        }];
22378        apli.placement = Some(Placement {
22379            estrategia: PlacementStrategy::SingleNode,
22380            clusters: vec!["rio".into()],
22381            shard_key: None,
22382            affinity: None,
22383        });
22384        apli.validate_kind_slot_coherence().expect(
22385            "an :kind Aplicacao caixa with declared M3 mesh slots must \
22386             pass the compound gate — Aplicacao is the mesh-slot family's \
22387             owner kind and the fold's identity element on that arm",
22388        );
22389
22390        let mut sup = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22391        sup.kind = CaixaKind::Supervisor;
22392        sup.bibliotecas = vec![];
22393        sup.estrategia = Some(RestartStrategy::OneForOne);
22394        sup.children = vec![ChildSpec {
22395            caixa: "worker".into(),
22396            versao: "^0.1".into(),
22397            restart: RestartPolicy::Permanent,
22398        }];
22399        sup.validate_kind_slot_coherence().expect(
22400            "an :kind Supervisor caixa with declared supervisor-tree slots \
22401             must pass the compound gate — Supervisor is the \
22402             supervisor-slot family's owner kind and the fold's identity \
22403             element on that arm",
22404        );
22405
22406        let mut svc = bare_servico_fixture("svc");
22407        svc.limits = Some(LimitsSpec {
22408            memory: Some(64 * 1024 * 1024),
22409            fuel: None,
22410            wall_clock: None,
22411            cpu: None,
22412        });
22413        svc.validate_kind_slot_coherence().expect(
22414            "an :kind Servico caixa with declared M2 slots must pass the \
22415             compound gate — Servico is the M2-slot family's owner kind \
22416             and the fold's identity element on that arm",
22417        );
22418    }
22419
22420    #[test]
22421    fn validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind() {
22422        // Positive control on the second identity-element arm: a
22423        // bare caixa (no declared typed slots) passes the compound
22424        // gate on every kind. Pins the fold's identity element on
22425        // the empty-slot axis — the paired `Vec::is_empty` short-
22426        // circuit guard fires before the wrap dispatch on all three
22427        // arms, so a bare caixa of any kind surfaces no diagnostic.
22428        // A silent regression that dropped the emptiness guard
22429        // would surface here as a false-positive rejection of every
22430        // no-slot caixa across the whole kind axis.
22431        for kind in CaixaKind::ALL {
22432            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22433            c.kind = *kind;
22434            c.bibliotecas = vec![];
22435            c.validate_kind_slot_coherence().unwrap_or_else(|err| {
22436                panic!(
22437                    "a bare :kind {kind:?} caixa (no declared typed slots) \
22438                     must pass the compound gate — the fold's identity \
22439                     element on the empty-slot axis is the paired \
22440                     Vec::is_empty short-circuit guard, got {err:?}",
22441                )
22442            });
22443        }
22444    }
22445
22446    #[test]
22447    fn run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator() {
22448        // Fail-before-pass-after identity-element pin on the owner-kind
22449        // arm of the substrate primitive: on a caixa whose kind IS the
22450        // owner of the family named by `is_owner`, the primitive
22451        // short-circuits before dispatching `accumulator` — pinned here
22452        // by a poison-pill accumulator that panics on call. If a
22453        // regression drops the `is_owner` short-circuit and always
22454        // invokes the accumulator, the poison panic surfaces here
22455        // rather than a spurious pass. Byte-equal to the pre-lift
22456        // `if !self.kind().is_<owner>() { … }` outer guard's
22457        // short-circuit at the pre-fold layout call site.
22458        let c = bare_servico_fixture("demo");
22459        c.run_kind_owned_slot_family_gate(
22460            CaixaKind::is_servico,
22461            |_| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking accumulator on the owner kind"),
22462            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on the owner kind"),
22463        )
22464        .expect(
22465            "the owner kind of a slot family must pass the substrate \
22466             primitive as the fold's identity element on the outer \
22467             is_owner guard, without invoking accumulator or wrap",
22468        );
22469    }
22470
22471    #[test]
22472    fn run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap() {
22473        // Fail-before-pass-after identity-element pin on the empty-
22474        // accumulator arm: on a non-owner kind whose per-family
22475        // accumulator yields no declared slot, the primitive short-
22476        // circuits before dispatching `wrap` — pinned here by a
22477        // poison-pill wrap that panics on call. Byte-equal to the
22478        // pre-lift `if !<slots>.is_empty() { … }` inner emptiness
22479        // guard's short-circuit at the pre-fold layout call site.
22480        let c = bare_servico_fixture("demo");
22481        c.run_kind_owned_slot_family_gate(
22482            CaixaKind::is_aplicacao,
22483            Caixa::declared_mesh_slots,
22484            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on an empty accumulator"),
22485        )
22486        .expect(
22487            "a non-owner kind carrying no declared slot in the family \
22488             must pass the substrate primitive as the fold's identity \
22489             element on the inner emptiness guard, without invoking \
22490             wrap",
22491        );
22492    }
22493
22494    #[test]
22495    fn run_kind_owned_slot_family_gate_non_owner_non_empty_wraps_verbatim() {
22496        // Equivalence pin on the refusal arm: on a non-owner kind
22497        // whose accumulator yields a non-empty slot list, the primitive
22498        // returns the caller-supplied wrap byte-equal to the direct
22499        // ctor dispatch on the same `(caixa, slots)` pair. Pins the
22500        // three-argument route through — `is_owner` fires false, the
22501        // accumulator produces the slot list, and the wrap ctor
22502        // receives verbatim what a direct dispatch would receive.
22503        // Sibling of the peer per-arm equivalence pins on
22504        // [`Caixa::validate_kind_slot_coherence`].
22505        use crate::aplicacao::Membro;
22506        let mut c = bare_servico_fixture("demo");
22507        c.membros = vec![Membro {
22508            caixa: "cart".into(),
22509            versao: "^0.1".into(),
22510        }];
22511        let via_primitive = c
22512            .run_kind_owned_slot_family_gate(
22513                CaixaKind::is_aplicacao,
22514                Caixa::declared_mesh_slots,
22515                crate::LayoutError::mesh_slots_on_non_aplicacao,
22516            )
22517            .unwrap_err();
22518        let via_direct =
22519            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22520        assert_eq!(
22521            via_primitive, via_direct,
22522            "Caixa::run_kind_owned_slot_family_gate must route the \
22523             non-owner-kind + non-empty-accumulator arm through the \
22524             caller-supplied wrap byte-equal to the direct ctor \
22525             dispatch on the same (caixa, slots) pair",
22526        );
22527    }
22528
22529    #[test]
22530    fn validate_kind_slot_coherence_routes_each_arm_through_run_kind_owned_slot_family_gate() {
22531        // Cross-primitive routing pin: every arm of the compound gate
22532        // [`Caixa::validate_kind_slot_coherence`] routes through the
22533        // substrate primitive [`Caixa::run_kind_owned_slot_family_gate`]
22534        // on its `(is_owner, accumulator, wrap)` triple. A silent
22535        // regression that de-folded one arm and re-inlined the four-
22536        // line block would surface here as a mismatch between the
22537        // compound-gate error and the direct-primitive-dispatch error
22538        // on the same fixture. Sibling of the peer
22539        // `probe_declared_entries_routes_miss_arm_through_probe_declared_entry`
22540        // cross-primitive routing pin on the layout-pipeline
22541        // existence-probe axis.
22542        use crate::aplicacao::Membro;
22543        use crate::limits::LimitsSpec;
22544        use crate::supervisor::RestartStrategy;
22545
22546        // Mesh arm — non-Aplicacao carrying a declared M3 slot.
22547        let mut mesh = bare_servico_fixture("demo");
22548        mesh.membros = vec![Membro {
22549            caixa: "cart".into(),
22550            versao: "^0.1".into(),
22551        }];
22552        let via_compound = mesh.validate_kind_slot_coherence().unwrap_err();
22553        let via_primitive = mesh
22554            .run_kind_owned_slot_family_gate(
22555                CaixaKind::is_aplicacao,
22556                Caixa::declared_mesh_slots,
22557                crate::LayoutError::mesh_slots_on_non_aplicacao,
22558            )
22559            .unwrap_err();
22560        assert_eq!(
22561            via_compound, via_primitive,
22562            "validate_kind_slot_coherence's mesh arm must route \
22563             byte-equal through the run_kind_owned_slot_family_gate \
22564             substrate primitive",
22565        );
22566
22567        // Supervisor arm — non-Supervisor carrying a declared
22568        // supervisor-tree slot on a kind foreign to both the Aplicacao
22569        // arm and this one.
22570        let mut sup = bare_servico_fixture("demo");
22571        sup.estrategia = Some(RestartStrategy::OneForOne);
22572        let via_compound = sup.validate_kind_slot_coherence().unwrap_err();
22573        let via_primitive = sup
22574            .run_kind_owned_slot_family_gate(
22575                CaixaKind::is_supervisor,
22576                Caixa::declared_supervisor_slots,
22577                crate::LayoutError::supervisor_slots_on_non_supervisor,
22578            )
22579            .unwrap_err();
22580        assert_eq!(
22581            via_compound, via_primitive,
22582            "validate_kind_slot_coherence's supervisor arm must route \
22583             byte-equal through the run_kind_owned_slot_family_gate \
22584             substrate primitive",
22585        );
22586
22587        // Servico arm — non-Servico carrying a declared M2 slot on a
22588        // kind foreign to every prior arm (Biblioteca — foreign to
22589        // both the Aplicacao mesh arm and the Supervisor supervisor
22590        // arm and the Servico M2 arm).
22591        let mut svc = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22592        svc.kind = CaixaKind::Biblioteca;
22593        svc.limits = Some(LimitsSpec {
22594            memory: Some(64 * 1024 * 1024),
22595            fuel: None,
22596            wall_clock: None,
22597            cpu: None,
22598        });
22599        let via_compound = svc.validate_kind_slot_coherence().unwrap_err();
22600        let via_primitive = svc
22601            .run_kind_owned_slot_family_gate(
22602                CaixaKind::is_servico,
22603                Caixa::declared_servico_slots,
22604                crate::LayoutError::servico_slots_on_non_servico,
22605            )
22606            .unwrap_err();
22607        assert_eq!(
22608            via_compound, via_primitive,
22609            "validate_kind_slot_coherence's servico arm must route \
22610             byte-equal through the run_kind_owned_slot_family_gate \
22611             substrate primitive",
22612        );
22613    }
22614
22615    #[test]
22616    fn validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate() {
22617        // Fail-before-pass-after per-arm equivalence pin on the
22618        // Supervisor no-code arm of the reciprocal code-surface
22619        // fold: a `:kind Supervisor` caixa carrying a declared
22620        // `:bibliotecas` entry (the smallest possible code-surface
22621        // declaration on a no-code kind) surfaces the same
22622        // [`crate::LayoutError::SupervisorOwnsCode`] variant
22623        // through both the compound gate
22624        // [`Caixa::validate_no_code_kind_coherence`] and the
22625        // standalone constructor
22626        // [`crate::LayoutError::supervisor_owns_code`]. Pins the
22627        // fold — a silent regression that de-folded the Supervisor
22628        // arm would surface here as a mismatch between the two
22629        // dispatches. Sibling in shape to the peer
22630        // `validate_kind_slot_coherence_folds_supervisor_arm_matches_gate`
22631        // per-arm equivalence pin on the cross-family
22632        // typed-slot-coherence fold.
22633        let mut c = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22634        c.kind = CaixaKind::Supervisor;
22635        c.bibliotecas = vec!["lib/sup.lisp".into()];
22636        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22637        let via_standalone = crate::LayoutError::supervisor_owns_code(&c);
22638        assert_eq!(
22639            via_method, via_standalone,
22640            "Caixa::validate_no_code_kind_coherence must surface the \
22641             Supervisor arm's diagnostic byte-equal to the standalone \
22642             LayoutError::supervisor_owns_code ctor",
22643        );
22644    }
22645
22646    #[test]
22647    fn validate_no_code_kind_coherence_folds_aplicacao_arm_matches_gate() {
22648        // Per-arm equivalence pin on the Aplicacao no-code arm —
22649        // the sibling of the Supervisor arm on the code-surface
22650        // fold. A `:kind Aplicacao` caixa carrying a declared
22651        // `:exe` entry surfaces the same
22652        // [`crate::LayoutError::AplicacaoOwnsCode`] variant through
22653        // both dispatches. Uses the `:exe` code-surface axis (a
22654        // second axis distinct from the Supervisor arm's
22655        // `:bibliotecas` fixture) so the three per-arm pins
22656        // collectively exercise every arm of the `has_code`
22657        // disjunction (`:bibliotecas || :exe || :servicos`).
22658        let mut c = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22659        c.kind = CaixaKind::Aplicacao;
22660        c.bibliotecas = vec![];
22661        c.exe = vec!["exe/app".into()];
22662        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22663        let via_standalone = crate::LayoutError::aplicacao_owns_code(&c);
22664        assert_eq!(
22665            via_method, via_standalone,
22666            "Caixa::validate_no_code_kind_coherence must surface the \
22667             Aplicacao arm's diagnostic byte-equal to the standalone \
22668             LayoutError::aplicacao_owns_code ctor",
22669        );
22670    }
22671
22672    #[test]
22673    fn validate_no_code_kind_coherence_folds_acao_arm_matches_gate() {
22674        // Per-arm equivalence pin on the Acao no-code arm — the
22675        // third and last arm on the code-surface fold. A `:kind
22676        // Acao` caixa carrying a declared `:servicos` entry
22677        // surfaces the same [`crate::LayoutError::AcaoOwnsCode`]
22678        // variant through both dispatches. Uses the `:servicos`
22679        // code-surface axis (the third distinct axis of the
22680        // `has_code` disjunction) so the three per-arm pins
22681        // collectively cover every arm of the code-surface
22682        // disjunction plus every no-code kind of the arm
22683        // dispatch.
22684        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
22685        c.kind = CaixaKind::Acao;
22686        c.bibliotecas = vec![];
22687        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22688        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22689        let via_standalone = crate::LayoutError::acao_owns_code(&c);
22690        assert_eq!(
22691            via_method, via_standalone,
22692            "Caixa::validate_no_code_kind_coherence must surface the \
22693             Acao arm's diagnostic byte-equal to the standalone \
22694             LayoutError::acao_owns_code ctor",
22695        );
22696    }
22697
22698    #[test]
22699    fn validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis() {
22700        // Positive control on the code-owning-kind identity
22701        // element: each of the three code-owning kinds
22702        // (`Biblioteca` owning `:bibliotecas`, `Binario` owning
22703        // `:exe`, `Servico` owning `:servicos`) passes the
22704        // compound gate cleanly when it declares its native code
22705        // surface. Pins the fold's second identity element — the
22706        // paired per-arm `is_<no-code-kind>()` short-circuit
22707        // fires on every code-owning kind, so a caixa with any
22708        // native code declaration on its owner kind surfaces no
22709        // diagnostic. A silent regression that dropped the paired
22710        // `is_<no-code-kind>()` short-circuit guard on any arm
22711        // would surface here as a false-positive rejection of the
22712        // corresponding owner kind. Peer with the
22713        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22714        // identity-element pin on the sibling cross-family fold.
22715        let mut bib = Caixa::from_lisp(&Caixa::template("bib")).unwrap();
22716        bib.kind = CaixaKind::Biblioteca;
22717        bib.bibliotecas = vec!["lib/bib.lisp".into()];
22718        bib.validate_no_code_kind_coherence().expect(
22719            "a :kind Biblioteca caixa with declared :bibliotecas must pass \
22720             the compound gate — Biblioteca owns the :bibliotecas code surface",
22721        );
22722
22723        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22724        bin.kind = CaixaKind::Binario;
22725        bin.bibliotecas = vec![];
22726        bin.exe = vec!["exe/bin".into()];
22727        bin.validate_no_code_kind_coherence().expect(
22728            "a :kind Binario caixa with declared :exe must pass the compound \
22729             gate — Binario owns the :exe code surface",
22730        );
22731
22732        let svc = bare_servico_fixture("svc");
22733        svc.validate_no_code_kind_coherence().expect(
22734            "a :kind Servico caixa with declared :servicos must pass the \
22735             compound gate — Servico owns the :servicos code surface",
22736        );
22737    }
22738
22739    #[test]
22740    fn validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22741        // Positive control on the has-no-code identity element:
22742        // a bare caixa (no declared code) passes the compound
22743        // gate on every kind — including the three no-code kinds
22744        // that would otherwise fire an OwnsCode diagnostic. Pins
22745        // the fold's first identity element — the paired
22746        // `!has_code` short-circuit fires before every per-arm
22747        // wrap dispatch, so a bare caixa of any kind surfaces no
22748        // diagnostic. A silent regression that dropped the
22749        // has_code guard would surface here as a false-positive
22750        // rejection of every no-code kind that declares no code.
22751        // Peer with the
22752        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22753        // identity-element pin on the sibling cross-family fold.
22754        for kind in CaixaKind::ALL {
22755            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22756            c.kind = *kind;
22757            c.bibliotecas = vec![];
22758            c.exe = vec![];
22759            c.servicos = vec![];
22760            c.validate_no_code_kind_coherence().unwrap_or_else(|err| {
22761                panic!(
22762                    "a bare :kind {kind:?} caixa (no declared code) must pass \
22763                     the compound gate — the fold's first identity element is \
22764                     the paired !has_code short-circuit, got {err:?}",
22765                )
22766            });
22767        }
22768    }
22769
22770    #[test]
22771    fn validate_ci_kind_coherence_folds_arm_matches_gate() {
22772        // Fail-before-pass-after per-arm equivalence pin on the
22773        // `:ci`-on-non-`Acao` arm: a `:kind Biblioteca` caixa
22774        // (the smallest non-`Acao` kind) carrying a declared
22775        // `:ci` slot surfaces the same
22776        // [`crate::LayoutError::CiOnNonAcao`] variant through the
22777        // compound gate [`Caixa::validate_ci_kind_coherence`] and
22778        // an inlined struct-literal wrap carrying `caixa.nome()`
22779        // + `caixa.kind()` verbatim. Pins the fold — a silent
22780        // regression that de-folded the arm would surface here as
22781        // a mismatch between the two dispatches. Sibling in shape
22782        // to the peer
22783        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22784        // per-arm equivalence pin on the reciprocal
22785        // code-surface fold.
22786        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22787        c.kind = CaixaKind::Biblioteca;
22788        c.ci = Some(canteiro_types::CiRun {
22789            workspace: "pleme-io".into(),
22790            repo: "caixa".into(),
22791            nodes: vec![],
22792        });
22793        let via_method = c.validate_ci_kind_coherence().unwrap_err();
22794        let via_standalone = crate::LayoutError::CiOnNonAcao {
22795            caixa: c.nome().to_string(),
22796            kind: c.kind(),
22797        };
22798        assert_eq!(
22799            via_method, via_standalone,
22800            "Caixa::validate_ci_kind_coherence must surface the \
22801             :ci-on-non-Acao arm's diagnostic byte-equal to a \
22802             LayoutError::CiOnNonAcao struct literal carrying the \
22803             caixa's nome + kind",
22804        );
22805    }
22806
22807    #[test]
22808    fn validate_ci_kind_coherence_fold_names_offending_kind_on_every_non_acao_kind() {
22809        // Exhaustive per-kind sweep on the non-`Acao` arm: for each
22810        // of the five non-`Acao` kinds
22811        // (`Biblioteca` / `Binario` / `Servico` / `Supervisor` /
22812        // `Aplicacao`), a caixa carrying a declared `:ci` slot
22813        // surfaces the [`crate::LayoutError::CiOnNonAcao`]
22814        // variant naming the offending kind verbatim. A silent
22815        // regression that mistyped one arm's kind-projection
22816        // (e.g. always threading `CaixaKind::Biblioteca` regardless
22817        // of the caixa's actual kind) would surface here as a
22818        // mismatch on every kind past the first. Peer of the
22819        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22820        // exhaustive-sweep pin on the sibling code-surface fold.
22821        for kind in CaixaKind::ALL {
22822            if kind.is_acao() {
22823                continue;
22824            }
22825            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22826            c.kind = *kind;
22827            c.ci = Some(canteiro_types::CiRun {
22828                workspace: "pleme-io".into(),
22829                repo: "caixa".into(),
22830                nodes: vec![],
22831            });
22832            let err = c.validate_ci_kind_coherence().unwrap_err();
22833            match err {
22834                crate::LayoutError::CiOnNonAcao {
22835                    caixa: got_caixa,
22836                    kind: got_kind,
22837                } => {
22838                    assert_eq!(
22839                        got_caixa,
22840                        c.nome(),
22841                        "CiOnNonAcao must name the offending caixa's nome verbatim on kind {kind:?}",
22842                    );
22843                    assert_eq!(
22844                        got_kind, *kind,
22845                        "CiOnNonAcao must name the offending kind verbatim on kind {kind:?}",
22846                    );
22847                }
22848                other => panic!(
22849                    "expected CiOnNonAcao on :kind {kind:?} with declared :ci, got {other:?}",
22850                ),
22851            }
22852        }
22853    }
22854
22855    #[test]
22856    fn validate_ci_kind_coherence_accepts_acao_on_every_ci_shape() {
22857        // Positive control on the owner-kind identity element: an
22858        // `:kind Acao` caixa passes the coherence gate cleanly on
22859        // every `:ci` shape — the arm's paired
22860        // `!kind().is_acao()` short-circuit fires before the
22861        // dispatch, so the fold surfaces no diagnostic even on
22862        // fixtures whose `:ci` would fail the peer
22863        // [`Self::validate_acao_shape`] decompose gate (a
22864        // duplicate-node fixture, an unknown-dep fixture, a
22865        // cyclic fixture). Pins the fold's first identity element
22866        // — a silent regression that dropped the paired
22867        // `!kind().is_acao()` short-circuit guard would surface
22868        // here as a false-positive rejection of every `Acao`
22869        // caixa. Peer with the
22870        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
22871        // identity-element pin on the sibling code-surface fold.
22872        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22873        c.kind = CaixaKind::Acao;
22874        c.bibliotecas = vec![];
22875        c.ci = Some(canteiro_types::CiRun {
22876            workspace: "pleme-io".into(),
22877            repo: "caixa".into(),
22878            nodes: vec![],
22879        });
22880        c.validate_ci_kind_coherence().expect(
22881            "a :kind Acao caixa with declared :ci must pass the compound \
22882             coherence gate — Acao is the :ci-owning kind (a malformed \
22883             :ci on Acao surfaces via validate_acao_shape's decompose gate, \
22884             not via this kind-coherence gate)",
22885        );
22886    }
22887
22888    #[test]
22889    fn validate_ci_kind_coherence_accepts_absent_ci_on_every_kind() {
22890        // Positive control on the absent-`:ci` identity element:
22891        // a caixa with `ci = None` passes the coherence gate on
22892        // every kind — including `Acao`, whose absent `:ci`
22893        // fails a separate presence gate ([`crate::LayoutError::MissingCi`])
22894        // downstream at the layout altitude, not this coherence
22895        // gate. Pins the fold's second identity element — the
22896        // paired `ci().is_some()` short-circuit fires before every
22897        // per-arm dispatch, so a caixa with no declared `:ci`
22898        // surfaces no coherence diagnostic. A silent regression
22899        // that dropped the paired `ci().is_some()` short-circuit
22900        // would surface here as a false-positive rejection on
22901        // every non-`Acao` kind. Peer with the
22902        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22903        // identity-element pin on the sibling code-surface fold.
22904        for kind in CaixaKind::ALL {
22905            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22906            c.kind = *kind;
22907            c.ci = None;
22908            c.validate_ci_kind_coherence().unwrap_or_else(|err| {
22909                panic!(
22910                    "a :kind {kind:?} caixa with no declared :ci must pass \
22911                     the compound coherence gate — the fold's second identity \
22912                     element is the paired ci().is_some() short-circuit, got \
22913                     {err:?}",
22914                )
22915            });
22916        }
22917    }
22918
22919    #[test]
22920    fn validate_foreign_code_kind_coherence_folds_arm_matches_gate() {
22921        // Fail-before-pass-after equivalence pin on the compound
22922        // foreign-code-slot coherence fold: a `:kind Servico` caixa
22923        // carrying a declared `:exe` entry (the smallest possible
22924        // foreign-code-slot declaration on a code-running kind that
22925        // is not its owner — Servico owns `:servicos`, not `:exe`)
22926        // surfaces the same [`crate::LayoutError::ForeignCodeSlot`]
22927        // variant through both the compound gate
22928        // [`Caixa::validate_foreign_code_kind_coherence`] and the
22929        // standalone constructor
22930        // [`crate::LayoutError::foreign_code_slot`] dispatched on the
22931        // same `declared_foreign_code_slots` list. Pins the fold — a
22932        // silent regression that de-folded the arm would surface here
22933        // as a mismatch between the two dispatches. Sibling in shape
22934        // to the peer
22935        // `validate_kind_slot_coherence_folds_mesh_arm_matches_gate`
22936        // / `validate_ci_kind_coherence_folds_arm_matches_gate` /
22937        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22938        // per-arm equivalence pins on the sibling kind-coherence folds.
22939        let mut c = bare_servico_fixture("demo");
22940        c.exe = vec!["exe/foreign".into()];
22941        let via_method = c.validate_foreign_code_kind_coherence().unwrap_err();
22942        let via_standalone =
22943            crate::LayoutError::foreign_code_slot(&c, c.declared_foreign_code_slots());
22944        assert_eq!(
22945            via_method, via_standalone,
22946            "Caixa::validate_foreign_code_kind_coherence must surface the \
22947             foreign-code-slot diagnostic byte-equal to the standalone \
22948             LayoutError::foreign_code_slot ctor on the same \
22949             declared_foreign_code_slots list",
22950        );
22951    }
22952
22953    #[test]
22954    fn validate_foreign_code_kind_coherence_exe_arm_precedes_servicos_arm() {
22955        // Cross-arm ordering pin on the fold's accumulator: a fixture
22956        // carrying BOTH a declared `:exe` AND a declared `:servicos`
22957        // on a kind foreign to both (a `:kind Biblioteca` here —
22958        // foreign to both the Binario arm and the Servico arm)
22959        // surfaces `:exe` first in the `ForeignCodeSlot`'s slots
22960        // list. Pins the canonical `:exe` → `:servicos` diagnostic
22961        // order [`Caixa::declared_foreign_code_slots`] establishes,
22962        // as a property of the substrate primitive rather than an
22963        // implicit accumulator convention. A silent reordering
22964        // regression at the accumulator would surface here as a
22965        // wrong-first-slot list before landing at a downstream
22966        // consumer's diagnostic-ordering expectation.
22967        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22968        c.kind = CaixaKind::Biblioteca;
22969        c.exe = vec!["exe/demo".into()];
22970        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22971        let err = c.validate_foreign_code_kind_coherence().unwrap_err();
22972        let crate::LayoutError::ForeignCodeSlot { slots, .. } = &err else {
22973            panic!("expected ForeignCodeSlot variant, got {err:?}");
22974        };
22975        assert!(
22976            slots.starts_with(":exe"),
22977            "expected the :exe arm to precede the :servicos arm in the \
22978             ForeignCodeSlot slots list under the canonical :exe → :servicos \
22979             order, got slots = {slots:?}",
22980        );
22981        assert!(
22982            slots.contains(":servicos"),
22983            "expected the :servicos arm to also fire in the ForeignCodeSlot \
22984             slots list on a fixture carrying both foreign code surfaces, \
22985             got slots = {slots:?}",
22986        );
22987    }
22988
22989    #[test]
22990    fn validate_foreign_code_kind_coherence_accepts_native_slot_on_owner_kind() {
22991        // Positive control on the native-slot identity element: each
22992        // code-surface slot's owner kind passes the fold trivially
22993        // when it declares only its native code surface. `:kind
22994        // Binario` with a declared `:exe` and no `:servicos` passes
22995        // (the `!requires_exe()` guard short-circuits the arm inside
22996        // [`Caixa::declared_foreign_code_slots`], so the accumulator
22997        // returns empty); `:kind Servico` with a declared `:servicos`
22998        // and no `:exe` passes for the mirror reason. Pins the fold's
22999        // native-slot identity element on both arms — a silent
23000        // regression that dropped either per-arm `!requires_<slot>()`
23001        // predicate would surface here as a false-positive rejection
23002        // of every native-slot declaration on its owner kind. Peer
23003        // with the
23004        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
23005        // identity-element pin on the sibling cross-family fold.
23006        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
23007        bin.kind = CaixaKind::Binario;
23008        bin.bibliotecas = vec![];
23009        bin.exe = vec!["exe/bin".into()];
23010        bin.servicos = vec![];
23011        bin.validate_foreign_code_kind_coherence().expect(
23012            "a :kind Binario caixa with a declared native :exe and no \
23013             :servicos must pass the compound coherence gate — Binario is \
23014             the :exe slot's owner kind and the fold's native-slot identity \
23015             element on that arm",
23016        );
23017
23018        let mut svc = bare_servico_fixture("svc");
23019        svc.exe = vec![];
23020        svc.validate_foreign_code_kind_coherence().expect(
23021            "a :kind Servico caixa with a declared native :servicos and no \
23022             :exe must pass the compound coherence gate — Servico is the \
23023             :servicos slot's owner kind and the fold's native-slot identity \
23024             element on that arm",
23025        );
23026    }
23027
23028    #[test]
23029    fn validate_foreign_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
23030        // Positive control on the empty-slot identity element: a
23031        // bare caixa (no declared `:exe` and no declared `:servicos`)
23032        // passes the compound gate on every kind. Pins the fold's
23033        // identity element on the empty-accumulator axis — the outer
23034        // `is_empty` short-circuit fires before the wrap dispatch on
23035        // every kind, so a bare caixa of any kind surfaces no
23036        // foreign-code-slot diagnostic. A silent regression that
23037        // dropped the emptiness guard would surface here as a
23038        // false-positive rejection of every no-code-slot caixa
23039        // across the whole kind axis. Peer with the
23040        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
23041        // identity-element pin on the sibling cross-family fold.
23042        for kind in CaixaKind::ALL {
23043            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
23044            c.kind = *kind;
23045            c.bibliotecas = vec![];
23046            c.exe = vec![];
23047            c.servicos = vec![];
23048            c.validate_foreign_code_kind_coherence()
23049                .unwrap_or_else(|err| {
23050                    panic!(
23051                        "a bare :kind {kind:?} caixa (no declared :exe / \
23052                         :servicos) must pass the compound coherence gate — \
23053                         the fold's identity element on the empty-accumulator \
23054                         axis is the outer Vec::is_empty short-circuit, got \
23055                         {err:?}",
23056                    )
23057                });
23058        }
23059    }
23060
23061    #[test]
23062    fn validate_required_kind_slot_folds_binario_arm_matches_gate() {
23063        // Fail-before-pass-after per-arm equivalence pin on the
23064        // `Binario` required-`:exe` arm of the required-slot fold:
23065        // a `:kind Binario` caixa carrying no declared `:exe` entry
23066        // surfaces the same
23067        // [`crate::LayoutError::BinarioWithoutExe`] variant through
23068        // both the compound gate
23069        // [`Caixa::validate_required_kind_slot`] and the standalone
23070        // constructor [`crate::LayoutError::binario_without_exe`].
23071        // Pins the fold — a silent regression that de-folded the
23072        // `Binario` arm would surface here as a mismatch between
23073        // the two dispatches. Sibling in shape to the peer
23074        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
23075        // per-arm equivalence pin on the reciprocal code-surface
23076        // fold.
23077        let mut c = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
23078        c.kind = CaixaKind::Binario;
23079        c.bibliotecas = vec![];
23080        c.exe = vec![];
23081        let via_method = c.validate_required_kind_slot().unwrap_err();
23082        let via_standalone = crate::LayoutError::binario_without_exe(&c);
23083        assert_eq!(
23084            via_method, via_standalone,
23085            "Caixa::validate_required_kind_slot must surface the \
23086             Binario arm's diagnostic byte-equal to the standalone \
23087             LayoutError::binario_without_exe ctor",
23088        );
23089    }
23090
23091    #[test]
23092    fn validate_required_kind_slot_folds_servico_arm_matches_gate() {
23093        // Per-arm equivalence pin on the `Servico` required-
23094        // `:servicos` arm — the sibling of the Binario arm on the
23095        // required-slot fold. A `:kind Servico` caixa carrying no
23096        // declared `:servicos` entry surfaces the same
23097        // [`crate::LayoutError::ServicoWithoutServicos`] variant
23098        // through both dispatches.
23099        let mut c = Caixa::from_lisp(&Caixa::template("svc")).unwrap();
23100        c.kind = CaixaKind::Servico;
23101        c.bibliotecas = vec![];
23102        c.servicos = vec![];
23103        let via_method = c.validate_required_kind_slot().unwrap_err();
23104        let via_standalone = crate::LayoutError::servico_without_servicos(&c);
23105        assert_eq!(
23106            via_method, via_standalone,
23107            "Caixa::validate_required_kind_slot must surface the \
23108             Servico arm's diagnostic byte-equal to the standalone \
23109             LayoutError::servico_without_servicos ctor",
23110        );
23111    }
23112
23113    #[test]
23114    fn validate_required_kind_slot_folds_acao_arm_matches_gate() {
23115        // Per-arm equivalence pin on the `Acao` required-`:ci` arm
23116        // — the third and last arm on the required-slot fold. A
23117        // `:kind Acao` caixa carrying no declared `:ci` slot
23118        // surfaces the same [`crate::LayoutError::MissingCi`]
23119        // variant through both dispatches. The three per-arm pins
23120        // collectively cover every required-slot axis and every
23121        // owner kind of the arm dispatch.
23122        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
23123        c.kind = CaixaKind::Acao;
23124        c.bibliotecas = vec![];
23125        c.ci = None;
23126        let via_method = c.validate_required_kind_slot().unwrap_err();
23127        let via_standalone = crate::LayoutError::missing_ci(&c);
23128        assert_eq!(
23129            via_method, via_standalone,
23130            "Caixa::validate_required_kind_slot must surface the \
23131             Acao arm's diagnostic byte-equal to the standalone \
23132             LayoutError::missing_ci ctor",
23133        );
23134    }
23135
23136    #[test]
23137    fn validate_required_kind_slot_accepts_owner_kind_with_required_slot_present() {
23138        // Positive control on the owner-kind-with-slot-present
23139        // identity element: each of the three owner kinds
23140        // (`Binario` with a non-empty `:exe`, `Servico` with a
23141        // non-empty `:servicos`, `Acao` with `ci = Some(_)`)
23142        // passes the compound gate cleanly when it declares its
23143        // required slot. Pins the fold's second identity element
23144        // — the paired `is_empty` / `is_none` short-circuit fires
23145        // on every owner kind whose required slot is present, so
23146        // a caixa with its native required slot surfaces no
23147        // diagnostic. A silent regression that dropped the paired
23148        // `is_empty` / `is_none` short-circuit guard on any arm
23149        // would surface here as a false-positive rejection of the
23150        // corresponding owner kind. Peer with the
23151        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
23152        // identity-element pin on the sibling code-surface fold.
23153        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
23154        bin.kind = CaixaKind::Binario;
23155        bin.bibliotecas = vec![];
23156        bin.exe = vec!["exe/bin".into()];
23157        bin.validate_required_kind_slot().expect(
23158            "a :kind Binario caixa with declared :exe must pass the \
23159             required-slot gate — Binario's required slot is present",
23160        );
23161
23162        let svc = bare_servico_fixture("svc");
23163        svc.validate_required_kind_slot().expect(
23164            "a :kind Servico caixa with declared :servicos must pass \
23165             the required-slot gate — Servico's required slot is present",
23166        );
23167
23168        let acao = acao_fixture("acao");
23169        acao.validate_required_kind_slot().expect(
23170            "a :kind Acao caixa with declared :ci must pass the \
23171             required-slot gate — Acao's required slot is present",
23172        );
23173    }
23174
23175    #[test]
23176    fn validate_required_kind_slot_accepts_non_owner_kinds() {
23177        // Positive control on the non-owner-kind identity element:
23178        // every kind that is not one of the three owner kinds
23179        // (`Binario` / `Servico` / `Acao`) passes the compound gate
23180        // trivially — each per-arm predicate is
23181        // `self.kind().requires_<slot>()`, which returns `true`
23182        // only for the owner kind of that arm, so a non-owner kind
23183        // short-circuits every per-arm dispatch. Bibliotheca,
23184        // Supervisor, and Aplicacao are the three non-owner kinds
23185        // this pin exercises — none of them owns a required slot in
23186        // this fold (`Biblioteca`'s `:bibliotecas` default-file
23187        // fallback stays on the layout-side `MissingLib` fs-oracle
23188        // gate outside this fold; `Supervisor`'s `:children` and
23189        // `Aplicacao`'s `:membros` are carried by
23190        // [`CaixaKind::requires_children`] /
23191        // [`CaixaKind::requires_membros`] without a paired
23192        // layout-side wire-up). A silent regression that swapped a
23193        // per-arm predicate for a non-`requires_*` guard would
23194        // surface here as a false-positive rejection of the
23195        // corresponding non-owner kind. Peer with the
23196        // `validate_ci_kind_coherence_accepts_absent_ci_on_every_kind`
23197        // identity-element pin on the sibling `:ci` fold.
23198        for kind in CaixaKind::ALL {
23199            if kind.requires_exe() || kind.requires_servicos() || kind.requires_ci() {
23200                continue;
23201            }
23202            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
23203            c.kind = *kind;
23204            c.bibliotecas = vec![];
23205            c.exe = vec![];
23206            c.servicos = vec![];
23207            c.ci = None;
23208            c.validate_required_kind_slot().unwrap_or_else(|err| {
23209                panic!(
23210                    "a :kind {kind:?} caixa (a non-owner kind on every \
23211                     required-slot arm) must pass the compound gate — the \
23212                     fold's identity element is the paired \
23213                     `self.kind().requires_<slot>()` short-circuit, got \
23214                     {err:?}",
23215                )
23216            });
23217        }
23218    }
23219
23220    // ── `manifest_code_path_slot_path_ctors!` — the paired `{ slot:
23221    //    &'static str, path: PathBuf }` two-slot envelope on
23222    //    `ManifestError`, strict sibling of the peer
23223    //    [`crate::behavior::behavior_slot_path_ctors!`] (67c31ec) on the
23224    //    sibling `BehaviorError` envelope's identical
23225    //    `{ slot: &'static str, path: PathBuf }` two-slot shape.
23226    //    Five-variant lift closing the five open-coded ctor sites
23227    //    remaining on the `:bibliotecas` / `:exe` / `:servicos`
23228    //    code-path-list value-shape trajectory this envelope carries.
23229
23230    #[test]
23231    fn code_path_absolute_ctor_matches_struct_literal_wrap() {
23232        let path = Path::new("/abs/lib/x.lisp");
23233        assert_eq!(
23234            ManifestError::code_path_absolute(":bibliotecas", path),
23235            ManifestError::CodePathAbsolute {
23236                slot: ":bibliotecas",
23237                path: path.to_path_buf(),
23238            },
23239            "generated code_path_absolute ctor must produce byte-equal \
23240             `ManifestError::CodePathAbsolute` to the pre-lift \
23241             struct-literal wrap on the same `(&'static str, &Path)` \
23242             fixture",
23243        );
23244    }
23245
23246    #[test]
23247    fn code_path_parent_escape_ctor_matches_struct_literal_wrap() {
23248        let path = Path::new("lib/../../etc/x.lisp");
23249        assert_eq!(
23250            ManifestError::code_path_parent_escape(":bibliotecas", path),
23251            ManifestError::CodePathParentEscape {
23252                slot: ":bibliotecas",
23253                path: path.to_path_buf(),
23254            },
23255            "generated code_path_parent_escape ctor must produce \
23256             byte-equal `ManifestError::CodePathParentEscape` to the \
23257             pre-lift struct-literal wrap on the same `(&'static str, \
23258             &Path)` fixture",
23259        );
23260    }
23261
23262    #[test]
23263    fn code_path_non_lisp_extension_ctor_matches_struct_literal_wrap() {
23264        let path = Path::new("lib/x.txt");
23265        assert_eq!(
23266            ManifestError::code_path_non_lisp_extension(":bibliotecas", path),
23267            ManifestError::CodePathNonLispExtension {
23268                slot: ":bibliotecas",
23269                path: path.to_path_buf(),
23270            },
23271            "generated code_path_non_lisp_extension ctor must produce \
23272             byte-equal `ManifestError::CodePathNonLispExtension` to \
23273             the pre-lift struct-literal wrap on the same \
23274             `(&'static str, &Path)` fixture",
23275        );
23276    }
23277
23278    #[test]
23279    fn code_path_non_computeunit_yaml_extension_ctor_matches_struct_literal_wrap() {
23280        let path = Path::new("servicos/x.yaml");
23281        assert_eq!(
23282            ManifestError::code_path_non_computeunit_yaml_extension(":servicos", path),
23283            ManifestError::CodePathNonComputeUnitYamlExtension {
23284                slot: ":servicos",
23285                path: path.to_path_buf(),
23286            },
23287            "generated code_path_non_computeunit_yaml_extension ctor \
23288             must produce byte-equal \
23289             `ManifestError::CodePathNonComputeUnitYamlExtension` to \
23290             the pre-lift struct-literal wrap on the same \
23291             `(&'static str, &Path)` fixture",
23292        );
23293    }
23294
23295    #[test]
23296    fn code_path_duplicate_ctor_matches_struct_literal_wrap() {
23297        let path = Path::new("lib/x.lisp");
23298        assert_eq!(
23299            ManifestError::code_path_duplicate(":bibliotecas", path),
23300            ManifestError::CodePathDuplicate {
23301                slot: ":bibliotecas",
23302                path: path.to_path_buf(),
23303            },
23304            "generated code_path_duplicate ctor must produce byte-equal \
23305             `ManifestError::CodePathDuplicate` to the pre-lift \
23306             struct-literal wrap on the same `(&'static str, &Path)` \
23307             fixture",
23308        );
23309    }
23310
23311    #[test]
23312    fn manifest_code_path_slot_path_ctors_route_slot_and_path_through_uniformly() {
23313        // Cross-axis routing pin: sweep the two constructor input axes
23314        // (`slot: &'static str`, `path: &Path`) through non-default
23315        // fixtures against every generated arm in the
23316        // [`manifest_code_path_slot_path_ctors!`] macro, so any
23317        // wrapper-side lowercase / trim / truncate / canonicalization at
23318        // codegen time — or a silent field re-name away from the
23319        // canonical `slot` / `path` axes on any one variant, or a `slot`
23320        // axis silently rerouted through `.to_string()` instead of
23321        // passed as `&'static str` verbatim, or a `path` axis silently
23322        // rerouted through `.canonicalize()` / `PathBuf::from(<lossy
23323        // string>)` instead of `.to_path_buf()` — surfaces here rather
23324        // than at a downstream diagnostic-shape mismatch. Peer of the
23325        // sibling
23326        // [`crate::behavior::tests::behavior_slot_path_ctors_route_slot_and_path_through_uniformly`]
23327        // pin (67c31ec) on the sibling `BehaviorError` envelope's
23328        // identical two-slot family.
23329        //
23330        // The `path` fixture carries three distinguishing traits at
23331        // once: a non-`root/`-relative leading segment (`weird/`), a
23332        // `..` component (a canonicalization trap that would collapse
23333        // to `weird/x.lisp` under `.canonicalize()`), and a mixed-case
23334        // extension (a lowercase-normalization trap that would collapse
23335        // `.LISP` to `.lisp` under any `to_ascii_lowercase()` codegen)
23336        // so a routing regression on any one of the three trap axes
23337        // surfaces at assert time. Similarly the `slot` fixture
23338        // sweeps the three canonical code-path author-key literals
23339        // (`:bibliotecas` / `:exe` / `:servicos`) so a silent lookup
23340        // against a per-variant const roster would surface here.
23341        let path = Path::new("weird/../nested/x.LISP");
23342        let cases: [(ManifestError, ManifestError); 5] = [
23343            (
23344                ManifestError::code_path_absolute(":bibliotecas", path),
23345                ManifestError::CodePathAbsolute {
23346                    slot: ":bibliotecas",
23347                    path: path.to_path_buf(),
23348                },
23349            ),
23350            (
23351                ManifestError::code_path_parent_escape(":exe", path),
23352                ManifestError::CodePathParentEscape {
23353                    slot: ":exe",
23354                    path: path.to_path_buf(),
23355                },
23356            ),
23357            (
23358                ManifestError::code_path_non_lisp_extension(":servicos", path),
23359                ManifestError::CodePathNonLispExtension {
23360                    slot: ":servicos",
23361                    path: path.to_path_buf(),
23362                },
23363            ),
23364            (
23365                ManifestError::code_path_non_computeunit_yaml_extension(":bibliotecas", path),
23366                ManifestError::CodePathNonComputeUnitYamlExtension {
23367                    slot: ":bibliotecas",
23368                    path: path.to_path_buf(),
23369                },
23370            ),
23371            (
23372                ManifestError::code_path_duplicate(":exe", path),
23373                ManifestError::CodePathDuplicate {
23374                    slot: ":exe",
23375                    path: path.to_path_buf(),
23376                },
23377            ),
23378        ];
23379        for (via_ctor, via_struct_literal) in cases {
23380            assert_eq!(
23381                via_ctor, via_struct_literal,
23382                "manifest_code_path_slot_path_ctors!-generated ctor \
23383                 must pass `slot` verbatim onto the canonical \
23384                 `&'static str` `slot` field and route `path` through \
23385                 `.to_path_buf()` onto the canonical `PathBuf` `path` \
23386                 field — a field-rename, silent-conversion, or \
23387                 axis-swap regression surfaces here rather than at a \
23388                 downstream diagnostic-shape mismatch",
23389            );
23390        }
23391    }
23392
23393    // ── `manifest_field_reason_ctors!` — the paired `{ <field>: String,
23394    //    reason: String }` two-slot envelope on `ManifestError`, direct
23395    //    sibling of the peer
23396    //    [`crate::aplicacao::aplicacao_field_reason_ctors!`] (981060b)
23397    //    on the M3 mesh `AplicacaoError` envelope's identical two-slot
23398    //    shape and of the peer [`crate::dep::dep_nome_axis_reason_ctors!`]
23399    //    (5621f8a) on the sibling `:deps` envelope's mirror-symmetric
23400    //    three-slot shape (the `nome` axis added at the per-dep-owned
23401    //    altitude). Nine-variant lift closing the nine open-coded ctor
23402    //    sites at the per-axis [`Caixa::validate_*`] cascade.
23403
23404    #[test]
23405    fn nome_invalid_ctor_matches_struct_literal_wrap() {
23406        let nome = "cart-svc";
23407        let reason = "sample reason text";
23408        assert_eq!(
23409            ManifestError::nome_invalid(nome, reason),
23410            ManifestError::NomeInvalid {
23411                nome: nome.to_string(),
23412                reason: reason.to_string(),
23413            },
23414            "generated nome_invalid ctor must produce byte-equal \
23415             `ManifestError::NomeInvalid` to the pre-lift struct-literal \
23416             wrap on the same `(&str, &str)` fixture",
23417        );
23418    }
23419
23420    #[test]
23421    fn nome_chart_name_budget_exceeded_ctor_matches_struct_literal_wrap() {
23422        let nome = "a-very-long-cart-service-name";
23423        let reason = "sample reason text";
23424        assert_eq!(
23425            ManifestError::nome_chart_name_budget_exceeded(nome, reason),
23426            ManifestError::NomeChartNameBudgetExceeded {
23427                nome: nome.to_string(),
23428                reason: reason.to_string(),
23429            },
23430            "generated nome_chart_name_budget_exceeded ctor must produce \
23431             byte-equal `ManifestError::NomeChartNameBudgetExceeded` to \
23432             the pre-lift struct-literal wrap on the same `(&str, &str)` \
23433             fixture",
23434        );
23435    }
23436
23437    #[test]
23438    fn versao_invalid_ctor_matches_struct_literal_wrap() {
23439        let versao = "0.1";
23440        let reason = "sample reason text";
23441        assert_eq!(
23442            ManifestError::versao_invalid(versao, reason),
23443            ManifestError::VersaoInvalid {
23444                versao: versao.to_string(),
23445                reason: reason.to_string(),
23446            },
23447            "generated versao_invalid ctor must produce byte-equal \
23448             `ManifestError::VersaoInvalid` to the pre-lift \
23449             struct-literal wrap on the same `(&str, &str)` fixture",
23450        );
23451    }
23452
23453    #[test]
23454    fn etiqueta_invalid_ctor_matches_struct_literal_wrap() {
23455        let etiqueta = "MyKeyword";
23456        let reason = "sample reason text";
23457        assert_eq!(
23458            ManifestError::etiqueta_invalid(etiqueta, reason),
23459            ManifestError::EtiquetaInvalid {
23460                etiqueta: etiqueta.to_string(),
23461                reason: reason.to_string(),
23462            },
23463            "generated etiqueta_invalid ctor must produce byte-equal \
23464             `ManifestError::EtiquetaInvalid` to the pre-lift \
23465             struct-literal wrap on the same `(&str, &str)` fixture",
23466        );
23467    }
23468
23469    #[test]
23470    fn autor_invalid_ctor_matches_struct_literal_wrap() {
23471        let autor = "Ada Lovelace";
23472        let reason = "sample reason text";
23473        assert_eq!(
23474            ManifestError::autor_invalid(autor, reason),
23475            ManifestError::AutorInvalid {
23476                autor: autor.to_string(),
23477                reason: reason.to_string(),
23478            },
23479            "generated autor_invalid ctor must produce byte-equal \
23480             `ManifestError::AutorInvalid` to the pre-lift struct-literal \
23481             wrap on the same `(&str, &str)` fixture",
23482        );
23483    }
23484
23485    #[test]
23486    fn repositorio_invalid_ctor_matches_struct_literal_wrap() {
23487        let repositorio = "https://example.com/no-dot-git";
23488        let reason = "sample reason text";
23489        assert_eq!(
23490            ManifestError::repositorio_invalid(repositorio, reason),
23491            ManifestError::RepositorioInvalid {
23492                repositorio: repositorio.to_string(),
23493                reason: reason.to_string(),
23494            },
23495            "generated repositorio_invalid ctor must produce byte-equal \
23496             `ManifestError::RepositorioInvalid` to the pre-lift \
23497             struct-literal wrap on the same `(&str, &str)` fixture",
23498        );
23499    }
23500
23501    #[test]
23502    fn descricao_invalid_ctor_matches_struct_literal_wrap() {
23503        let descricao = "some description";
23504        let reason = "sample reason text";
23505        assert_eq!(
23506            ManifestError::descricao_invalid(descricao, reason),
23507            ManifestError::DescricaoInvalid {
23508                descricao: descricao.to_string(),
23509                reason: reason.to_string(),
23510            },
23511            "generated descricao_invalid ctor must produce byte-equal \
23512             `ManifestError::DescricaoInvalid` to the pre-lift \
23513             struct-literal wrap on the same `(&str, &str)` fixture",
23514        );
23515    }
23516
23517    #[test]
23518    fn licenca_invalid_ctor_matches_struct_literal_wrap() {
23519        let licenca = "not-an-spdx";
23520        let reason = "sample reason text";
23521        assert_eq!(
23522            ManifestError::licenca_invalid(licenca, reason),
23523            ManifestError::LicencaInvalid {
23524                licenca: licenca.to_string(),
23525                reason: reason.to_string(),
23526            },
23527            "generated licenca_invalid ctor must produce byte-equal \
23528             `ManifestError::LicencaInvalid` to the pre-lift \
23529             struct-literal wrap on the same `(&str, &str)` fixture",
23530        );
23531    }
23532
23533    #[test]
23534    fn edicao_invalid_ctor_matches_struct_literal_wrap() {
23535        let edicao = "26";
23536        let reason = "sample reason text";
23537        assert_eq!(
23538            ManifestError::edicao_invalid(edicao, reason),
23539            ManifestError::EdicaoInvalid {
23540                edicao: edicao.to_string(),
23541                reason: reason.to_string(),
23542            },
23543            "generated edicao_invalid ctor must produce byte-equal \
23544             `ManifestError::EdicaoInvalid` to the pre-lift \
23545             struct-literal wrap on the same `(&str, &str)` fixture",
23546        );
23547    }
23548
23549    // Cross-family invariance pin — the nine sibling ctors all route
23550    // `reason: impl Into<String>` + `<field>: &str` verbatim onto their
23551    // respective typed variants through the shared
23552    // [`manifest_field_reason_ctors!`] macro. Sweeps three fixture
23553    // shapes for `reason` (`&str` literal, owned `String`, `format!(…)`
23554    // output — the three shapes every in-crate wire-up threads through:
23555    // the parser-shaped `String` every `Result<(), String>` predicate
23556    // returns, the `e.to_string()` owned `String` the
23557    // `semver::Version::parse` arm passes, and the literal-shape reason
23558    // the `EdicaoInvalid` direct arm passes) against every generated arm
23559    // so any per-arm wrapper transformation drift surfaces here rather
23560    // than at a downstream diagnostic-shape mismatch. Peer of the
23561    // sibling
23562    // [`crate::aplicacao::tests::aplicacao_field_reason_ctors_route_reason_through_into_uniformly`]
23563    // pin (981060b) on the sibling `AplicacaoError` envelope's identical
23564    // two-slot family.
23565    #[test]
23566    fn manifest_field_reason_ctors_route_reason_through_into_uniformly() {
23567        let via_literal = "literal reason text";
23568        let via_owned: String = String::from("literal reason text");
23569        let via_format = format!("{} reason text", "literal");
23570        assert_eq!(
23571            ManifestError::nome_invalid("n", via_literal),
23572            ManifestError::nome_invalid("n", via_owned.clone()),
23573        );
23574        assert_eq!(
23575            ManifestError::nome_invalid("n", via_literal),
23576            ManifestError::nome_invalid("n", via_format.clone()),
23577        );
23578        assert_eq!(
23579            ManifestError::nome_chart_name_budget_exceeded("n", via_literal),
23580            ManifestError::nome_chart_name_budget_exceeded("n", via_owned.clone()),
23581        );
23582        assert_eq!(
23583            ManifestError::versao_invalid("0.1", via_literal),
23584            ManifestError::versao_invalid("0.1", via_owned.clone()),
23585        );
23586        assert_eq!(
23587            ManifestError::etiqueta_invalid("k", via_literal),
23588            ManifestError::etiqueta_invalid("k", via_owned.clone()),
23589        );
23590        assert_eq!(
23591            ManifestError::autor_invalid("a", via_literal),
23592            ManifestError::autor_invalid("a", via_owned.clone()),
23593        );
23594        assert_eq!(
23595            ManifestError::repositorio_invalid("r", via_literal),
23596            ManifestError::repositorio_invalid("r", via_owned.clone()),
23597        );
23598        assert_eq!(
23599            ManifestError::descricao_invalid("d", via_literal),
23600            ManifestError::descricao_invalid("d", via_owned.clone()),
23601        );
23602        assert_eq!(
23603            ManifestError::licenca_invalid("l", via_literal),
23604            ManifestError::licenca_invalid("l", via_owned.clone()),
23605        );
23606        assert_eq!(
23607            ManifestError::edicao_invalid("26", via_literal),
23608            ManifestError::edicao_invalid("26", via_owned),
23609        );
23610        assert_eq!(
23611            ManifestError::edicao_invalid("26", via_literal),
23612            ManifestError::edicao_invalid("26", via_format),
23613        );
23614    }
23615
23616    // Cross-arm routing pin — the nine sibling ctors accept both `&str`
23617    // (from the [`Caixa::nome`] / [`Caixa::versao`] / [`Caixa::repositorio`]
23618    // / [`Caixa::descricao`] / [`Caixa::licenca`] / [`Caixa::edicao`]
23619    // accessors that return `&str`) and `&String` (from the
23620    // [`Caixa::etiquetas`] / [`Caixa::autores`] slice iterators that yield
23621    // `&String`) at the `<field>: &str` parameter via Deref coercion. This
23622    // pin sweeps both call shapes against the two accessors' actual
23623    // wire-up postures so a future rebrand of the etiquetas / autores
23624    // slice-iterator type (a lift from `&[String]` to `&[Cow<'_, str>]`,
23625    // a `smol_str::SmolStr` per-entry swap) that silently broke the
23626    // Deref-coercion path surfaces at this pin rather than at a
23627    // recompile-time type-mismatch far from the ctor family.
23628    #[test]
23629    fn manifest_field_reason_ctors_accept_both_str_and_string_slice_iters() {
23630        let owned: String = String::from("MyKeyword");
23631        // `&str` literal — the canonical accessor-return shape
23632        // ([`Caixa::nome`] etc. yield `&str`).
23633        assert_eq!(
23634            ManifestError::etiqueta_invalid("MyKeyword", "r"),
23635            ManifestError::EtiquetaInvalid {
23636                etiqueta: "MyKeyword".to_string(),
23637                reason: "r".to_string(),
23638            },
23639        );
23640        // `&String` — the canonical slice-iterator-yield shape
23641        // ([`Caixa::etiquetas`] / [`Caixa::autores`] yield `&String`).
23642        assert_eq!(
23643            ManifestError::etiqueta_invalid(&owned, "r"),
23644            ManifestError::EtiquetaInvalid {
23645                etiqueta: owned.clone(),
23646                reason: "r".to_string(),
23647            },
23648        );
23649        // Both call shapes must produce byte-equal
23650        // [`ManifestError::EtiquetaInvalid`] values on the same
23651        // underlying `String`, so a wire-up threading `etiqueta: &String`
23652        // through the same ctor as a peer wire-up threading `nome: &str`
23653        // through it collapses onto one canonical shape.
23654        assert_eq!(
23655            ManifestError::etiqueta_invalid("MyKeyword", "r"),
23656            ManifestError::etiqueta_invalid(&owned, "r"),
23657        );
23658    }
23659
23660    // ── `manifest_field_only_ctors!` — the paired `{ <field>: String }`
23661    //    single-slot envelope on `ManifestError`, direct sibling of the
23662    //    peer [`crate::aplicacao::aplicacao_caixa_only_ctors!`] (d9f6867,
23663    //    `{ caixa: String }` on `AplicacaoError`) and
23664    //    [`crate::aplicacao::aplicacao_path_only_ctors!`] (3ba8de6,
23665    //    `{ path: String }` on `AplicacaoError`) on the M3 mesh envelope,
23666    //    of the peer [`crate::supervisor::supervisor_caixa_only_ctors!`]
23667    //    (db09650, `{ caixa: String }` on `SupervisorError`), and of the
23668    //    peer [`crate::dep::dep_nome_only_ctors!`] (792aa92,
23669    //    `{ nome: String }` on `DepError`) folds on their sibling
23670    //    envelopes. Two-variant lift closing the last two open-coded
23671    //    single-`String`-slot ctor sites at
23672    //    [`Caixa::validate_etiquetas`] and [`Caixa::validate_autores`].
23673
23674    #[test]
23675    fn etiqueta_duplicate_ctor_matches_struct_literal_wrap() {
23676        assert_eq!(
23677            ManifestError::etiqueta_duplicate("mesh"),
23678            ManifestError::EtiquetaDuplicate {
23679                etiqueta: "mesh".to_string(),
23680            },
23681            "generated etiqueta_duplicate ctor must produce byte-equal \
23682             `ManifestError::EtiquetaDuplicate` to the pre-lift \
23683             struct-literal wrap on the same `&str` fixture",
23684        );
23685    }
23686
23687    #[test]
23688    fn autor_duplicate_ctor_matches_struct_literal_wrap() {
23689        assert_eq!(
23690            ManifestError::autor_duplicate("pleme-io"),
23691            ManifestError::AutorDuplicate {
23692                autor: "pleme-io".to_string(),
23693            },
23694            "generated autor_duplicate ctor must produce byte-equal \
23695             `ManifestError::AutorDuplicate` to the pre-lift \
23696             struct-literal wrap on the same `&str` fixture",
23697        );
23698    }
23699
23700    #[test]
23701    fn manifest_field_only_ctors_route_field_through_to_string() {
23702        // Cross-axis pin: sweep the sole constructor input axis
23703        // (`<field>: &str`) through a non-default fixture value against
23704        // every generated arm in the [`manifest_field_only_ctors!`]
23705        // macro, so any wrapper-side lowercase / trim / truncate / silent
23706        // constant-substitution on the `<field>.to_string()` sole-field
23707        // construction surfaces here rather than at a downstream
23708        // diagnostic-shape mismatch. Peer of the sibling
23709        // [`crate::aplicacao::tests::aplicacao_caixa_only_ctors_route_caixa_through_to_string`]
23710        // (d9f6867) and
23711        // [`crate::aplicacao::tests::aplicacao_path_only_ctors_route_path_through_to_string`]
23712        // (3ba8de6) cross-axis pins on the peer `AplicacaoError`
23713        // single-`String`-slot envelopes.
23714        let value = "cache-v2";
23715        assert_eq!(
23716            ManifestError::etiqueta_duplicate(value),
23717            ManifestError::EtiquetaDuplicate {
23718                etiqueta: value.to_string(),
23719            },
23720        );
23721        assert_eq!(
23722            ManifestError::autor_duplicate(value),
23723            ManifestError::AutorDuplicate {
23724                autor: value.to_string(),
23725            },
23726        );
23727    }
23728
23729    #[test]
23730    fn manifest_field_only_ctors_accept_both_str_and_string_slice_iters() {
23731        // The two wire-up sites at [`Caixa::validate_etiquetas`] and
23732        // [`Caixa::validate_autores`] each thread a `&String` loop head
23733        // through the ctor via Deref coercion at the `<field>: &str`
23734        // parameter — this pin locks that call shape's byte-equality
23735        // against the direct `&str` shape so a future rebrand of the
23736        // `:etiquetas` / `:autores` slice-iterator type that silently
23737        // broke the Deref-coercion path surfaces here rather than at a
23738        // recompile-time type-mismatch far from the ctor family. Peer of
23739        // the sibling
23740        // [`manifest_field_reason_ctors_accept_both_str_and_string_slice_iters`]
23741        // pin on the peer two-slot `{ <field>: String, reason: String }`
23742        // envelope.
23743        let etiqueta: String = String::from("mesh");
23744        assert_eq!(
23745            ManifestError::etiqueta_duplicate("mesh"),
23746            ManifestError::etiqueta_duplicate(&etiqueta),
23747        );
23748        let autor: String = String::from("pleme-io");
23749        assert_eq!(
23750            ManifestError::autor_duplicate("pleme-io"),
23751            ManifestError::autor_duplicate(&autor),
23752        );
23753    }
23754}