Skip to main content

caixa_core/
manifest.rs

1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use tatara_lisp::DeriveTataraDomain;
5
6use thiserror::Error;
7
8use crate::{
9    CaixaKind, Dep,
10    behavior::BehaviorSpec,
11    dep::DepError,
12    limits::LimitsSpec,
13    render::{
14        PathShapeViolation, is_computeunit_yaml_extension, is_git_repo_url, is_lisp_extension,
15        is_sandboxed_relative_path,
16    },
17    supervisor::SupervisorSpec,
18    upgrade::UpgradeFromEntry,
19};
20
21/// Top-level manifest for a caixa (a tatara-lisp package).
22///
23/// Authored as `caixa.lisp`:
24///
25/// ```lisp
26/// (defcaixa
27///   :nome        "pangea-tatara-aws"
28///   :versao      "0.1.0"
29///   :kind        Biblioteca
30///   :edicao      "2026"
31///   :descricao   "AWS provider caixa for tatara-lisp"
32///   :repositorio "github:pleme-io/pangea-tatara-aws"
33///   :licenca     "MIT"
34///   :autores     ("pleme-io")
35///   :etiquetas   ("iac" "aws" "pangea")
36///   :deps        ((:nome "caixa-teia"    :versao "^0.1")
37///                 (:nome "iac-forge-ir"  :versao "^0.5"))
38///   :deps-dev    ((:nome "tatara-check"  :versao "*"))
39///   :bibliotecas ("lib/pangea-tatara-aws.lisp"))
40/// ```
41///
42/// Because `Caixa` derives [`tatara_lisp::domain::TataraDomain`], the manifest
43/// is parsed directly by the tatara-lisp compiler — an ill-formed manifest is
44/// a compile error, not a runtime error.
45#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[tatara(keyword = "defcaixa")]
48pub struct Caixa {
49    /// Package name — the canonical string used in `:deps`, the registry, and
50    /// the default lib/exe entry names.
51    pub nome: String,
52
53    /// Package version — a semver literal like `"0.1.0"`. Parsed lazily via
54    /// [`crate::CaixaVersion::parse`].
55    pub versao: String,
56
57    /// What this caixa produces. See [`CaixaKind`].
58    pub kind: CaixaKind,
59
60    /// Language edition — determines macro surface + compatibility flags.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub edicao: Option<String>,
63
64    /// Free-form description shown in the registry listing.
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub descricao: Option<String>,
67
68    /// Homepage or repo URL.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub repositorio: Option<String>,
71
72    /// SPDX license expression — `"MIT"`, `"Apache-2.0 OR MIT"`, etc.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub licenca: Option<String>,
75
76    /// Authors — free-form strings.
77    #[serde(default)]
78    pub autores: Vec<String>,
79
80    /// Topical tags used for registry search.
81    #[serde(default)]
82    pub etiquetas: Vec<String>,
83
84    /// Runtime dependencies.
85    #[serde(default)]
86    pub deps: Vec<Dep>,
87
88    /// Development-only dependencies (tests, lint, bench).
89    #[serde(default)]
90    pub deps_dev: Vec<Dep>,
91
92    /// Paths to executable entry points (relative to the package root).
93    /// Required when `:kind Binario`.
94    #[serde(default)]
95    pub exe: Vec<String>,
96
97    /// Paths to library entry points (relative to the package root).
98    /// First entry is the canonical `lib/<nome>.lisp`; when omitted under
99    /// `:kind Biblioteca`, the layout check expects `lib/<nome>.lisp`.
100    #[serde(default)]
101    pub bibliotecas: Vec<String>,
102
103    /// Paths to service manifests (relative to the package root).
104    /// Required when `:kind Servico`.
105    #[serde(default)]
106    pub servicos: Vec<String>,
107
108    // ── M2 typed-substrate extensions per theory/ABSORPTION-ROADMAP.md ──
109    //
110    // All four are optional + default to "absent"; existing caixas
111    // round-trip unchanged. Each maps onto a prior-art primitive named
112    // in theory/INSPIRATIONS.md:
113    //
114    //   :limits        — Lunatic per-process limits (§III.1)
115    //   :behavior      — OTP gen_server callbacks  (§II.3)
116    //   :upgrade-from  — OTP appup migration       (§II.4)
117    //   :estrategia    — OTP supervisor strategy   (§II.2 + §III.2)
118    //   :children      — OTP supervisor children    (§II.2 + §III.2)
119    //
120    // The supervisor slots are flat on Caixa (vs nested under a
121    // SupervisorSpec sub-form) to keep tatara-lisp authoring at one
122    // level of nesting; SupervisorSpec exists for validation +
123    // composition convenience (`Caixa::supervisor_view()`).
124    /// Lunatic-style per-process resource limits. None = unbounded.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub limits: Option<LimitsSpec>,
127
128    /// OTP-shaped behavior callbacks for Servico-kind caixas.
129    /// Authored as `(:on-init "..." :on-call "..." …)`.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub behavior: Option<BehaviorSpec>,
132
133    /// OTP appup — declarative upgrade instructions per prior version.
134    /// Empty list = no hot-upgrade path declared (caller falls back to
135    /// `:Restart` strategy).
136    #[serde(default)]
137    pub upgrade_from: Vec<UpgradeFromEntry>,
138
139    /// OTP supervisor strategy. Required when `:kind Supervisor`;
140    /// ignored otherwise.
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub estrategia: Option<crate::supervisor::RestartStrategy>,
143
144    /// Max restarts before the supervisor itself fails. Defaults via
145    /// SupervisorSpec at validation time.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub max_restarts: Option<u32>,
148
149    /// Sliding window for `max_restarts`. Authored as a duration
150    /// string (`"60s"`, `"5m"`).
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub restart_window: Option<String>,
153
154    /// Static children of a supervisor. Required for OneForOne /
155    /// OneForAll / RestForOne; must be empty for SimpleOneForOne.
156    #[serde(default)]
157    pub children: Vec<crate::supervisor::ChildSpec>,
158
159    // ── M3 Aplicacao slots (theory/MESH-COMPOSITION.md) ─────────────────
160    //
161    // Required when :kind Aplicacao; ignored otherwise.
162    // Composed into a typed AplicacaoSpec via Caixa::aplicacao_view().
163    /// Member Servicos that make up this Aplicacao. Each is a
164    /// caixa-name + version-constraint pair. Required for Aplicacao.
165    #[serde(default)]
166    pub membros: Vec<crate::aplicacao::Membro>,
167
168    /// WIT-typed inter-Servico contracts. Each `:de` and `:para`
169    /// must reference a name in `:membros`.
170    #[serde(default)]
171    pub contratos: Vec<crate::aplicacao::WitContract>,
172
173    /// Mesh-level policies (timeout, retries, circuit-breaker, mTLS,
174    /// rate-limit). Apply to every contrato unless overridden per-edge
175    /// in M4.
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub politicas: Option<crate::aplicacao::MeshPolicy>,
178
179    /// Placement strategy across the cluster fleet
180    /// (single-node | replicated | sharded).
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub placement: Option<crate::aplicacao::Placement>,
183
184    /// External entry point — gateway / ingress shape. Optional;
185    /// only for public Aplicacaos.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub entrada: Option<crate::aplicacao::Entrada>,
188
189    // ── Acao slot (CANTEIRO §7.1-C) ──────────────────────────────────────
190    //
191    // Required when :kind Acao; ignored otherwise (mirrors the M2/
192    // supervisor-tree/M3 slot triads above — a declared-but-foreign `:ci`
193    // is a `LayoutError::CiOnNonAcao` build error, not a silent drop).
194    /// Typed CI run — a repo's CI run as a set of typed nodes + their
195    /// dependency edges. Required for `:kind Acao`; validated (not
196    /// rendered) by the `caixa-actions` renderer via
197    /// `canteiro_types::decompose`. See `caixa-actions`' crate docs for
198    /// the M0 validate-only contract.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub ci: Option<canteiro_types::CiRun>,
201}
202
203/// Why reading a manifest into a [`Caixa`] failed.
204///
205/// Split from [`ManifestError`] (which reports a *parsed* manifest that is
206/// semantically wrong) because the two answer different questions, and the
207/// distinction is the whole point of this type: `ManifestError` means "your
208/// caixa is wrong", `LeituraError::DialetoEstrangeiro` means "this file is not
209/// a caixa".
210#[derive(Debug, thiserror::Error)]
211pub enum LeituraError {
212    /// The source is not readable as a `(defcaixa …)` package manifest — bad
213    /// syntax, a wrong head symbol, an unknown or mistyped slot.
214    ///
215    /// `#[source]`, not `#[error(transparent)]`. Transparent delegates
216    /// `source()` past the inner error to ITS source, which drops the
217    /// `LispError` off the cause chain — and `feira`'s
218    /// `load_caixa_parse_error_preserves_underlying_lisp_error_on_chain`
219    /// pins that a caller can `downcast_ref::<tatara_lisp::LispError>()`
220    /// through an anyhow context to read the typed payload. That pin caught
221    /// this exact regression when the variant first landed transparent.
222    #[error("{0}")]
223    Leitura(
224        #[source]
225        #[from]
226        tatara_lisp::LispError,
227    ),
228
229    /// The source IS a well-formed `(defcaixa …)` form, but of a different
230    /// declaration than this crate's.
231    ///
232    /// The variant that did not exist before, and whose absence is the defect.
233    /// A `(defcaixa :name "x" :ecosystem :go …)` used to reach the derive's
234    /// `parse_kwargs_strict` and come back as an unknown-keyword rejection —
235    /// byte-identical in shape to a typo in a real manifest. Measured over the
236    /// org checkout on 2026-07-31, that shape is the MAJORITY of the corpus, so
237    /// the confusing error was also the common one.
238    ///
239    /// Carrying the dialect means a consumer can branch on "not mine" without
240    /// re-parsing, and a census can count it. Every user-facing byte-string
241    /// (canonical keyword, one-line description, consuming crate) is a
242    /// projection of [`crate::dialeto::CaixaDialeto`] — the variant stores the
243    /// typed dialect and the `#[error]` template calls
244    /// [`CaixaDialeto::palavra_canonica`] /
245    /// [`CaixaDialeto::descricao`] / [`CaixaDialeto::consumidor`] on it, so
246    /// the three axes cannot silently diverge from the classification. Prior
247    /// to this closure the variant carried each accessor's return value as a
248    /// stored `&'static str` snapshot alongside `dialeto`, and the sole
249    /// constructor at [`Caixa::from_lisp`] filled all four fields — a caller
250    /// could construct `DialetoEstrangeiro { dialeto: Molde,
251    /// palavra_canonica: "defcaixa", … }` and every downstream consumer
252    /// (Display, ad-hoc audit, future JSON serialization) would silently
253    /// disagree with `dialeto.palavra_canonica() == "defmolde"`. The typed
254    /// enum owns the projections; the variant only carries the axis.
255    #[error(
256        "this is a `{palavra}` declaration ({desc}), read by \
257         {cons} — not a caixa-core package manifest. `defcaixa` is the \
258         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
259         are different declarations that shared one keyword until 2026-07-31",
260        palavra = dialeto.palavra_canonica(),
261        desc = dialeto.descricao(),
262        cons = dialeto.consumidor()
263    )]
264    DialetoEstrangeiro {
265        /// Which declaration this actually is. Sole authoritative axis;
266        /// every user-facing projection routes through
267        /// [`crate::dialeto::CaixaDialeto`]'s typed accessors so the four
268        /// axes cannot silently disagree.
269        dialeto: crate::dialeto::CaixaDialeto,
270    },
271
272    /// Not a manifest declaration at all.
273    #[error(transparent)]
274    Dialeto(#[from] crate::dialeto::DialetoError),
275}
276
277/// Substrate-canonical universal-axis per-[`Caixa`] `:licenca` SPDX-shaped
278/// license-expression fallback for the `Option<String>` `:licenca` slot —
279/// the `"MIT"` SPDX identifier every [`caixa-helm`]-rendered
280/// `lareira-<nome>` Helm chart's `README.md` `## License` section folds an
281/// author-omitted (`None`) `:licenca` slot through, extracted as a typed
282/// `pub const` so every substrate-side consumer that resolves "what license
283/// scalar does an author-omitted `:licenca` degrade onto?" reaches for
284/// exactly one substrate-primitive `&'static str`.
285///
286/// The `:licenca` fallback axis has one production consumer today — the
287/// [`caixa-helm`] `build_readme` fold at `caixa-helm/src/lib.rs`'s
288/// `caixa.licenca().unwrap_or(CAIXA_LICENCA_DEFAULT)` `README.md`
289/// `## License` section body — with three sibling caixa-core sites that
290/// cite the `"MIT"` fallback in prose (this crate's [`Caixa::licenca`]
291/// accessor's docstring, [`Self::validate_licenca`]'s docstring, and the
292/// [`ManifestError::LicencaEmpty`] `#[error]` template's user-facing text)
293/// all quoting the exact byte-string a future substrate-side rebrand of the
294/// fallback (a tightening to `"Apache-2.0"` as the substrate absorbs the
295/// wasm-component-model conventions the `wasi:*` WIT worlds already carry,
296/// a per-cluster license-default overlay the M4 CR materializer resolves
297/// per-CR, a promotion to the plain `Option<String>` byte-string into a
298/// richer `SpdxExpression` enum once the SPDX-expression parser lands per
299/// [`Self::validate_licenca`]'s docstring roadmap) would silently split
300/// against — the caixa-helm renderer would emit the new byte, the
301/// docstrings would still cite the prior byte, and every author who reads
302/// the accessor docstring before authoring would file a fresh
303/// `:licenca "MIT"` verbatim rather than defer to the substrate default,
304/// with the drift surfacing at chart-README-audit time far from the
305/// substrate rebrand commit.
306///
307/// Prior to this lift the sole production emitter (`build_readme`) carried
308/// an inline `"MIT"` byte literal at
309/// `caixa-helm/src/lib.rs:1018`'s `.unwrap_or("MIT")` fallback arm — one
310/// occurrence of the same load-bearing per-`Caixa` universal-axis
311/// SPDX-shaped license-expression convention as the four sibling caixa-core
312/// docstring citations, drift-prone by construction ahead of the second
313/// occurrence the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
314/// materializer's per-Aplicacao registry-annotation synthesis (the
315/// [`Self::validate_licenca`] roadmap already names the `Chart.yaml
316/// annotations["artifacthub.io/license"]` axis every registry-facing chart
317/// carries as the second consumer) will surface.
318///
319/// The `"MIT"` value pins the canonical CAIXA-SDLC §I license scaffold
320/// every `feira init`-emitted [`Self::template`] carries verbatim
321/// (`:licenca "MIT"`) and every substrate-side renderer fixture
322/// ([`caixa-helm`]'s `sample_caixa`, [`caixa-flux`]'s renderer fixtures,
323/// [`caixa-mesh`]'s renderer fixtures) seeds by construction, matching the
324/// pleme-io repo `LICENSE` header this workspace itself ships under. The
325/// alternatives an author declares explicitly (compound SPDX expressions
326/// like `"Apache-2.0 OR MIT"`, permissive-family peers like
327/// `"Apache-2.0"` / `"BSD-3-Clause"`, license-with-exception forms like
328/// `"Apache-2.0 WITH LLVM-exception"`) express deliberate license postures
329/// an author declares explicitly, never a posture an author-omitted slot
330/// should silently assume by default.
331///
332/// Lifted as a typed `pub const` so the substrate's chosen license
333/// fallback has exactly one source of truth on the `:licenca` fallback
334/// axis, on the same substrate-primitive lift discipline the peer
335/// per-`Caixa` load-bearing-scalar constants
336/// ([`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`],
337/// [`crate::version::DEFAULT_GIT_REMOTE`],
338/// [`crate::version::DEFAULT_PLEME_GIT_ORG`]) already carry on the sibling
339/// per-`Caixa` universal-axis publish-side convention surface, and the
340/// same discipline the sibling M2 per-supervisor default set carries
341/// end-to-end ([`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`],
342/// [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`],
343/// [`crate::supervisor::SUPERVISOR_RESTART_WINDOW_DEFAULT`],
344/// [`crate::supervisor::SUPERVISOR_CHILD_RESTART_DEFAULT`]) and the M3
345/// per-`:placement` default set already carries
346/// ([`crate::aplicacao::PLACEMENT_ESTRATEGIA_DEFAULT`]) on the paired
347/// M2 / M3 typed-slot-default axes. First typed default on the outer
348/// top-level [`Caixa`] universal-axis surface to converge onto the
349/// substrate-primitive-lift discipline the M2 / M3 typed-slot families
350/// already carry.
351pub const CAIXA_LICENCA_DEFAULT: &str = "MIT";
352
353impl Caixa {
354    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
355    ///
356    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
357    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
358    /// and who reads it, instead of an unknown-keyword rejection that reads as
359    /// "your manifest is broken".
360    ///
361    /// The ordering is load-bearing. Handing a foreign dialect to the derive
362    /// first and interpreting the failure afterwards would mean guessing from
363    /// an error message, and the guess would be wrong for every file whose
364    /// first unknown slot happens to be one both schemas could plausibly carry.
365    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
366        use tatara_lisp::domain::TataraDomain;
367        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
368        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
369
370        // Route the foreign-dialect rejection gate through the lifted
371        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
372        // typed predicate rather than the pre-lift hand-rolled three-arm
373        // `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
374        // literal — the `defmolde` declaration-family partition (the two-
375        // arity closure of [`crate::dialeto::CaixaDialeto::Molde`] and
376        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two arms
377        // whose sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
378        // projection already collapses onto `"defmolde"` and whose sibling
379        // [`crate::dialeto::CaixaDialeto::consumidor`] projection already
380        // collapses onto `"pleme-doc-gen"`) resolves through one dispatch
381        // on the substrate primitive. `Pacote` (the tatara-lisp package
382        // manifest this derive can parse) and `Desconhecido` (deliberately
383        // falls through to the derive rather than short-circuiting: a
384        // `(defcaixa …)` matching neither schema is most likely a genuine
385        // package manifest with a typo in `:nome`, and the derive's
386        // diagnostic — which names the offending keyword and suggests the
387        // nearest slot — is far better than anything this classifier
388        // could say) both return `false` from `is_molde_family()` and fall
389        // through to the derive. Only the typed dialect flows into the
390        // error — the three user-facing projections (canonical keyword,
391        // description, consumer) are read at Display time through
392        // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
393        // variant cannot carry a snapshot that drifts from
394        // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
395        // `descricao` / `consumidor`. A future fifth dialect the
396        // [`crate::dialeto`] module doc's "third dialect" hazard
397        // actualises that belongs to the `defmolde` family lands one
398        // match arm at [`crate::dialeto::CaixaDialeto::is_molde_family`]
399        // and this gate picks up the new arm by construction — the pre-
400        // lift wildcard `foreign =>` was compile-time-anonymous and would
401        // silently absorb any hypothetical fifth `defcaixa`-family arm as
402        // foreign; routing the partition through the typed predicate
403        // closes both drift surfaces.
404        let dialeto = crate::dialeto::classify_form(first)?;
405        if dialeto.is_molde_family() {
406            return Err(LeituraError::DialetoEstrangeiro { dialeto });
407        }
408
409        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
410    }
411
412    /// Register `Caixa` with the global tatara-lisp domain registry so
413    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
414    /// the registry (e.g. `tatara-check`).
415    ///
416    /// Returns the typed [`tatara_lisp::KeywordCollision`] on the second
417    /// (and every subsequent) call in the same process — one keyword,
418    /// one type, per process is a hard invariant of the upstream
419    /// registry, and a caller that hits it must fix its crate graph
420    /// rather than swallowing the error. Peer of the sibling per-crate
421    /// `register()` entry points at `caixa-flake/src/flake.rs`,
422    /// `caixa-fmt/src/lisp_config.rs`, `caixa-lacre/src/lock.rs`,
423    /// `caixa-lint/src/lisp_config.rs`, `caixa-resolver/src/lisp_config.rs`
424    /// — every substrate crate that owns a tatara-lisp keyword now
425    /// propagates the same typed error verbatim, so a downstream binary
426    /// that seeds the registry (`tatara-check`, the future LSP) reaches
427    /// for one shape at every call site.
428    ///
429    /// # Errors
430    ///
431    /// [`tatara_lisp::KeywordCollision`] when a peer type has already
432    /// claimed the `defcaixa` keyword in this process.
433    pub fn register() -> Result<(), tatara_lisp::KeywordCollision> {
434        tatara_lisp::domain::register::<Self>()
435    }
436
437    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
438    /// accessor every consumer of the top-level manifest's license axis
439    /// keys off — returns the author-declared `:licenca` byte-string
440    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
441    /// `Option<String>` storage. `None` when the slot is absent (the
442    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
443    /// fallback" shape [`Self::validate_licenca`] documents at
444    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
445    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
446    /// predicate too, so an authored-but-unset `:licenca` round-trips to
447    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
448    /// section structurally identical to one that omits the slot).
449    ///
450    /// The `:licenca` slot carries the universal-axis SPDX-expression
451    /// license identifier every kind of caixa emits under (CAIXA-SDLC
452    /// §I — the author-facing surface every `defcaixa` form supplies) —
453    /// the typed slot's `Option<String>` accept-set (empty-string
454    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
455    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
456    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
457    /// section (caixa-helm/src/lib.rs:962) and (through future
458    /// tightening documented at [`Self::validate_licenca`]) the
459    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
460    /// registry-facing chart carries. Every downstream consumer that
461    /// reads the license byte-string keys off this scalar (the
462    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
463    /// routes through `self.licenca.as_deref()`, the caixa-helm
464    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
465    /// the fallback off the `Option::is_none()` arm, every future
466    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
467    /// acknowledges).
468    ///
469    /// Prior to this lift the `.licenca` field was accessed inline at
470    /// two production sites — [`Self::validate_licenca`]'s
471    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
472    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
473    /// "MIT".into())` `README.md` `## License` fold — two open-coded
474    /// field-accesses that expressed no compile-time link back to the
475    /// typed slot. A future extension of the `:licenca` axis to a
476    /// richer author surface — a per-`:licenca` structured SPDX
477    /// expression parser + license-id allowlist (the future tightening
478    /// [`Self::validate_licenca`]'s docstring acknowledges), a
479    /// per-cluster license-default overlay the M4 CR materializer
480    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
481    /// unlisted caixa" arm), a promotion of the plain
482    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
483    /// once the SPDX-expression parser lands — would have had to be
484    /// threaded through both open-coded copies in lockstep or the
485    /// validate gate and the caixa-helm emit path would silently
486    /// disagree on which license a given [`Caixa`] resolves to (an
487    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
488    /// while the emit path silently rendered a stale `MIT` fallback,
489    /// or vice versa). Lifting the resolution to a typed method on the
490    /// substrate primitive means every downstream consumer of the
491    /// caixa's per-`Caixa` license surface reaches for exactly one
492    /// typed dispatch — the resolver's accept-set migrates as a unit
493    /// on any future axis addition.
494    ///
495    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
496    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
497    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
498    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
499    /// substrate primitive, thin projections at each consumer"
500    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
501    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
502    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
503    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
504    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
505    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
506    /// typed-slot atom axes, extended here to the outer top-level
507    /// `Caixa` universal-axis surface. Named `licenca()` to match the
508    /// storage field's name; the accessor's identity maps onto the
509    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
510    /// carries.
511    #[must_use]
512    pub const fn licenca(&self) -> Option<&str> {
513        match &self.licenca {
514            Some(s) => Some(s.as_str()),
515            None => None,
516        }
517    }
518
519    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
520    /// accessor every consumer of the top-level manifest's homepage /
521    /// source-of-truth axis keys off — returns the author-declared
522    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
523    /// from the typed slot's own `Option<String>` storage. `None` when
524    /// the slot is absent (the canonical "omit to defer to the renderer's
525    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
526    /// carries the `Option<String>` through verbatim so an author-omitted
527    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
528    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
529    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
530    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
531    /// fallback derived from `caixa.nome`).
532    ///
533    /// The `:repositorio` slot carries the universal-axis git-repo-URL
534    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
535    /// §I — the author-facing surface every `defcaixa` form supplies) —
536    /// the typed slot's `Option<String>` accept-set (empty-string
537    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
538    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
539    /// past the shared [`crate::render::is_git_repo_url`] predicate the
540    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
541    /// four load-bearing downstream consumers:
542    ///
543    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
544    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
545    ///     universal-axis identity gate wired at caixa-build time.
546    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
547    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
548    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
549    ///     that ingests the chart (ArtifactHub, chartmuseum,
550    ///     `helm search repo`) surfaces as the chart's canonical source-
551    ///     of-truth link.
552    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
553    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
554    ///     chart's `README.md` header link back to the source repo,
555    ///     which every author who inspects the rendered chart bundle
556    ///     lands at.
557    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
558    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
559    ///     the rendered `GitRepository` CR's `spec.url` field, which
560    ///     FluxCD's `source-controller` polls to reconcile the caixa's
561    ///     manifest bundle from git.
562    ///
563    /// Prior to this lift the `.repositorio` field was accessed inline
564    /// at four production sites — [`Self::validate_repositorio`]'s
565    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
566    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
567    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
568    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
569    /// `README.md` `## Source` fold, and the caixa-flux
570    /// `ClusterBundleOpts::for_caixa`
571    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
572    /// `GitRepository.spec.url` fold — four open-coded field-accesses
573    /// that expressed no compile-time link back to the typed slot. A
574    /// future extension of the `:repositorio` axis to a richer author
575    /// surface — a per-`:repositorio` structured
576    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
577    /// (the future tightening [`Self::validate_repositorio`]'s
578    /// docstring anticipates alongside the peer per-`:deps :fonte
579    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
580    /// materializer resolves per-CR (the "cluster policy rewrites
581    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
582    /// arm the private-registry story acknowledges), a promotion of
583    /// the plain `Option<String>` byte-string to a richer
584    /// `RepoUrl` enum discriminated on scheme — would have had to be
585    /// threaded through all four open-coded copies in lockstep or the
586    /// validate gate and the three emit paths would silently disagree
587    /// on which URL a given [`Caixa`] resolves to (an author's
588    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
589    /// while one of the emit paths silently rendered a stale URL, or
590    /// vice versa). Lifting the resolution to a typed method on the
591    /// substrate primitive means every downstream consumer of the
592    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
593    /// typed dispatch — the resolver's accept-set migrates as a unit on
594    /// any future axis addition.
595    ///
596    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
597    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
598    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
599    /// projection pattern this lift folds on. Same "one typed dispatch
600    /// on the substrate primitive, thin projections at each consumer"
601    /// discipline the peer per-`:placement`
602    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
603    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
604    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
605    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
606    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
607    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
608    /// typed-slot atom axes, extended here to the second outer top-level
609    /// `Caixa` universal-axis surface. Named `repositorio()` to match
610    /// the storage field's name; the accessor's identity maps onto the
611    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
612    /// carries.
613    #[must_use]
614    pub const fn repositorio(&self) -> Option<&str> {
615        match &self.repositorio {
616            Some(s) => Some(s.as_str()),
617            None => None,
618        }
619    }
620
621    /// Substrate-canonical per-`Caixa` **resolved-git-repo-URL** composer —
622    /// returns the caixa's canonical git-source-of-truth URL as an owned
623    /// [`String`], author-declared `:repositorio` byte-string verbatim on
624    /// the `Some` arm and the substrate's canonical pleme-org github URL
625    /// fallback ([`crate::DEFAULT_PLEME_GIT_ORG`] and [`Self::nome`]
626    /// interpolated into `https://github.com/<org>/<nome>`) on the
627    /// `None` arm. Every substrate-side consumer that resolves
628    /// "which git URL does this caixa's source live at?" reaches for
629    /// exactly one typed dispatch on the substrate primitive — the raw
630    /// `caixa.repositorio().map(str::to_owned).unwrap_or_else(|| format!(
631    /// "https://github.com/{org}/{nome}", org = DEFAULT_PLEME_GIT_ORG,
632    /// nome = caixa.nome()))` open-coded composition every prior caller
633    /// re-derived collapses onto one canonical arm.
634    ///
635    /// Distinct from [`Self::repositorio`] (`Option<&str>`, exposes the
636    /// author-omitted / author-declared partition to the caller) — this
637    /// accessor is the **resolved** URL surface, folding the fallback in
638    /// at the substrate-primitive boundary. Every consumer that keys off
639    /// the `Option::is_none()` discriminator (a [`Chart.yaml`] `home:`
640    /// field emit that must omit the field entirely on an author-omitted
641    /// `:repositorio`, per the [`Self::repositorio`] docstring's
642    /// documented four-consumer list) reaches through the raw
643    /// [`Self::repositorio`] `Option<&str>` accessor by construction — the
644    /// resolved-URL composer sits alongside it as the second projection
645    /// on the same underlying `:repositorio` slot rather than replacing
646    /// the raw accessor.
647    ///
648    /// The fallback branch is the exact byte-image of the prior inline
649    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url` composer at
650    /// caixa-flux/src/lib.rs:2080 — pinned by the sibling caixa-flux
651    /// byte-parity test
652    /// `cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`
653    /// against a future implementation of this method that reordered the
654    /// `format!` template arguments, migrated the `<org>` segment to a
655    /// different constant (the [`crate::DEFAULT_PLEME_GIT_ORG`] axis a
656    /// future substrate-side git-org migration may split off), or
657    /// silently absorbed the empty-string arm (a hypothetical
658    /// `Some("") → fallback` collapse the raw [`Self::repositorio`]
659    /// accessor's docstring explicitly rejects on the sibling raw
660    /// accessor).
661    ///
662    /// Peer of the sibling per-`&Caixa`-axis composed helpers
663    /// [`caixa-flux::cluster_bundle_for_caixa`] (06d52d7) on the sibling
664    /// substrate-side renderer surface — same "close the composed
665    /// substrate-primitive at one canonical arm on the single-`&Caixa`
666    /// dispatch, converge every prior open-coded caller onto the arm"
667    /// discipline extended onto the resolved-git-URL projection of the
668    /// per-`Caixa` `:repositorio` axis. Owns per-call [`String`]
669    /// allocation on both arms (the `Some` arm's `str::to_owned` and the
670    /// `None` arm's `format!`) — the by-value return matches every
671    /// downstream consumer's field-fill shape (the caixa-flux
672    /// `ClusterBundleOpts::git_url: String` field, every future
673    /// `Chart.yaml` `home:` fold's `Option<String>` field-fill on the
674    /// `Some` arm).
675    #[must_use]
676    pub fn canonical_git_url(&self) -> String {
677        self.repositorio().map_or_else(
678            || {
679                format!(
680                    "https://github.com/{org}/{nome}",
681                    org = crate::DEFAULT_PLEME_GIT_ORG,
682                    nome = self.nome(),
683                )
684            },
685            str::to_owned,
686        )
687    }
688
689    /// Substrate-canonical per-`Caixa` **resolved-publish-tag** composer —
690    /// returns the caixa's canonical Zig-style git-publish-tag as an owned
691    /// [`String`], derived by concatenating
692    /// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] with the typed
693    /// [`Self::versao`] byte-string on a single `format!` template.
694    /// Every substrate-side consumer that resolves "which git tag does this
695    /// caixa publish under?" reaches for exactly one typed dispatch on the
696    /// substrate primitive — the raw `format!("{prefix}{versao}", prefix =
697    /// caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao = caixa.versao())`
698    /// open-coded composition every prior caller re-derived collapses onto
699    /// one canonical arm.
700    ///
701    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
702    /// git-URL composer on the paired per-`Caixa` git-remote axis — same
703    /// "close the composed substrate-primitive at one canonical arm on the
704    /// single-`&Caixa` dispatch, converge every prior open-coded caller
705    /// onto the arm" discipline extended from the resolved-URL projection
706    /// of the per-`Caixa` `:repositorio` axis onto the resolved-tag
707    /// projection of the per-`Caixa` `:versao` axis. The two accessors
708    /// jointly close the pair of scalars every `FluxCD` `GitRepository` CR
709    /// keys off (`spec.url` via [`Self::canonical_git_url`],
710    /// `spec.ref.tag` via [`Self::publish_tag`]) at the substrate primitive
711    /// — a downstream consumer that reaches through both accessors reads
712    /// the complete published-git-identity of a caixa through two typed
713    /// dispatches, not four open-coded field accesses.
714    ///
715    /// The reader-side (`caixa-flux::cluster_bundle` /
716    /// `ClusterBundleOpts::for_caixa`'s `git_ref` field, every future
717    /// per-cluster snapshot bundle emitter, the future M4
718    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's tag-carrier
719    /// slot on the tatara `Process` intent) always resolves the tag under
720    /// the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] prefix — this
721    /// method encodes that reader-side convention. The writer-side
722    /// (`caixa-feira`'s `feira publish` `--prefix` clap flag) allows the
723    /// operator to override the prefix at publish time; the two surfaces
724    /// intentionally sit on the "canonical default + operator override"
725    /// pair the sibling [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] constant's
726    /// own docstring documents — a `feira publish --prefix release/`
727    /// override is the operator's explicit opt-out from the substrate
728    /// default, not a supported drift axis.
729    ///
730    /// The composition body is the exact byte-image of the prior inline
731    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_ref` composer at
732    /// caixa-flux/src/lib.rs:2105 — pinned by the sibling caixa-flux
733    /// byte-parity test
734    /// `cluster_bundle_opts_for_caixa_git_ref_routes_through_publish_tag_accessor`
735    /// against a future implementation of this method that reordered the
736    /// `format!` template arguments, migrated the `<prefix>` segment to a
737    /// different constant (the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] axis
738    /// a future Zig-style-tag rebrand may split off — the constant's own
739    /// docstring anticipates a substrate-side move to `release/<versao>`
740    /// or bare `<versao>` shapes once a sibling forge convention adopts a
741    /// slash-namespaced or bare-scalar form), interposed a canonicalization
742    /// pass on the `:versao` axis (a SemVer-2 build-metadata strip an OCI-
743    /// tag normalizer might apply once the M4 registry-alignment slot
744    /// lands), or silently absorbed an empty `:versao` arm (which cannot
745    /// occur past the [`Self::validate_versao`] gate but which a
746    /// hypothetical bypass on the accessor path must not silently paper
747    /// over).
748    ///
749    /// Owns per-call [`String`] allocation via the single `format!`
750    /// invocation — the by-value return matches every downstream
751    /// consumer's field-fill shape (the caixa-flux `GitRefSpec::Tag(String)`
752    /// variant's owned payload, every future `intent.aplicacao.tag: String`
753    /// field-fill on the M4 CR materializer's tag-carrier slot).
754    #[must_use]
755    pub fn publish_tag(&self) -> String {
756        format!(
757            "{prefix}{versao}",
758            prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
759            versao = self.versao(),
760        )
761    }
762
763    /// Substrate-canonical per-`Caixa` **resolved-Helm-chart-name** composer
764    /// — returns the caixa's canonical `lareira-<nome>` per-Servico Helm
765    /// chart identity as an owned [`String`], derived by dispatching through
766    /// the substrate-canonical [`crate::lareira_chart_name`] helper against
767    /// the typed [`Self::nome`] byte-string. Every substrate-side consumer
768    /// that resolves "which Helm chart identity does this caixa render
769    /// under?" reaches for exactly one typed dispatch on the substrate
770    /// primitive — the raw `caixa_core::lareira_chart_name(caixa.nome())`
771    /// two-step compose every prior caller re-derived collapses onto one
772    /// canonical arm on the single-`&Caixa` dispatch.
773    ///
774    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
775    /// git-URL composer + [`Self::publish_tag`] (07e05b8) resolved-publish-
776    /// tag composer on the paired per-`Caixa` published-artifact-identity
777    /// axis — same "close the composed substrate-primitive at one canonical
778    /// arm on the single-`&Caixa` dispatch, converge every prior open-coded
779    /// caller onto the arm" discipline extended from the resolved-URL /
780    /// resolved-tag projections of the `:repositorio` / `:versao` axes onto
781    /// the resolved-chart-name projection of the `:nome` axis. The three
782    /// accessors jointly close the triple of scalars every per-Servico
783    /// deploy artifact keys off (git source URL via
784    /// [`Self::canonical_git_url`], git source tag via
785    /// [`Self::publish_tag`], per-Servico Helm chart identity via
786    /// [`Self::lareira_chart_name`]) at the substrate primitive — a
787    /// downstream consumer that reaches through all three reads the
788    /// complete deploy-artifact identity of a caixa through three typed
789    /// dispatches, not six open-coded compositions across three renderer
790    /// crates.
791    ///
792    /// The reader-side (three production sites at the time of the lift —
793    /// [`caixa-helm::render_chart_for_servico_with`]'s `ChartDir.name`
794    /// composer at caixa-helm/src/lib.rs:778, the peer
795    /// [`caixa-flux::cluster_bundle`]'s per-CR `chart_name` binding at
796    /// caixa-flux/src/lib.rs:2219, and
797    /// [`caixa-tatara::process_for_aplicacao`]'s `release_name`
798    /// composer at caixa-tatara/src/lib.rs:227, plus every future
799    /// per-Servico OCI publish emitter the CAIXA-SDLC §II
800    /// `caixa-publish.yml` reusable workflow's `skopeo push` step keys
801    /// off, the future per-cluster snapshot bundle emitter, the future
802    /// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
803    /// per-member chart-carrier slot on the tatara `Process` intent) —
804    /// always resolves the chart name under the canonical
805    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] prefix; this method encodes
806    /// that reader-side convention. The joint-length invariant the peer
807    /// [`Self::validate_nome_chart_name_budget`] gate enforces at
808    /// caixa-build time (author-declared `:nome` + fixed prefix ≤
809    /// [`crate::DNS_1123_LABEL_MAX_LEN`]) is verified on the input to
810    /// this composer by construction, so the produced `lareira-<nome>`
811    /// string is a valid Helm chart-name segment on every accept-set
812    /// input.
813    ///
814    /// The composition body is the exact byte-image of the prior inline
815    /// `caixa_core::lareira_chart_name(caixa.nome())` two-step form every
816    /// prior caller re-derived — pinned by the sibling caixa-helm /
817    /// caixa-flux / caixa-tatara byte-parity tests
818    /// `<crate>_lareira_chart_name_routes_through_caixa_accessor` against
819    /// a future implementation of this method that reordered the
820    /// composition arguments, migrated the `<prefix>` segment to a
821    /// different constant (the [`crate::LAREIRA_CHART_NAME_PREFIX`] axis a
822    /// future substrate-side chart-family rebrand may split off — the
823    /// constant's own docstring anticipates a substrate-side move once
824    /// the `lareira-` scoping intent outlives the family it names),
825    /// interposed a canonicalization pass on the `:nome` axis (a per-
826    /// registry namespace-qualification an M4 CR materializer might apply
827    /// per-CR — the "`pleme-io/checkout` vs `partner-org/checkout`
828    /// collision" arm the multi-tenant-registry story acknowledges), or
829    /// silently absorbed an empty `:nome` arm (which cannot occur past
830    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
831    /// on the accessor path must not silently paper over).
832    ///
833    /// Owns per-call [`String`] allocation via the single
834    /// [`crate::lareira_chart_name`] `format!` invocation — the by-value
835    /// return matches every downstream consumer's field-fill shape (the
836    /// caixa-helm `ChartDir.name: String` field, the caixa-flux per-CR
837    /// `chart_name: String` binding, the caixa-tatara
838    /// `AplicacaoIntent.release_name: Option<String>` field-fill on the
839    /// `Some` arm).
840    #[must_use]
841    pub fn lareira_chart_name(&self) -> String {
842        crate::lareira_chart_name(self.nome())
843    }
844
845    /// Substrate-canonical per-`Caixa` **resolved-OCI-chart-ref** composer
846    /// — returns the caixa's canonical `oci://<registry>/lareira-<nome>`
847    /// per-Servico Helm chart OCI artifact reference as an owned
848    /// [`String`], derived by dispatching through the substrate-canonical
849    /// [`crate::oci_chart_ref`] helper (which itself composes
850    /// [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied `registry` +
851    /// [`crate::lareira_chart_name`]-of-[`Self::nome`]) against the
852    /// caller-supplied `registry` and the typed [`Self::nome`] byte-string.
853    /// Every substrate-side consumer that resolves "which OCI chart
854    /// artifact does this caixa publish under, in this registry?" reaches
855    /// for exactly one typed dispatch on the substrate primitive — the raw
856    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step compose
857    /// every prior caller re-derived collapses onto one canonical arm on
858    /// the single-`(&Caixa, &str)` dispatch.
859    ///
860    /// Fourth member of the paired per-`Caixa` published-artifact-identity
861    /// axis alongside [`Self::canonical_git_url`] (124f864) /
862    /// [`Self::publish_tag`] (07e05b8) / [`Self::lareira_chart_name`]
863    /// (a8f0bee) — same "close the composed substrate-primitive at one
864    /// canonical arm on the single-`&Caixa` dispatch, converge every
865    /// prior open-coded caller onto the arm" discipline extended from the
866    /// resolved-URL / resolved-tag / resolved-chart-name projections of
867    /// the `:repositorio` / `:versao` / `:nome` axes onto the resolved-
868    /// OCI-ref projection over the paired `(registry, :nome)` inputs. The
869    /// four accessors jointly close the per-`Caixa` published-artifact-
870    /// identity surface every downstream consumer of a caixa's published
871    /// deploy artifacts keys off (git source URL via
872    /// [`Self::canonical_git_url`], git source tag via
873    /// [`Self::publish_tag`], per-Servico Helm chart identity via
874    /// [`Self::lareira_chart_name`], per-registry OCI chart artifact
875    /// reference via [`Self::oci_chart_ref`]) at the substrate primitive
876    /// — a downstream consumer that reaches through all four reads the
877    /// complete deploy-artifact identity of a caixa through four typed
878    /// dispatches, not eight open-coded compositions across four renderer
879    /// crates. The unique-signature dispatch (`(&Caixa, &str)` on this
880    /// method vs. `&Caixa` on the sibling three) reflects the extra input
881    /// axis this composer folds in: unlike the git-URL / git-tag / chart-
882    /// name axes (each derived purely from a `&Caixa`), the OCI-ref axis
883    /// pairs the caixa's per-`:nome` chart identity with the caller-
884    /// supplied per-registry authority segment, so the accessor threads
885    /// the registry byte-string through as a positional `&str`.
886    ///
887    /// The reader-side (one production site at the time of the lift —
888    /// [`caixa-tatara::process_for_aplicacao`]'s `derive_chart_ref` helper
889    /// at caixa-tatara/src/lib.rs:333 that composes the emitted
890    /// `AplicacaoIntent.chart_ref` scalar the tatara-reconciler feeds into
891    /// `helm install`, plus every future per-Servico OCI publish emitter
892    /// the CAIXA-SDLC §II `caixa-publish.yml` reusable workflow's
893    /// `skopeo push` step keys off, the future per-cluster snapshot bundle
894    /// emitter's per-CR `oci://…` field-fill on the M4 registry-alignment
895    /// slot, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
896    /// materializer's per-member `chart_ref` slot on the tatara `Process`
897    /// intent, the `FluxCD` `HelmRelease` `spec.chart.spec.chart` field-fill
898    /// on the OCI-source path an M4 per-cluster registry-rewrite overlay
899    /// applies per-CR) — always resolves the OCI ref under the canonical
900    /// [`crate::OCI_SCHEME_PREFIX`] scheme prefix + the canonical
901    /// [`Self::lareira_chart_name`] chart-name segment; this method
902    /// encodes that reader-side convention.
903    ///
904    /// The composition body is the exact byte-image of the prior inline
905    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step form
906    /// every prior caller re-derived — pinned by the sibling caixa-tatara
907    /// byte-parity test
908    /// `derive_chart_ref_routes_through_caixa_oci_chart_ref_accessor`
909    /// against a future implementation of this method that reordered the
910    /// composition arguments, migrated the `<scheme>` segment to a
911    /// different constant (the [`crate::OCI_SCHEME_PREFIX`] axis a future
912    /// substrate-side registry-protocol rebrand may split off — the
913    /// constant's own docstring anticipates a substrate-side move once
914    /// Helm 3 / `FluxCD` introduce a successor scheme past `oci://`),
915    /// migrated the `<chart>` segment off the paired
916    /// [`crate::lareira_chart_name`] composer (a per-registry
917    /// namespace-qualification an M4 CR materializer might apply per-CR),
918    /// interposed a canonicalization pass on the `registry` axis (an OCI-
919    /// authority normalization once the M4 registry-alignment slot lands),
920    /// or silently absorbed an empty `:nome` arm (which cannot occur past
921    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
922    /// on the accessor path must not silently paper over).
923    ///
924    /// Owns per-call [`String`] allocation via the single
925    /// [`crate::oci_chart_ref`] `format!` invocation — the by-value return
926    /// matches every downstream consumer's field-fill shape (the caixa-
927    /// tatara `AplicacaoIntent.chart_ref: String` field-fill, every
928    /// future `intent.aplicacao.chart_ref: String` field-fill on the M4
929    /// CR materializer's chart-ref-carrier slot, every future
930    /// `HelmRelease.spec.chart.spec.chart: String` field-fill on the OCI-
931    /// source path).
932    #[must_use]
933    pub fn oci_chart_ref(&self, registry: &str) -> String {
934        crate::oci_chart_ref(registry, self.nome())
935    }
936
937    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
938    /// chart-description scalar accessor every consumer of the top-level
939    /// manifest's Chart.yaml `description:` axis keys off — returns the
940    /// author-declared `:descricao` byte-string verbatim as an
941    /// `Option<&str>`, borrowed from the typed slot's own
942    /// `Option<String>` storage. `None` when the slot is absent (the
943    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
944    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
945    /// omitted slot through a `format!("Generated chart for caixa Servico
946    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
947    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
948    /// and [`caixa-feira`]'s `render_flake` folds it through a
949    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
950    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
951    ///
952    /// The `:descricao` slot carries the universal-axis free-form-prose
953    /// chart-description identifier every kind of caixa emits under
954    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
955    /// supplies) — the typed slot's `Option<String>` accept-set
956    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
957    /// chart-description-shape-invalid rejected through
958    /// [`ManifestError::DescricaoInvalid`] past the shared
959    /// [`crate::render::is_chart_description_shape`] predicate the peer
960    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
961    /// load-bearing downstream consumers:
962    ///
963    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
964    ///     gate binding — the universal-axis identity gate wired at
965    ///     caixa-build time.
966    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
967    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
968    ///     chart's `Chart.yaml` `description:` field, which
969    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
970    ///     `WARNING [chart.metadata.description]: description is required`
971    ///     when absent) and which every registry that ingests the chart
972    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
973    ///     chart's canonical one-line prose descriptor.
974    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
975    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
976    ///     header directly beneath the `# <chart-name>` title, which
977    ///     every author who inspects the rendered chart bundle lands at.
978    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
979    ///     top-level fold — the emitted `flake.nix`'s `description`
980    ///     field, which every Nix consumer (`nix flake show`,
981    ///     `nix flake metadata`, downstream flake-registry ingestors)
982    ///     surfaces as the flake's canonical descriptor.
983    ///
984    /// Prior to this lift the `.descricao` field was accessed inline at
985    /// four production sites — [`Self::validate_descricao`]'s
986    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
987    /// caixa-helm `build_chart_yaml`
988    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
989    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
990    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
991    /// `README.md` header fold, and the caixa-feira `render_flake`
992    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
993    /// `description = ""` fold — four open-coded field-accesses that
994    /// expressed no compile-time link back to the typed slot. A future
995    /// extension of the `:descricao` axis to a richer author surface —
996    /// a per-`:descricao` locale-tagged multi-language descriptor map
997    /// (the "one caixa, N language-tagged prose descriptions" arm
998    /// author-tooling internationalization anticipates), a
999    /// per-registry-target length-and-shape overlay the M4 CR
1000    /// materializer resolves per-CR (the "ArtifactHub caps description
1001    /// at 512 bytes but the internal registry caps at 256" arm), a
1002    /// promotion of the plain `Option<String>` byte-string to a richer
1003    /// `ChartDescription` newtype guaranteeing the
1004    /// `is_chart_description_shape` predicate at the type level — would
1005    /// have had to be threaded through all four open-coded copies in
1006    /// lockstep or the validate gate and the three emit paths would
1007    /// silently disagree on which prose string a given [`Caixa`]
1008    /// resolves to (an author's
1009    /// `:descricao "Checkout flow orchestration."` would satisfy
1010    /// validate while one of the emit paths silently rendered a stale
1011    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
1012    /// resolution to a typed method on the substrate primitive means
1013    /// every downstream consumer of the caixa's per-`Caixa`
1014    /// chart-description surface reaches for exactly one typed dispatch
1015    /// — the resolver's accept-set migrates as a unit on any future
1016    /// axis addition.
1017    ///
1018    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
1019    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
1020    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
1021    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
1022    /// lift folds on. Same "one typed dispatch on the substrate
1023    /// primitive, thin projections at each consumer" discipline the
1024    /// peer per-`:placement`
1025    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1026    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1027    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1028    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1029    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1030    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1031    /// typed-slot atom axes, extended here to the third outer top-level
1032    /// `Caixa` universal-axis surface. Named `descricao()` to match the
1033    /// storage field's name; the accessor's identity maps onto the
1034    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1035    /// carries. The one remaining universal `Option<String>` slot
1036    /// (`:edicao`) folds on this pattern next.
1037    #[must_use]
1038    pub const fn descricao(&self) -> Option<&str> {
1039        match &self.descricao {
1040            Some(s) => Some(s.as_str()),
1041            None => None,
1042        }
1043    }
1044
1045    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
1046    /// accessor every consumer of the top-level manifest's tatara-lisp
1047    /// edition-selector axis keys off — returns the author-declared
1048    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
1049    /// the typed slot's own `Option<String>` storage. `None` when the
1050    /// slot is absent (the canonical "omit the slot to defer to the
1051    /// substrate's default edition" shape every existing
1052    /// [`caixa-resolver`] integration test fixture carries via
1053    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
1054    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
1055    /// arm by construction, so an author-omitted `:edicao` round-trips
1056    /// to a build without triggering the year-shape predicate).
1057    ///
1058    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
1059    /// decimal-year language-edition identifier every kind of caixa
1060    /// emits under (CAIXA-SDLC §I — the author-facing surface every
1061    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
1062    /// accept-set (empty-string rejected through
1063    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
1064    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
1065    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
1066    /// onto one load-bearing downstream consumer today
1067    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
1068    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
1069    /// future edition-aware substrate consumer the CAIXA-SDLC §I
1070    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
1071    /// selector every edition-aware build step keys off, the future
1072    /// per-edition compatibility-flag overlay the M4 CR materializer
1073    /// resolves per-CR, the peer [`Caixa::template`] canonical
1074    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
1075    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
1076    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
1077    /// carry `edicao: Some("2026".into())` by construction).
1078    ///
1079    /// Prior to this lift the `.edicao` field was accessed inline at
1080    /// one production site — [`Self::validate_edicao`]'s
1081    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
1082    /// open-coded field-access that expressed no compile-time link
1083    /// back to the typed slot. A future extension of the `:edicao`
1084    /// axis to a richer author surface — a per-`:edicao` known-
1085    /// edition allowlist (the future tightening
1086    /// [`Self::validate_edicao`]'s docstring acknowledges past the
1087    /// structural year-shape floor, rejecting year-shaped values that
1088    /// don't name a tatara-lisp edition the substrate actually
1089    /// understands — `"1999"` is year-shaped but no `1999` edition
1090    /// exists), a per-edition compatibility-flag overlay the M4 CR
1091    /// materializer resolves per-CR (the "edition `"2026"` enables
1092    /// macro-surface features the sibling `"2018"` gates behind a
1093    /// feature flag" arm the edition-selector story anticipates), a
1094    /// promotion of the plain `Option<String>` byte-string to a
1095    /// richer `CaixaEdition` enum discriminated on year once a sibling
1096    /// edition to `"2026"` lands — would have had to be threaded
1097    /// through the open-coded copy in lockstep with every future
1098    /// edition-aware consumer, or the validate gate and the future
1099    /// edition-aware consumer path would silently disagree on which
1100    /// edition a given [`Caixa`] resolves to (an author's
1101    /// `:edicao "2026"` would satisfy validate while a future
1102    /// edition-aware consumer silently defaulted to a stale edition,
1103    /// or vice versa). Lifting the resolution to a typed method on
1104    /// the substrate primitive means every downstream consumer of the
1105    /// caixa's per-`Caixa` edition surface reaches for exactly one
1106    /// typed dispatch — the resolver's accept-set migrates as a unit
1107    /// on any future axis addition.
1108    ///
1109    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
1110    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
1111    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
1112    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
1113    /// `Option<&str>` scalar" projection pattern this lift folds on.
1114    /// Same "one typed dispatch on the substrate primitive, thin
1115    /// projections at each consumer" discipline the peer per-`:placement`
1116    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1117    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1118    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1119    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1120    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1121    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1122    /// typed-slot atom axes, extended here to close the outer top-level
1123    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
1124    /// slot. Named `edicao()` to match the storage field's name; the
1125    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1126    /// vocabulary the slot's docstring already carries.
1127    #[must_use]
1128    pub const fn edicao(&self) -> Option<&str> {
1129        match &self.edicao {
1130            Some(s) => Some(s.as_str()),
1131            None => None,
1132        }
1133    }
1134
1135    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
1136    /// label caixa-identity scalar accessor every consumer of the top-
1137    /// level manifest's identity axis keys off — returns the author-
1138    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
1139    /// the typed slot's own `String` storage. Non-optional (`:nome` is
1140    /// a required-axis scalar every `defcaixa` form must supply; the
1141    /// [`Self::from_lisp`] derive rejects an omitted / non-string
1142    /// `:nome` at parse time, so a `Caixa` past parse definitionally
1143    /// carries a non-`None` `:nome`).
1144    ///
1145    /// The `:nome` slot carries the universal-axis DNS-1123-label
1146    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
1147    /// the primary identity axis every `defcaixa` form supplies
1148    /// alongside `:versao` / `:kind`; the substrate-wide identity every
1149    /// other typed surface that names a caixa reaches through — `:deps`
1150    /// entries, `:membros` entries, `:children` entries, the
1151    /// `lareira-<nome>` Helm chart name every per-Servico renderer
1152    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
1153    /// renderer emits) — the typed slot's `String` accept-set (empty
1154    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
1155    /// invalid rejected through [`ManifestError::NomeInvalid`] past
1156    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
1157    /// the peer name axes each land on, joint-length-with-`lareira-`-
1158    /// prefix rejected through
1159    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
1160    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
1161    /// load-bearing downstream consumer the substrate carries — the
1162    /// two universal-axis validate gates at caixa-build time
1163    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
1164    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
1165    /// derivation every per-Servico renderer keys off, the caixa-helm
1166    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
1167    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
1168    /// `HTTPRoute` per-Aplicacao name axes at
1169    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
1170    /// [`crate::pleme_program_selector`] /
1171    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
1172    /// derivations, and every future substrate renderer that emits an
1173    /// artifact keyed by the caixa's identity.
1174    ///
1175    /// Prior to this lift the `.nome` field was accessed inline at a
1176    /// dozen production sites across `caixa-core` (the two universal-
1177    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
1178    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
1179    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
1180    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
1181    /// entry `name:` fold, the `flux_kustomization_source_subtree`
1182    /// per-cluster subpath derivation), and `caixa-mesh` (the
1183    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
1184    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
1185    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
1186    /// insert) — a dozen open-coded field-accesses that expressed no
1187    /// compile-time link back to the typed slot. A future extension of
1188    /// the `:nome` axis to a richer author surface — a per-`:nome`
1189    /// structured `CaixaIdentity` newtype that carries the joint-
1190    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
1191    /// enforces at the type level (rather than as a validate-time
1192    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
1193    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
1194    /// `partner-org/checkout` collision" arm the multi-tenant-registry
1195    /// story acknowledges), a promotion of the plain `String` byte-
1196    /// string to a richer `CaixaNome` newtype discriminated on
1197    /// namespace prefix — would have had to be threaded through every
1198    /// open-coded copy in lockstep or the two validate gates and the
1199    /// dozen emit paths would silently disagree on which identity a
1200    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
1201    /// would satisfy validate while one of the emit paths silently
1202    /// rendered a drifted other identity, or vice versa). Lifting the
1203    /// resolution to a typed method on the substrate primitive means
1204    /// every downstream consumer of the caixa's per-`Caixa` identity
1205    /// surface reaches for exactly one typed dispatch — the resolver's
1206    /// accept-set migrates as a unit on any future axis addition.
1207    ///
1208    /// First outer top-level [`Caixa`] `&str`-return required-scalar
1209    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
1210    /// projection pattern the sibling per-`Caixa` `:versao` future lift
1211    /// folds on. Sibling in shape to the peer per-`:membros`
1212    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
1213    /// [`crate::aplicacao::WitContract::source`] /
1214    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
1215    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
1216    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
1217    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
1218    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
1219    /// per-sub-struct required-axis accessors carry on the sibling M3
1220    /// mesh-slot-atom scalar-value axes, extended here to open the
1221    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
1222    /// Named `nome()` to match the storage field's name; the accessor's
1223    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1224    /// slot's docstring already carries.
1225    #[must_use]
1226    pub const fn nome(&self) -> &str {
1227        self.nome.as_str()
1228    }
1229
1230    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
1231    /// pinned-version scalar accessor every consumer of the top-level
1232    /// manifest's version axis keys off — returns the author-declared
1233    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
1234    /// typed slot's own `String` storage. Non-optional (`:versao` is a
1235    /// required-axis scalar every `defcaixa` form must supply alongside
1236    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
1237    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
1238    /// parse definitionally carries a non-`None` `:versao`).
1239    ///
1240    /// The `:versao` slot carries the universal-axis SemVer-2
1241    /// concrete-version body every kind of caixa emits under
1242    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
1243    /// supplies alongside `:nome` / `:kind`; the substrate-wide
1244    /// pinned-version every downstream artifact-emitting consumer
1245    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
1246    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
1247    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
1248    /// prefix composes on top of, the programs.yaml entry's `versao:`
1249    /// value the `lareira-fleet-programs` aggregator carries onto each
1250    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
1251    /// tags every substrate-side `skopeo push` writes, the lacre
1252    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
1253    /// prior-version references peers in the exact same SemVer-2 shape).
1254    /// The typed slot's `String` accept-set (empty rejected through
1255    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
1256    /// through [`ManifestError::VersaoInvalid`] past
1257    /// [`semver::Version::parse`]) maps onto every load-bearing
1258    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
1259    /// universal-axis validate gate at caixa-build time, the
1260    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
1261    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
1262    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
1263    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
1264    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
1265    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
1266    /// tag derivation (`format!("{prefix}{versao}")`), and every future
1267    /// substrate renderer that emits an artifact keyed by the caixa's
1268    /// pinned version.
1269    ///
1270    /// Prior to this lift the `.versao` field was accessed inline at a
1271    /// dozen production sites across `caixa-core` (the universal-axis
1272    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
1273    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
1274    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
1275    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
1276    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
1277    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
1278    /// (the `feira publish` git-tag derivation + the `feira app graph` /
1279    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
1280    /// field-accesses that expressed no compile-time link back to the
1281    /// typed slot. A future extension of the `:versao` axis to a richer
1282    /// author surface — a per-`:versao` structured `CaixaVersion` at the
1283    /// storage layer (the substrate already carries a `CaixaVersion`
1284    /// newtype at [`crate::version::CaixaVersion`], deferred until the
1285    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
1286    /// a per-registry `:versao` immutability overlay the M4 CR
1287    /// materializer enforces per-CR, a promotion of the plain `String`
1288    /// byte-string to a richer `PinnedVersao` newtype discriminated on
1289    /// SemVer-2 pre-release / build-metadata presence — would have had
1290    /// to be threaded through every open-coded copy in lockstep or the
1291    /// validate gate and the dozen emit paths would silently disagree
1292    /// on which version a given [`Caixa`] resolves to (an author's
1293    /// `:versao "0.1.0"` would satisfy validate while one of the emit
1294    /// paths silently rendered a drifted other version, or vice versa).
1295    /// Lifting the resolution to a typed method on the substrate
1296    /// primitive means every downstream consumer of the caixa's
1297    /// per-`Caixa` pinned-version surface reaches for exactly one typed
1298    /// dispatch — the resolver's accept-set migrates as a unit on any
1299    /// future axis addition.
1300    ///
1301    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
1302    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
1303    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
1304    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
1305    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
1306    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
1307    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
1308    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
1309    /// on the sibling per-typed-slot version-carrier axes, extended here
1310    /// to close the second outer top-level [`Caixa`] required-`&str`-
1311    /// carrying axis so the two universal-axis identity-carrying
1312    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
1313    /// share the same "one typed dispatch per axis" discipline. Named
1314    /// `versao()` to match the storage field's name; the accessor's
1315    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1316    /// slot's docstring already carries.
1317    #[must_use]
1318    pub const fn versao(&self) -> &str {
1319        self.versao.as_str()
1320    }
1321
1322    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
1323    /// closed-set-enum discriminant accessor every consumer of the top-
1324    /// level manifest's kind axis keys off — returns the author-declared
1325    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
1326    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
1327    /// (`:kind` is a required-axis discriminant every `defcaixa` form
1328    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
1329    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
1330    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
1331    /// variant).
1332    ///
1333    /// The `:kind` slot carries the universal-axis closed-set typed-
1334    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
1335    /// §I — the primary shape gate every renderer / verifier /
1336    /// operator branches on; the five variants `Biblioteca` /
1337    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1338    /// the caixa surface into disjoint runtime contracts) — the typed
1339    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1340    /// values through the derive-macro's symbol-arm gate, exhaustively
1341    /// matched at every downstream dispatch site) maps onto every
1342    /// load-bearing downstream consumer the substrate carries:
1343    ///
1344    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1345    ///     predicate — the canonical two-line
1346    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1347    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1348    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1349    ///     ComputeUnit` CR materializer) runs at its entry-point,
1350    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1351    ///     `actual:` field the diagnostic surfaces to name the offending
1352    ///     caixa's variant.
1353    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1354    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1355    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1356    ///     slot columns into their typed sub-spec only when the kind
1357    ///     matches (returns `None` otherwise); the future per-Servico
1358    ///     M2-view composer (`servico_view`) will follow the same shape.
1359    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1360    ///     coherence gate — the `!self.kind.requires_exe()` /
1361    ///     `!self.kind.requires_servicos()` predicates that fence
1362    ///     each code-surface slot from the wrong owning kind.
1363    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1364    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1365    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1366    ///     coherence error carriers (`SupervisorOwnsCode` /
1367    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1368    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1369    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1370    ///     variant in their `kind:` field.
1371    ///
1372    /// Prior to this lift the `.kind` field was accessed inline at
1373    /// twenty-plus production sites across `caixa-core` (the
1374    /// [`crate::render::require_kind`] entry-gate predicate + the
1375    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1376    /// composers, the `declared_foreign_code_slots` per-slot kind-
1377    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1378    /// kind ↔ code-surface predicates + four error carriers) — a score
1379    /// of open-coded field-accesses that expressed no compile-time link
1380    /// back to the typed slot. A future extension of the `:kind` axis
1381    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1382    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1383    /// variant across the wasm-component / legacy-container / native-
1384    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1385    /// kind-overlay the M4 CR materializer resolves per-CR (the
1386    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1387    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1388    /// enum to a richer `KindWithRuntime` discriminated on the
1389    /// component-model world axis — would have had to be threaded
1390    /// through every open-coded copy in lockstep or the entry gate,
1391    /// the view composers, and the layout invariants would silently
1392    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1393    /// the resolution to a typed method on the substrate primitive
1394    /// means every downstream consumer of the caixa's per-`Caixa`
1395    /// kind surface reaches for exactly one typed dispatch — the
1396    /// resolver's accept-set migrates as a unit on any future axis
1397    /// addition.
1398    ///
1399    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1400    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1401    /// required-discriminant" projection pattern. Sibling in shape to
1402    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1403    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1404    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1405    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1406    /// on the sibling nested-spec typed-slot discriminator axes,
1407    /// extended here to the outer top-level [`Caixa`] universal-axis
1408    /// surface. Named `kind()` to match the storage field's name;
1409    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1410    /// vocabulary the slot's docstring already carries.
1411    #[must_use]
1412    pub const fn kind(&self) -> CaixaKind {
1413        self.kind
1414    }
1415
1416    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1417    /// maintainer-name-list slice-accessor every consumer of the top-
1418    /// level manifest's maintainer axis keys off — returns the author-
1419    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1420    /// the same backing buffer the raw `self.autores.as_slice()` field
1421    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1422    /// empty axis every `defcaixa` form supplies with an empty `()` when
1423    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1424    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1425    /// parse definitionally carries a `Vec<String>` slot — possibly
1426    /// empty — and the returned `&[String]` degenerates to an empty
1427    /// slice on that arm without any silent `None` collapse).
1428    ///
1429    /// The `:autores` slot carries the universal-axis maintainer-name
1430    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1431    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1432    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1433    /// every downstream registry-facing artifact emits under) — the
1434    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1435    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1436    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1437    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1438    /// onto every load-bearing downstream consumer the substrate carries
1439    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1440    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1441    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1442    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1443    /// name, email: None }` record, every future per-`Caixa` registry-
1444    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1445    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1446    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1447    /// the future per-cluster author-notification overlay the M4 CR
1448    /// materializer resolves per-CR).
1449    ///
1450    /// Prior to this lift the `.autores` field was accessed inline at
1451    /// two production sites — [`Self::validate_autores`]'s `for autor
1452    /// in &self.autores` walk that gates every entry through
1453    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1454    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1455    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1456    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1457    /// two open-coded field-accesses that expressed no compile-time link
1458    /// back to the typed slot. A future extension of the `:autores` axis
1459    /// to a richer author surface — a per-`:autores` structured
1460    /// `Maintainer { name, email, url }` at the storage layer once the
1461    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1462    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1463    /// enforces per-CR (the "cluster policy demands every author declare
1464    /// an on-file `mailto:` contact" arm), a promotion of the plain
1465    /// `Vec<String>` byte-string list to a richer
1466    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1467    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1468    /// predicate already resolves through — would have had to be
1469    /// threaded through both open-coded copies in lockstep or the
1470    /// validate gate and the caixa-helm emit path would silently
1471    /// disagree on which authors a given [`Caixa`] resolves to (an
1472    /// author's `:autores ("alice" "bob")` would satisfy validate while
1473    /// the caixa-helm emit path silently rendered a drifted other
1474    /// maintainer list, or vice versa). Lifting the resolution to a
1475    /// typed method on the substrate primitive means every downstream
1476    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1477    /// for exactly one typed dispatch — the resolver's accept-set
1478    /// migrates as a unit on any future axis addition.
1479    ///
1480    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1481    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1482    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1483    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1484    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1485    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1486    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1487    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1488    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1489    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1490    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1491    /// per-M3 typed-slot list axes, extended here to the outer top-level
1492    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1493    /// `&Vec<String>`) because every downstream consumer of the author
1494    /// list treats it as a read-only sequence — the slice-view is the
1495    /// narrowest borrow that supports every present + roadmapped consumer
1496    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1497    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1498    /// reaches for (the storage-side `Vec` remains reachable through the
1499    /// `pub autores` field for the mutation-carrying serde round-trip and
1500    /// per-test fixture-mutation paths). Named `autores()` to match the
1501    /// storage field's name; the accessor's identity maps onto the
1502    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1503    /// carries.
1504    #[must_use]
1505    pub const fn autores(&self) -> &[String] {
1506        self.autores.as_slice()
1507    }
1508
1509    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1510    /// registry-search-tag-list slice-accessor every consumer of the
1511    /// top-level manifest's topical-tag axis keys off — returns the
1512    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1513    /// slice-view over the same backing buffer the raw
1514    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1515    /// list-carrying (`:etiquetas` is a default-empty axis every
1516    /// `defcaixa` form supplies with an empty `()` when unset; the
1517    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1518    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1519    /// definitionally carries a `Vec<String>` slot — possibly empty —
1520    /// and the returned `&[String]` degenerates to an empty slice on
1521    /// that arm without any silent `None` collapse).
1522    ///
1523    /// The `:etiquetas` slot carries the universal-axis topical-tag
1524    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1525    /// author-facing surface every `defcaixa` form supplies alongside
1526    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1527    /// search-facing axis every downstream registry-facing artifact
1528    /// emits under) — the typed slot's `Vec<String>` accept-set
1529    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1530    /// non-chart-keyword-shape rejected through
1531    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1532    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1533    /// every load-bearing downstream consumer the substrate carries —
1534    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1535    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1536    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1537    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1538    /// `Chart.yaml` `keywords:` array (chained with the
1539    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1540    /// dedup'd through a `BTreeSet` at emit time), every future per-
1541    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1542    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1543    /// annotation, the future per-cluster tag-notification overlay the
1544    /// M4 CR materializer resolves per-CR).
1545    ///
1546    /// Prior to this lift the `.etiquetas` field was accessed inline at
1547    /// two production sites — [`Self::validate_etiquetas`]'s `for
1548    /// etiqueta in &self.etiquetas` walk that gates every entry through
1549    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1550    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1551    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1552    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1553    /// two open-coded field-accesses that expressed no compile-time
1554    /// link back to the typed slot. A future extension of the
1555    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1556    /// structured `ChartKeyword { name, uri, category }` at the storage
1557    /// layer once the substrate absorbs `artifacthub.io/keywords`
1558    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1559    /// CR materializer enforces per-CR (the "cluster policy demands
1560    /// every tag come from a substrate-approved taxonomy" arm), a
1561    /// promotion of the plain `Vec<String>` byte-string list to a
1562    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1563    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1564    /// already resolves through — would have had to be threaded through
1565    /// both open-coded copies in lockstep or the validate gate and the
1566    /// caixa-helm emit path would silently disagree on which tags a
1567    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1568    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1569    /// path silently rendered a drifted other keyword list, or vice
1570    /// versa). Lifting the resolution to a typed method on the
1571    /// substrate primitive means every downstream consumer of the
1572    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1573    /// typed dispatch — the resolver's accept-set migrates as a unit
1574    /// on any future axis addition.
1575    ///
1576    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1577    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1578    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1579    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1580    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1581    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1582    /// fold onto the same pattern in future lifts. Sibling in shape to
1583    /// the peer per-`:supervisor`
1584    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1585    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1586    /// (a6e18d7), per-`:membros`
1587    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1588    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1589    /// (0dcc926), and per-`:upgrade-from :instructions`
1590    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1591    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1592    /// typed-slot list axes, extended here to the outer top-level
1593    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1594    /// `&Vec<String>`) because every downstream consumer of the tag
1595    /// list treats it as a read-only sequence — the slice-view is the
1596    /// narrowest borrow that supports every present + roadmapped
1597    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1598    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1599    /// the typed view reaches for (the storage-side `Vec` remains
1600    /// reachable through the `pub etiquetas` field for the mutation-
1601    /// carrying serde round-trip and per-test fixture-mutation paths).
1602    /// Named `etiquetas()` to match the storage field's name; the
1603    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1604    /// vocabulary the slot's docstring already carries.
1605    #[must_use]
1606    pub const fn etiquetas(&self) -> &[String] {
1607        self.etiquetas.as_slice()
1608    }
1609
1610    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1611    /// library-source-path-list slice-accessor every consumer of the
1612    /// top-level manifest's Biblioteca-source axis keys off — returns
1613    /// the author-declared `:bibliotecas` list verbatim as a
1614    /// `&[String]` slice-view over the same backing buffer the raw
1615    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1616    /// list-carrying (`:bibliotecas` is a default-empty axis every
1617    /// `defcaixa` form supplies with an empty `()` when unset; the
1618    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1619    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1620    /// parse definitionally carries a `Vec<String>` slot — possibly
1621    /// empty — and the returned `&[String]` degenerates to an empty
1622    /// slice on that arm without any silent `None` collapse).
1623    ///
1624    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1625    /// entry-path list every `:kind Biblioteca` caixa emits under
1626    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1627    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1628    /// substrate-wide library-carrier axis every downstream
1629    /// authoring-facing consumer keys off) — the typed slot's
1630    /// `Vec<String>` accept-set (empty-per-entry rejected through
1631    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1632    /// non-sandboxed-relative-shape rejected through
1633    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1634    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1635    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1636    /// maps onto every load-bearing downstream consumer the substrate
1637    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1638    /// empty-check + per-entry file-exists loop at
1639    /// caixa-core/src/layout.rs that gates each entry through
1640    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1641    /// [`Self::validate_code_paths`] per-slot shape gate at
1642    /// caixa-core/src/manifest.rs that walks each entry through the
1643    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1644    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1645    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1646    /// declared library file for lexical / structural errors before
1647    /// downstream `importar` resolution, every future per-`Caixa`
1648    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1649    /// (the future `tatara-lispc` compilation entry the docstring at
1650    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1651    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1652    /// the future `caixa-lsp` per-library semantic-token stream the
1653    /// caixa-lsp docstring roadmaps).
1654    ///
1655    /// Prior to this lift the `.bibliotecas` field was accessed inline
1656    /// at three production sites — [`crate::LayoutInvariants`]'s
1657    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1658    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1659    /// declared library path through the on-disk-existence check,
1660    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1661    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1662    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1663    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1664    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1665    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1666    /// coded field-accesses that expressed no compile-time link back
1667    /// to the typed slot. A future extension of the `:bibliotecas`
1668    /// axis to a richer library surface — a per-`:bibliotecas`
1669    /// structured `BibliotecaEntry { path, edition, exports }` at the
1670    /// storage layer once the substrate absorbs the per-library
1671    /// language-edition + explicit-exports tuple the tatara-lisp
1672    /// module-system roadmap acknowledges, a per-registry
1673    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1674    /// per-CR (the "cluster policy demands every biblioteca declare
1675    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1676    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1677    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1678    /// [`crate::render::is_sandboxed_relative_path`] +
1679    /// [`crate::render::is_lisp_extension`] predicates already resolve
1680    /// through — would have had to be threaded through all three
1681    /// open-coded copies in lockstep or the layout gate, the shape
1682    /// validator, and the `feira build` phase-1 parse walk would
1683    /// silently disagree on which library paths a given [`Caixa`]
1684    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1685    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1686    /// silently parsed a drifted other list, or vice versa). Lifting
1687    /// the resolution to a typed method on the substrate primitive
1688    /// means every downstream consumer of the caixa's per-`Caixa`
1689    /// library-source surface reaches for exactly one typed dispatch
1690    /// — the resolver's accept-set migrates as a unit on any future
1691    /// axis addition.
1692    ///
1693    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1694    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1695    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1696    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1697    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1698    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1699    /// `:children` / `:membros` / `:contratos`) fold onto the same
1700    /// pattern in future lifts. Sibling in shape to the peer
1701    /// per-`:supervisor`
1702    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1703    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1704    /// (a6e18d7), per-`:membros`
1705    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1706    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1707    /// (0dcc926), and per-`:upgrade-from :instructions`
1708    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1709    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1710    /// typed-slot list axes, extended here to the outer top-level
1711    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1712    /// `&Vec<String>`) because every downstream consumer of the
1713    /// library-source list treats it as a read-only sequence — the
1714    /// slice-view is the narrowest borrow that supports every
1715    /// present + roadmapped consumer (`.iter()`, `.len()`,
1716    /// `.is_empty()`) without leaking the backing `Vec`'s
1717    /// grow/push/reserve surface no consumer of the typed view
1718    /// reaches for (the storage-side `Vec` remains reachable through
1719    /// the `pub bibliotecas` field for the mutation-carrying serde
1720    /// round-trip and per-test fixture-mutation paths). Named
1721    /// `bibliotecas()` to match the storage field's name; the
1722    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1723    /// vocabulary the slot's docstring already carries.
1724    #[must_use]
1725    pub const fn bibliotecas(&self) -> &[String] {
1726        self.bibliotecas.as_slice()
1727    }
1728
1729    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1730    /// nix-built-executable-entry-path-list slice-accessor every consumer
1731    /// of the top-level manifest's Binario-executable axis keys off —
1732    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1733    /// slice-view over the same backing buffer the raw
1734    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1735    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1736    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1737    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1738    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1739    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1740    /// degenerates to an empty slice on that arm without any silent
1741    /// `None` collapse).
1742    ///
1743    /// The `:exe` slot carries the universal-axis nix-built executable
1744    /// entry-path list every `:kind Binario` caixa emits under
1745    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1746    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1747    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1748    /// downstream flake-build-facing consumer keys off) — the typed
1749    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1750    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1751    /// non-sandboxed-relative-shape rejected through
1752    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1753    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1754    /// directory paths rejected past the layout's
1755    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1756    /// onto every load-bearing downstream consumer the substrate carries
1757    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1758    /// per-entry file-exists + `exe/`-directory-fence loop at
1759    /// caixa-core/src/layout.rs that gates each entry through
1760    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1761    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1762    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1763    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1764    /// that fences code-surface slots off from the two no-code kinds,
1765    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1766    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1767    /// fences the `:exe` code surface off from every non-Binario code-
1768    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1769    /// that walks each entry through the sandbox-relative / cross-entry
1770    /// duplicate gates, every future per-`Caixa` executable-facing
1771    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1772    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1773    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1774    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1775    /// future `feira nix` per-executable Binario-target emit path).
1776    ///
1777    /// Prior to this lift the `.exe` field was accessed inline at three
1778    /// production sites — the compound-code-path `has_code =
1779    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1780    /// !caixa.servicos.is_empty()` OR-fold on the
1781    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1782    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1783    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1784    /// gate, the per-entry `for p in &caixa.exe`
1785    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1786    /// [`Self::declared_foreign_code_slots`]'s
1787    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1788    /// open-coded field-accesses that expressed no compile-time link
1789    /// back to the typed slot. A future extension of the `:exe` axis
1790    /// to a richer executable surface — a per-`:exe` structured
1791    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1792    /// layer once the substrate absorbs the per-executable
1793    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1794    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1795    /// the M4 CR materializer enforces per-CR (the "cluster policy
1796    /// demands every Binario declare an explicit `:wrapper`" arm), a
1797    /// promotion of the plain `Vec<String>` byte-string list to a
1798    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1799    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1800    /// fence already resolves through — would have had to be threaded
1801    /// through all four open-coded copies in lockstep or the layout
1802    /// gate, the shape validator, and the `feira nix` emit path would
1803    /// silently disagree on which executable paths a given [`Caixa`]
1804    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1805    /// satisfy layout while `feira nix` silently packaged a drifted
1806    /// other list, or vice versa). Lifting the resolution to a typed
1807    /// method on the substrate primitive means every downstream
1808    /// consumer of the caixa's per-`Caixa` executable-source surface
1809    /// reaches for exactly one typed dispatch — the resolver's accept-
1810    /// set migrates as a unit on any future axis addition.
1811    ///
1812    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1813    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1814    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1815    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1816    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1817    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1818    /// future lift closes onto (per the trio of code-surface list slots
1819    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1820    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1821    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1822    /// last unlifted code-surface slot). Sibling in shape to the peer
1823    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1824    /// (bc92bce), per-`:placement`
1825    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1826    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1827    /// (6c77e36), per-`:contratos`
1828    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1829    /// per-`:upgrade-from :instructions`
1830    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1831    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1832    /// typed-slot list axes, extended here to the outer top-level
1833    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1834    /// `&Vec<String>`) because every downstream consumer of the
1835    /// executable-source list treats it as a read-only sequence — the
1836    /// slice-view is the narrowest borrow that supports every
1837    /// present + roadmapped consumer (`.iter()`, `.len()`,
1838    /// `.is_empty()`) without leaking the backing `Vec`'s
1839    /// grow/push/reserve surface no consumer of the typed view
1840    /// reaches for (the storage-side `Vec` remains reachable through
1841    /// the `pub exe` field for the mutation-carrying serde
1842    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1843    /// to match the storage field's name; the accessor's identity
1844    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1845    /// docstring already carries.
1846    #[must_use]
1847    pub const fn exe(&self) -> &[String] {
1848        self.exe.as_slice()
1849    }
1850
1851    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1852    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1853    /// of the top-level manifest's Servico-component axis keys off —
1854    /// returns the author-declared `:servicos` list verbatim as a
1855    /// `&[String]` slice-view over the same backing buffer the raw
1856    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1857    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1858    /// form supplies with an empty `()` when unset; the
1859    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1860    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1861    /// definitionally carries a `Vec<String>` slot — possibly empty —
1862    /// and the returned `&[String]` degenerates to an empty slice on
1863    /// that arm without any silent `None` collapse).
1864    ///
1865    /// The `:servicos` slot carries the universal-axis
1866    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1867    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1868    /// author-facing surface every `defcaixa` form supplies alongside
1869    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1870    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1871    /// Servico-facing renderer keys off) — the typed slot's
1872    /// `Vec<String>` accept-set (empty-per-entry rejected through
1873    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1874    /// non-sandboxed-relative-shape rejected through
1875    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1876    /// extension rejected through
1877    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1878    /// entry duplicate rejected through
1879    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1880    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1881    /// renderer entry-points, out-of-`servicos/`-directory paths
1882    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1883    /// `starts_with` fence) maps onto every load-bearing downstream
1884    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1885    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1886    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1887    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1888    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1889    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1890    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1891    /// that fences code-surface slots off from the two no-code kinds,
1892    /// [`Self::declared_foreign_code_slots`]'s
1893    /// `!self.servicos.is_empty()` arm on the
1894    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1895    /// `:servicos` code surface off from every non-Servico code-running
1896    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1897    /// walks each entry through the sandbox-relative / `.computeunit.
1898    /// yaml`-extension / cross-entry duplicate gates, the
1899    /// [`crate::require_single_servico`] V0 singularity gate every
1900    /// per-Servico renderer entry-point runs through
1901    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1902    /// `feira deploy` per-verb `first_servico_path` walk at
1903    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1904    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1905    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1906    /// per-Servico OCI packager, the future M4
1907    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1908    /// per-Servico OTel collector-config emit).
1909    ///
1910    /// Prior to this lift the `.servicos` field was accessed inline at
1911    /// five production sites — the compound-code-path `has_code =
1912    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1913    /// !caixa.servicos.is_empty()` OR-fold on the
1914    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1915    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1916    /// `caixa.servicos.is_empty()`
1917    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1918    /// per-entry `for p in &caixa.servicos`
1919    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1920    /// [`Self::declared_foreign_code_slots`]'s
1921    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1922    /// and the [`crate::require_single_servico`] V0 count gate's
1923    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1924    /// projection (both the accept-arm predicate and the
1925    /// diagnostic-carrying `ServicoCountMismatch { count }`
1926    /// projection) — five open-coded field-accesses across three
1927    /// crates that expressed no compile-time link back to the typed
1928    /// slot. A future extension of the `:servicos` axis to a richer
1929    /// component surface — a per-`:servicos` structured
1930    /// `ServicoEntry { path, world, capabilities }` at the storage
1931    /// layer once the substrate absorbs the per-component WIT-world +
1932    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1933    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1934    /// materializer enforces per-CR (the "cluster policy demands every
1935    /// Servico declare an explicit `:world`" arm), a promotion of the
1936    /// plain `Vec<String>` byte-string list to a richer
1937    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1938    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1939    /// `starts_with(servicos_dir)` fence and the
1940    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1941    /// already resolve through, a promotion of the V0 singleton
1942    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1943    /// component-model multi-world boundary — would have had to be
1944    /// threaded through all five open-coded copies in lockstep or the
1945    /// layout gate, the shape validator, the V0 count gate, and the
1946    /// `feira chart` / `feira deploy` entry-point walks would silently
1947    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1948    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1949    /// yaml")` would satisfy layout while `feira chart` silently
1950    /// packaged a drifted other list, or vice versa). Lifting the
1951    /// resolution to a typed method on the substrate primitive means
1952    /// every downstream consumer of the caixa's per-`Caixa`
1953    /// ComputeUnit-CR-source surface reaches for exactly one typed
1954    /// dispatch — the resolver's accept-set migrates as a unit on any
1955    /// future axis addition.
1956    ///
1957    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1958    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1959    /// projection pattern [`Self::autores`] (b5d813f) opened,
1960    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1961    /// (8a36c23) closed the universal-axis text-tag family of, and
1962    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1963    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1964    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1965    /// a substrate-canonical slice accessor, the trio of code-surface
1966    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1967    /// tuple carries is complete on the typed dispatch surface (the
1968    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1969    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1970    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1971    /// per-element accessor swap in isolation — a future companion lift
1972    /// promotes the tuple's element type to `&[String]` and threads the
1973    /// triple of typed dispatches through as a unit). Sibling in shape
1974    /// to the peer per-`:supervisor`
1975    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1976    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1977    /// (a6e18d7), per-`:membros`
1978    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1979    /// per-`:contratos`
1980    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1981    /// per-`:upgrade-from :instructions`
1982    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1983    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1984    /// typed-slot list axes, extended here to the outer top-level
1985    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1986    /// `&Vec<String>`) because every downstream consumer of the
1987    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1988    /// the slice-view is the narrowest borrow that supports every
1989    /// present + roadmapped consumer (`.iter()`, `.len()`,
1990    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1991    /// grow/push/reserve surface no consumer of the typed view reaches
1992    /// for (the storage-side `Vec` remains reachable through the
1993    /// `pub servicos` field for the mutation-carrying serde round-trip
1994    /// and per-test fixture-mutation paths, and for the
1995    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1996    /// homogeneous-element-type shape carries the raw field access
1997    /// until the trio-closure lift promotes the tuple as a unit).
1998    /// Named `servicos()` to match the storage field's name; the
1999    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
2000    /// vocabulary the slot's docstring already carries.
2001    #[must_use]
2002    pub const fn servicos(&self) -> &[String] {
2003        self.servicos.as_slice()
2004    }
2005
2006    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
2007    /// runtime-dependency-declaration-list slice-accessor every consumer
2008    /// of the top-level manifest's runtime-dep-graph axis keys off —
2009    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
2010    /// slice-view over the same backing buffer the raw
2011    /// `self.deps.as_slice()` field access borrows from. Empty-list-
2012    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
2013    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
2014    /// derive folds an omitted `:deps` through `#[serde(default)]` to
2015    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
2016    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
2017    /// degenerates to an empty slice on that arm without any silent
2018    /// `None` collapse).
2019    ///
2020    /// The `:deps` slot carries the universal-axis runtime dependency
2021    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
2022    /// facing surface every `defcaixa` form supplies alongside `:nome` /
2023    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
2024    /// every downstream resolver-facing artifact emits under) — the
2025    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
2026    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
2027    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
2028    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
2029    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
2030    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
2031    /// maps onto every load-bearing downstream consumer the substrate
2032    /// carries — the [`Self::validate_deps`] per-entry
2033    /// [`Dep::validate`] + within-list dedup walk at
2034    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
2035    /// cross-list self-reference gate at caixa-core/src/layout.rs that
2036    /// checks each entry against the caixa's own `:nome`, the
2037    /// caixa-resolver `for dep in &root.deps` closure walk at
2038    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
2039    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
2040    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
2041    /// caixa-crd/src/conversion.rs that materializes each entry into the
2042    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
2043    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
2044    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
2045    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
2046    /// closure emit walk the caixa-resolver docstring roadmaps).
2047    ///
2048    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2049    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2050    /// sibling `:deps-dev` future lift closes on. Peer of the closed
2051    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
2052    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
2053    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
2054    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
2055    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
2056    /// pattern onto a novel element-type axis (`Dep` composite vs the
2057    /// prior sibling family's `String` scalar). Sibling in shape to the
2058    /// peer per-`:supervisor`
2059    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
2060    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
2061    /// (a6e18d7), per-`:membros`
2062    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
2063    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
2064    /// (0dcc926), and per-`:upgrade-from :instructions`
2065    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2066    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2067    /// typed-slot list axes, extended here to the outer top-level
2068    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
2069    /// (not `&Vec<Dep>`) because every downstream consumer of the
2070    /// runtime-dep list treats it as a read-only sequence — the slice-
2071    /// view is the narrowest borrow that supports every present +
2072    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2073    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2074    /// of the typed view reaches for (the storage-side `Vec` remains
2075    /// reachable through the `pub deps` field for the mutation-carrying
2076    /// serde round-trip and per-test fixture-mutation paths). Named
2077    /// `deps()` to match the storage field's name; the accessor's
2078    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
2079    /// slot's docstring already carries.
2080    #[must_use]
2081    pub const fn deps(&self) -> &[Dep] {
2082        self.deps.as_slice()
2083    }
2084
2085    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
2086    /// development-only-dependency-declaration-list slice-accessor every
2087    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
2088    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
2089    /// slice-view over the same backing buffer the raw
2090    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
2091    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
2092    /// form supplies with an empty `()` when unset; the
2093    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
2094    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
2095    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
2096    /// the returned `&[Dep]` degenerates to an empty slice on that arm
2097    /// without any silent `None` collapse).
2098    ///
2099    /// The `:deps-dev` slot carries the universal-axis dev-only
2100    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
2101    /// the author-facing sibling of `:deps` that every `defcaixa` form
2102    /// supplies to declare tests / lint / bench closures the runtime
2103    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
2104    /// axis every downstream test-facing artifact emits under, matching
2105    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
2106    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
2107    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
2108    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
2109    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
2110    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
2111    /// within-list duplicate `:nome` rejected through
2112    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
2113    /// load-bearing downstream consumer the substrate carries — the
2114    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
2115    /// dedup walk at caixa-core/src/manifest.rs, the
2116    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
2117    /// gate at caixa-core/src/layout.rs that checks each entry against
2118    /// the caixa's own `:nome`, the caixa-resolver
2119    /// `for dep in &root.deps_dev` closure walk at
2120    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
2121    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
2122    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
2123    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
2124    /// overlay the M4 CR materializer resolves per-CR, the future
2125    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
2126    /// roadmaps).
2127    ///
2128    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2129    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2130    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
2131    /// jointly close the two-list dep-graph surface every downstream
2132    /// resolver-facing consumer keys off (runtime `:deps` +
2133    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
2134    /// pair the [`Self::validate_deps`] gate already walks in canonical
2135    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
2136    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
2137    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
2138    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
2139    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
2140    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
2141    /// dev-dep composite-element axis (`Dep` composite, matching the
2142    /// [`Self::deps`] element type). Sibling in shape to the peer
2143    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
2144    /// (bc92bce), per-`:placement`
2145    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
2146    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
2147    /// (6c77e36), per-`:contratos`
2148    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
2149    /// per-`:upgrade-from :instructions`
2150    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2151    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2152    /// typed-slot list axes, folded here to the outer top-level
2153    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
2154    /// (not `&Vec<Dep>`) because every downstream consumer of the
2155    /// dev-dep list treats it as a read-only sequence — the slice-view
2156    /// is the narrowest borrow that supports every present +
2157    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2158    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2159    /// of the typed view reaches for (the storage-side `Vec` remains
2160    /// reachable through the `pub deps_dev` field for the mutation-
2161    /// carrying serde round-trip and per-test fixture-mutation paths).
2162    /// Named `deps_dev()` to match the storage field's `snake_case` name;
2163    /// the kebab-case author-surface tag `:deps-dev` is the same axis
2164    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
2165    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
2166    /// docstring already carries.
2167    #[must_use]
2168    pub const fn deps_dev(&self) -> &[Dep] {
2169        self.deps_dev.as_slice()
2170    }
2171
2172    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
2173    /// every consumer that walks one of the two dep-list axes keyed on a
2174    /// [`crate::dep::DepList`] discriminant reaches for — routes the
2175    /// `(list: DepList) -> &[Dep]` projection through one typed method on
2176    /// the substrate primitive rather than the prior open-coded
2177    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
2178    /// inline dispatch every per-axis walker would otherwise carry.
2179    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
2180    /// `&[Dep]` slice-view over the same backing buffer the sibling
2181    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
2182    /// accessors borrow from, preserving the empty-list-carrying invariant
2183    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
2184    /// are default-empty axes every `defcaixa` form supplies with an empty
2185    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
2186    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
2187    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
2188    /// returned `&[Dep]` degenerates to an empty slice on either arm
2189    /// without any silent `None` collapse).
2190    ///
2191    /// The [`crate::dep::DepList`] closed-set typed enum is the
2192    /// substrate's canonical discriminator for the "runtime-closure
2193    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
2194    /// consumer dispatches on — the compiler-checked exhaustiveness on
2195    /// the enum's `match` arms is the build-time guarantee that no future
2196    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
2197    /// that a future third dep-list axis (a `:deps-build` build-only
2198    /// closure once the substrate grows cross-artifact heterogeneous
2199    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
2200    /// consumer. Prior to this the read side carried two per-slot
2201    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
2202    /// typed dispatch that a per-axis walker could parametrise on, so
2203    /// every per-list walker (the [`Self::validate_deps`] per-list
2204    /// [`crate::render::insert_first_seen`] dedup walk, a future
2205    /// `feira app graph` per-list dep summary, a future M4 per-cluster
2206    /// dev-closure-audit overlay the CR materializer resolves per-CR)
2207    /// open-coded the same two-block "run over `:deps`, then run over
2208    /// `:deps-dev`" pattern — a silent duplication that a future third
2209    /// dep-list axis would have had to grow a third block at every site.
2210    ///
2211    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
2212    /// (359fba5) — closes the two-side dispatch symmetry on the outer
2213    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
2214    /// side, `deps_of` on the read side, both keyed on the same
2215    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
2216    /// the substrate primitive, thin projections at each consumer"
2217    /// discipline the sibling per-slot read accessors ([`Self::nome`]
2218    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
2219    /// the outer-[`Caixa`] typed-dispatch read surface.
2220    ///
2221    /// Declared `pub const fn` — every operator in the body is already
2222    /// `const`-callable (the [`crate::dep::DepList`] enum is a plain
2223    /// closed-set `#[derive(Copy)]` discriminator so the `match` arms
2224    /// are const-evaluable, and each arm forwards through the sibling
2225    /// `pub const fn` [`Self::deps`] / [`Self::deps_dev`] per-slot
2226    /// slice accessor). Pinned load-bearing by the paired
2227    /// [`caixa_deps_of_is_const_fn`][pin] wrapper test (a
2228    /// `const fn deps_of_via_const_fn(c: &Caixa, l: DepList) -> &[Dep]`
2229    /// that forwards through this accessor) — any future accidental
2230    /// downgrade to non-`const` fails the wrapper at caixa-core build
2231    /// time with E0015 (`cannot call non-const method`), strictly
2232    /// stronger than a runtime `assert!` and side-stepping the
2233    /// destructor-in-const restriction the `Caixa` fixture's owning
2234    /// carriers rule out on the direct-`const _: () = assert!(…)`
2235    /// residence. Peer of the sibling per-`Dep` outer-accessor
2236    /// family's parallel `const`-eval-surface pass and of the outer-
2237    /// `Caixa` slice-return accessor family's earlier pass (231a968)
2238    /// — same "one canonical dispatch per axis, `const`-eval posture
2239    /// pinned at the substrate primitive, thin projections at each
2240    /// consumer" discipline extended onto the outer-`Caixa`
2241    /// typed-dispatch read surface on the [`DepList`]-keyed dep-list
2242    /// axis.
2243    ///
2244    /// [DepList]: crate::dep::DepList
2245    /// [pin]: tests::caixa_deps_of_is_const_fn
2246    #[must_use]
2247    pub const fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2248        match list {
2249            crate::dep::DepList::Prod => self.deps(),
2250            crate::dep::DepList::Dev => self.deps_dev(),
2251        }
2252    }
2253
2254    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2255    /// consumer that appends to one of the two dep-list axes keys off
2256    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2257    /// method on the substrate primitive rather than the prior
2258    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2259    /// else { &mut caixa.deps }` inline dispatch + open-coded
2260    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2261    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2262    /// a within-list name collision — the same `list: &'static str`
2263    /// diagnostic shape [`Self::validate_deps`]'s per-list
2264    /// [`crate::render::insert_first_seen`] walk raises on the peer
2265    /// parse-time within-list dedup axis, so a future author reading a
2266    /// `feira add` refusal and a `feira build` refusal reaches for the
2267    /// same corrective surface without switching diagnostic idioms.
2268    ///
2269    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2270    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2271    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2272    /// dispatches on — the compiler-checked exhaustiveness on the
2273    /// enum's `match` arms is the build-time guarantee that no future
2274    /// per-list mutation-site regresses to a bare-`bool`-flag
2275    /// (`is_dev: bool`) inline dispatch that a future third
2276    /// dep-list axis (a `:deps-build` build-only closure once the
2277    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2278    /// CAIXA-SDLC §I) would silently split at every consumer.
2279    ///
2280    /// Same "one typed dispatch on the substrate primitive, thin
2281    /// projections at each consumer" discipline the sibling per-slot
2282    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2283    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2284    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2285    /// the substrate's first typed-mutation dispatch on the top-level
2286    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2287    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2288    /// diagnostic path routed no through-line back to the typed slot,
2289    /// so a future extension of either dep-list axis to a richer author
2290    /// surface (a per-cluster override the operator pins through a
2291    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2292    /// roadmap acknowledges, an M4
2293    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2294    /// admission-webhook that normalized the list at admission time)
2295    /// would have had to be threaded through the `feira add` mutation
2296    /// site in lockstep with every read consumer or one path would
2297    /// silently disagree with the other on which list a given dep lands
2298    /// in. Lifting the resolution rule to a typed method on the
2299    /// substrate primitive means every downstream dep-list-mutating
2300    /// consumer of the top-level manifest reaches for exactly one typed
2301    /// dispatch — the resolver's accept-set migrates as a unit on any
2302    /// future axis addition.
2303    ///
2304    /// # Errors
2305    ///
2306    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2307    /// when another entry in the same list already carries the same
2308    /// `:nome` — the mutation is refused and the caller can surface the
2309    /// typed diagnostic to the author (the `feira add` verb routes the
2310    /// error through `anyhow::Error::from`, which preserves the
2311    /// canonical `#[error(...)]`-templated diagnostic body).
2312    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2313        let target = match list {
2314            crate::dep::DepList::Prod => &mut self.deps,
2315            crate::dep::DepList::Dev => &mut self.deps_dev,
2316        };
2317        if target.iter().any(|d| d.nome() == dep.nome()) {
2318            return Err(DepError::DuplicateNome {
2319                nome: dep.nome().to_string(),
2320                list: list.as_str(),
2321            });
2322        }
2323        target.push(dep);
2324        Ok(())
2325    }
2326
2327    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2328    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2329    /// composite-reference accessor every consumer of the top-level
2330    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2331    /// off — returns the author-declared `:limits` typed composite
2332    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2333    /// backing storage the raw `self.limits.as_ref()` field access
2334    /// borrows from, with `None` naming the "no `:limits` block
2335    /// authored — every per-axis Lunatic-sandbox cap defers to the
2336    /// wasm-engine-default arm named on the per-axis
2337    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2338    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2339    /// docstrings" partition every downstream Servico-M2-overlay
2340    /// emitter treats as "emit nothing" and the sibling
2341    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2342    /// treats as "skip the per-axis
2343    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2344    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2345    ///
2346    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2347    /// composite — the load-bearing container of every Lunatic-shaped
2348    /// per-process wasm32-sandbox cap axis every long-running wasm
2349    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2350    /// Lunatic per-process linear-memory / fuel / wall-clock /
2351    /// millicore cap primitives translated onto pleme-io's typed
2352    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2353    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2354    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2355    /// chart both fan on). Every per-`:limits` axis threads through a
2356    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2357    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2358    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2359    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2360    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2361    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2362    /// consumer that reaches for a limits axis first passes through
2363    /// this outer accessor onto the composite and then dispatches
2364    /// onto the per-axis accessor — the two-level dispatch means
2365    /// every per-`:limits` reader now routes through a typed dispatch
2366    /// on the substrate primitive at both altitudes.
2367    ///
2368    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2369    /// was accessed inline at three production sites — the
2370    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2371    /// `if let Some(l) = &caixa.limits { … }` traversal head
2372    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2373    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2374    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2375    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2376    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2377    /// [`LimitsSpec::validate`] fans onto), the
2378    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2379    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2380    /// head (caixa-core/src/render.rs:18504, which drives the
2381    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2382    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2383    /// block emitter fans on), and the
2384    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2385    /// set enumerator's `self.limits.is_some()` presence probe
2386    /// (caixa-core/src/manifest.rs:1788, which drives the
2387    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2388    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2389    /// gate reads) — three open-coded outer-field accesses that
2390    /// expressed no compile-time link back to the typed slot at the
2391    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2392    /// axis to a richer author surface (a multi-`:limits` list the M4
2393    /// CR materializer resolves per-CR at admission time so a Servico
2394    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2395    /// cluster `:limits-overrides` slot the operator pins so a
2396    /// cluster-specific policy can tighten a caixa-declared cap
2397    /// without re-authoring the `caixa.lisp`, a promotion of the
2398    /// plain `Option<LimitsSpec>` to a richer
2399    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2400    /// resolved dynamic-cap surface lands) would have had to be
2401    /// threaded through all three open-coded copies in lockstep or
2402    /// one consumer would silently disagree with the peers on which
2403    /// limits composite a given Caixa resolves to — the layout gate's
2404    /// per-axis bracket-dispatch seed reading the raw slot while the
2405    /// peer `servico_m2_overlay` emitter read an operator-resolved
2406    /// slot would silently split the build-time sandbox-shape gate
2407    /// from the runtime `ComputeUnit` CR emission gate, a three-
2408    /// consumer split at the layout gate, the M2 overlay emitter, and
2409    /// the declared-slot enumerator far from the source `caixa.lisp`
2410    /// with no field naming the limits-drift root cause. Lifting the
2411    /// resolution rule to a typed method on the substrate primitive
2412    /// means every downstream consumer of the caixa's per-`Caixa`
2413    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2414    /// one typed dispatch — the resolver's accept-set migrates as a
2415    /// unit on any future axis addition.
2416    ///
2417    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2418    /// composite-reference accessor — opens the outer-`Caixa`
2419    /// `Option<&Composite>` composite-reference projection pattern the
2420    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2421    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2422    /// [`crate::aplicacao::Placement`] / `:entrada`
2423    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2424    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2425    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2426    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2427    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2428    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2429    /// altitude — extends that "one typed dispatch on the substrate
2430    /// primitive, thin projections at each consumer" discipline onto
2431    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2432    /// runtime slot family's outer-composite axis. Returns
2433    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2434    /// clone) because every downstream consumer of the limits
2435    /// composite treats it as a read-only per-axis dispatch source —
2436    /// the reference-view is the narrowest borrow that supports every
2437    /// present + roadmapped consumer (per-axis accessor dispatch,
2438    /// `.is_empty()`-gated overlay projection, presence-probe early
2439    /// return on the "author-omitted `:limits` ⇒ engine-default
2440    /// applies" partition) without cloning the composite through
2441    /// every consumer's fast path. The `Option` half of the return-
2442    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2443    /// engine-default applies" partition (not a default composite the
2444    /// downstream must reject on emptiness) — the accessor projects
2445    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2446    /// reference-return unchanged. Named `limits()` to match the
2447    /// storage field's name verbatim and the tatara-lisp author-
2448    /// surface term (`:limits`) the field's own docstring already
2449    /// carries.
2450    #[must_use]
2451    pub const fn limits(&self) -> Option<&LimitsSpec> {
2452        self.limits.as_ref()
2453    }
2454
2455    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2456    /// composite OTP-`gen_server`-shaped callback-table optional-
2457    /// composite-reference accessor every consumer of the top-level
2458    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2459    /// keys off — returns the author-declared `:behavior` typed
2460    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2461    /// the same backing storage the raw `self.behavior.as_ref()` field
2462    /// access borrows from, with `None` naming the "no `:behavior`
2463    /// block authored — every per-callback OTP-shaped hook defers to
2464    /// the wasm-engine's runtime default arm named on the per-axis
2465    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2466    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2467    /// [`BehaviorSpec::on_state_change`] /
2468    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2469    /// partition every downstream Servico-M2-overlay emitter treats as
2470    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2471    /// per-`:behavior` shape gate treats as "skip the per-arm
2472    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2473    /// per-callback on-disk `MissingEntry` existence check".
2474    ///
2475    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2476    /// composite — the load-bearing container of every OTP-shaped
2477    /// per-Servico lifecycle-callback path axis every long-running wasm
2478    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2479    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2480    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2481    /// translated onto pleme-io's typed `:behavior :on-init` /
2482    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2483    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2484    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2485    /// chart both fan on). Every per-`:behavior` axis threads through a
2486    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2487    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2488    /// Every downstream consumer that reaches for a behavior axis
2489    /// first passes through this outer accessor onto the composite
2490    /// and then dispatches onto the per-callback accessor — the
2491    /// two-level dispatch means every per-`:behavior` reader now
2492    /// routes through a typed dispatch on the substrate primitive at
2493    /// both altitudes.
2494    ///
2495    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2496    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2497    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2498    /// keys the "per-version `:state-change` instruction must have a
2499    /// `:on-state-change` callback" precondition off this accessor's
2500    /// composite (the callback-side counterpart to the
2501    /// `:upgrade-from :instructions :state-change :script` refusal at
2502    /// the appup-side). Threading that gate's traversal input through
2503    /// this accessor closes the cross-slot invariant on the substrate
2504    /// primitive, not on the raw field.
2505    ///
2506    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2507    /// composite was accessed inline at four production sites — the
2508    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2509    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2510    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2511    /// `BehaviorError` refusal cascade + the per-callback on-disk
2512    /// [`crate::LayoutError::MissingEntry`] existence check under
2513    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2514    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2515    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2516    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2517    /// drives the `:state-change` ↔ `:on-state-change` precondition
2518    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2519    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2520    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2521    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2522    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2523    /// Servico values-block emitter fans on), and the
2524    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2525    /// set enumerator's `self.behavior.is_some()` presence probe
2526    /// (caixa-core/src/manifest.rs:1919, which drives the
2527    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2528    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2529    /// gate reads) — four open-coded outer-field accesses that
2530    /// expressed no compile-time link back to the typed slot at the
2531    /// [`Caixa`] altitude. A future extension of the `:behavior`
2532    /// outer axis to a richer author surface (a per-callback overlay
2533    /// resolver the operator materializes at admission time so a
2534    /// cluster-specific policy can inject a per-callback tracing
2535    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2536    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2537    /// dynamic}` partition once a runtime-resolved behavior-swap
2538    /// surface lands, the M4 per-callback middleware chain the
2539    /// caixa-operator's per-Servico admission webhook keys off) would
2540    /// have had to be threaded through all four open-coded copies in
2541    /// lockstep or one consumer would silently disagree with the
2542    /// peers on which behavior composite a given Caixa resolves to —
2543    /// the layout gate's per-callback existence-check seed reading
2544    /// the raw slot while the peer `servico_m2_overlay` emitter read
2545    /// an operator-resolved slot would silently split the build-time
2546    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2547    /// gate from the cross-slot `:state-change` composition gate from
2548    /// the M2 declared-slot enumerator, a four-consumer split far
2549    /// from the source `caixa.lisp` with no field naming the
2550    /// behavior-drift root cause. Lifting the resolution rule to a
2551    /// typed method on the substrate primitive means every downstream
2552    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2553    /// composite surface reaches for exactly one typed dispatch — the
2554    /// resolver's accept-set migrates as a unit on any future axis
2555    /// addition.
2556    ///
2557    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2558    /// composite-reference accessor — sibling to the opening
2559    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2560    /// `Option<&Composite>` composite-reference sub-family, extends
2561    /// the "one typed dispatch on the substrate primitive, thin
2562    /// projections at each consumer" discipline onto the second of
2563    /// the three M2 Servico-runtime slots. The remaining
2564    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2565    /// altitude — the M3 mesh-slot family (`:politicas`,
2566    /// `:placement`, `:entrada` — already closed on the inner
2567    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2568    /// d32111c) — remain the future sibling lifts on the outer
2569    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2570    /// the owning composite by copy or clone) because every
2571    /// downstream consumer of the behavior composite treats it as a
2572    /// read-only per-callback dispatch source — the reference-view is
2573    /// the narrowest borrow that supports every present + roadmapped
2574    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2575    /// overlay projection, presence-probe early return on the
2576    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2577    /// partition, cross-slot `:state-change` composition input)
2578    /// without cloning the composite through every consumer's fast
2579    /// path. The `Option` half of the return-type preserves the
2580    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2581    /// applies" partition (not a default composite the downstream
2582    /// must reject on emptiness) — the accessor projects the raw
2583    /// `Option<BehaviorSpec>` slot's presence bit through the
2584    /// reference-return unchanged. Named `behavior()` to match the
2585    /// storage field's name verbatim and the tatara-lisp author-
2586    /// surface term (`:behavior`) the field's own docstring already
2587    /// carries.
2588    #[must_use]
2589    pub const fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2590        self.behavior.as_ref()
2591    }
2592
2593    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2594    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2595    /// reference accessor every consumer of the top-level manifest's
2596    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2597    /// reader keys off — returns the author-declared `:politicas` typed
2598    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2599    /// same backing storage the raw `self.politicas.as_ref()` field
2600    /// access borrows from, with `None` naming the "no `:politicas`
2601    /// block authored — every per-axis mesh-policy scalar defers to the
2602    /// cluster-default arm named on the per-axis
2603    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2604    /// [`crate::aplicacao::MeshPolicy::retries`] /
2605    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2606    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2607    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2608    /// docstrings" partition every downstream caixa-mesh /
2609    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2610    /// "emit no per-`:politicas` overlay" and the sibling
2611    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2612    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2613    /// arm.
2614    ///
2615    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2616    /// Aplicacao typed composite — the load-bearing container of every
2617    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2618    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2619    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2620    /// composite; §V — the "no infinite blocking" per-call deadline +
2621    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2622    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2623    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2624    /// threads through a lifted per-slot accessor on the
2625    /// [`crate::aplicacao::MeshPolicy`] type: the
2626    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2627    /// mTLS-enforcement toggle, the
2628    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2629    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2630    /// (7073d0f) Gateway-API per-call deadline, the
2631    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2632    /// Envoy-outlier-detection composite. Every downstream consumer
2633    /// that reaches for a mesh-policy axis first passes through this
2634    /// outer accessor onto the composite and then dispatches onto the
2635    /// per-axis accessor — the two-level dispatch means every per-
2636    /// `:politicas` reader now routes through a typed dispatch on the
2637    /// substrate primitive at both altitudes.
2638    ///
2639    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2640    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2641    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2642    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2643    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2644    /// composite whether or not the author declared the outer slot.
2645    /// The outer accessor preserves the "author-omitted vs authored-
2646    /// empty" partition the inner accessor's `is_empty()`-gated
2647    /// renderer overlay collapses — routing the presence bit through
2648    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2649    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2650    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2651    ///
2652    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2653    /// composite was accessed inline at two production sites — the
2654    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2655    /// `self.politicas.clone().unwrap_or_default()` traversal head
2656    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2657    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2658    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2659    /// then observes), and the [`Self::declared_mesh_slots`] M3
2660    /// declared-slot-set enumerator's `self.politicas.is_some()`
2661    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2662    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2663    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2664    /// coherence gate reads) — two open-coded outer-field accesses
2665    /// that expressed no compile-time link back to the typed slot at
2666    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2667    /// outer axis to a richer author surface (a per-cluster
2668    /// `:politicas-overrides` slot the operator materializes at
2669    /// admission time so a cluster-specific policy can tighten the
2670    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2671    /// promotion of the plain `Option<MeshPolicy>` to a richer
2672    /// `{static, dynamic}` partition once the M4 per-edge
2673    /// contrato-scoped policy-override surface lands, the M5 traffic-
2674    /// shaping composition the caixa-operator's per-Aplicacao mesh
2675    /// admission webhook keys off) would have had to be threaded
2676    /// through both open-coded copies in lockstep or the Aplicacao-
2677    /// composition seed's default-fold arm would silently disagree
2678    /// with the M3 declared-slot enumerator on which policy composite
2679    /// a given Caixa resolves to — the seed reading an operator-
2680    /// resolved slot while the enumerator's presence probe read the
2681    /// raw slot would silently split the build-time mesh-artifact
2682    /// emission gate from the M3 declared-slot enumerator's kind-
2683    /// coherence gate, a two-consumer split far from the source
2684    /// `caixa.lisp` with no field naming the policy-drift root cause.
2685    /// Lifting the resolution rule to a typed method on the substrate
2686    /// primitive means every downstream consumer of the caixa's per-
2687    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2688    /// reaches for exactly one typed dispatch — the resolver's
2689    /// accept-set migrates as a unit on any future axis addition.
2690    ///
2691    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2692    /// composite-reference accessor — sibling to the opening
2693    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2694    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2695    /// reference sub-family, extends the "one typed dispatch on the
2696    /// substrate primitive, thin projections at each consumer"
2697    /// discipline onto the first of the three M3 mesh-slot axes.
2698    /// Peer of the closed inner mesh-slot outer-composite family the
2699    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2700    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2701    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2702    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2703    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2704    /// mesh-slot arm of the composite-reference family the remaining
2705    /// two axes (`:placement`, `:entrada`) fold onto in future
2706    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2707    /// composite by copy or clone) because every downstream consumer
2708    /// of the mesh-policy composite treats it as a read-only per-axis
2709    /// dispatch source — the reference-view is the narrowest borrow
2710    /// that supports every present + roadmapped consumer (per-axis
2711    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2712    /// presence-probe early return on the "author-omitted `:politicas`
2713    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2714    /// seed's default-fold arm) without cloning the composite through
2715    /// every consumer's fast path. The `Option` half of the return-
2716    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2717    /// cluster-default applies" partition (not a default composite
2718    /// the downstream must reject on emptiness) — the accessor
2719    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2720    /// through the reference-return unchanged. Named `politicas()` to
2721    /// match the storage field's name verbatim and the tatara-lisp
2722    /// author-surface term (`:politicas`) the field's own docstring
2723    /// already carries.
2724    #[must_use]
2725    pub const fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2726        self.politicas.as_ref()
2727    }
2728
2729    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2730    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2731    /// reference accessor every consumer of the top-level manifest's
2732    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2733    /// reader keys off — returns the author-declared `:placement` typed
2734    /// composite verbatim as an `Option<&Placement>` reference over the
2735    /// same backing storage the raw `self.placement.as_ref()` field
2736    /// access borrows from, with `None` naming the "no `:placement`
2737    /// block authored — every per-axis placement scalar defers to the
2738    /// cluster-default arm named on the per-axis
2739    /// [`crate::aplicacao::Placement::estrategia`] /
2740    /// [`crate::aplicacao::Placement::clusters`] /
2741    /// [`crate::aplicacao::Placement::affinity`] /
2742    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2743    /// docstrings" partition every downstream caixa-mesh /
2744    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2745    /// "emit no per-`:placement` overlay" and the sibling
2746    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2747    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2748    ///
2749    /// The outer `:placement` slot carries the M3 mesh-slot per-
2750    /// Aplicacao typed distribution composite — the load-bearing
2751    /// container of every where-does-this-Aplicacao-run axis every
2752    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2753    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2754    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2755    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2756    /// Aplicacao's typed distribution composite; §V CSE invariants —
2757    /// "distribution is a first-class typed composite, not a runtime
2758    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2759    /// typed inter-Servico contrato-edge overlay the per-cluster
2760    /// mesh renderer keys off). Every per-`:placement` axis threads
2761    /// through a lifted per-slot accessor on the
2762    /// [`crate::aplicacao::Placement`] type: the
2763    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2764    /// MESH-COMPOSITION distribution-strategy scalar, the
2765    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2766    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2767    /// M3-Adaptive-compression-hint optional-scalar, and the
2768    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2769    /// sharding extractor-expression optional-scalar. Every downstream
2770    /// consumer that reaches for a placement axis first passes through
2771    /// this outer accessor onto the composite and then dispatches onto
2772    /// the per-axis accessor — the two-level dispatch means every per-
2773    /// `:placement` reader now routes through a typed dispatch on the
2774    /// substrate primitive at both altitudes.
2775    ///
2776    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2777    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2778    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2779    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2780    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2781    /// whether or not the author declared the outer slot. The outer
2782    /// accessor preserves the "author-omitted vs authored-empty" partition
2783    /// the inner accessor collapses at the cluster-default fold —
2784    /// routing the presence bit through this accessor keeps the
2785    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2786    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2787    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2788    /// dispatch.
2789    ///
2790    /// Prior to this lift the `.placement` `Option<Placement>`
2791    /// composite was accessed inline at two production sites — the
2792    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2793    /// `self.placement.clone().unwrap_or_default()` traversal head
2794    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2795    /// the [`crate::aplicacao::Placement::default`] cluster-default
2796    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2797    /// then observes), and the [`Self::declared_mesh_slots`] M3
2798    /// declared-slot-set enumerator's `self.placement.is_some()`
2799    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2800    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2801    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2802    /// coherence gate reads) — two open-coded outer-field accesses
2803    /// that expressed no compile-time link back to the typed slot at
2804    /// the [`Caixa`] altitude. A future extension of the `:placement`
2805    /// outer axis to a richer author surface (a per-cluster
2806    /// `:placement-overrides` slot the operator materializes at
2807    /// admission time so a cluster-specific placement can tighten the
2808    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2809    /// per-tenant placement-alias table the M4
2810    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2811    /// per-CR at admission time, a promotion of the plain
2812    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2813    /// once Orleans-style virtual-actor dynamic placement comes into
2814    /// typed scope) would have had to be threaded through both open-
2815    /// coded copies in lockstep or the Aplicacao-composition seed's
2816    /// default-fold arm would silently disagree with the M3 declared-
2817    /// slot enumerator on which distribution composite a given Caixa
2818    /// resolves to — the seed reading an operator-resolved slot while
2819    /// the enumerator's presence probe read the raw slot would
2820    /// silently split the build-time distribution-artifact emission
2821    /// gate from the M3 declared-slot enumerator's kind-coherence
2822    /// gate, a two-consumer split far from the source `caixa.lisp`
2823    /// with no field naming the distribution-drift root cause.
2824    /// Lifting the resolution rule to a typed method on the substrate
2825    /// primitive means every downstream consumer of the caixa's per-
2826    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2827    /// reaches for exactly one typed dispatch — the resolver's
2828    /// accept-set migrates as a unit on any future axis addition.
2829    ///
2830    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2831    /// composite-reference accessor — sibling to the opening
2832    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2833    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2834    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2835    /// composite-reference sub-family, folds on the "one typed
2836    /// dispatch on the substrate primitive, thin projections at each
2837    /// consumer" discipline extended onto the second of the three M3
2838    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2839    /// composite family the sibling
2840    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2841    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2842    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2843    /// accessor pins already close on the inner
2844    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2845    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2846    /// [`Self::politicas`] opened, extending the discipline onto the
2847    /// second of the three M3 mesh-slot axes. The remaining M3
2848    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2849    /// discipline in the final sibling lift, closing the outer top-
2850    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2851    /// Returns `Option<&Placement>` (not the owning composite by copy
2852    /// or clone) because every downstream consumer of the placement
2853    /// composite treats it as a read-only per-axis dispatch source —
2854    /// the reference-view is the narrowest borrow that supports every
2855    /// present + roadmapped consumer (per-axis accessor dispatch,
2856    /// serde composite-serialization on the programs.yaml overlay,
2857    /// presence-probe early return on the "author-omitted `:placement`
2858    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2859    /// seed's default-fold arm) without cloning the composite through
2860    /// every consumer's fast path. The `Option` half of the return-
2861    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2862    /// cluster-default applies" partition (not a default composite
2863    /// the downstream must reject on emptiness) — the accessor
2864    /// projects the raw `Option<Placement>` slot's presence bit
2865    /// through the reference-return unchanged. Named `placement()` to
2866    /// match the storage field's name verbatim and the tatara-lisp
2867    /// author-surface term (`:placement`) the field's own docstring
2868    /// already carries.
2869    #[must_use]
2870    pub const fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2871        self.placement.as_ref()
2872    }
2873
2874    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2875    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2876    /// composite-reference accessor every consumer of the top-level
2877    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2878    /// composite reader keys off — returns the author-declared
2879    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2880    /// reference over the same backing storage the raw
2881    /// `self.entrada.as_ref()` field access borrows from, with `None`
2882    /// naming the "no `:entrada` block authored — this Aplicacao is
2883    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2884    /// partition every downstream caixa-mesh Gateway-API artifact
2885    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2886    /// backend for this Aplicacao" and the sibling
2887    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2888    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2889    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2890    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2891    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2892    /// the same `Option<&Entrada>` presence bit unchanged).
2893    ///
2894    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2895    /// Aplicacao typed external-gateway composite — the load-bearing
2896    /// container of every how-does-the-outside-world-reach-this-
2897    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2898    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2899    /// external-entry composite; §V CSE invariants — "the external
2900    /// gateway is a first-class typed composite, not a per-Servico
2901    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2902    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2903    /// API renderer keys off). Every per-`:entrada` axis threads
2904    /// through a lifted per-slot accessor on the
2905    /// [`crate::aplicacao::Entrada`] type: the
2906    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2907    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2908    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2909    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2910    /// backend `trigger.service.port` scalar, and the
2911    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2912    /// resolver every HTTPRoute-aware renderer consumes. Every
2913    /// downstream consumer that reaches for an entry axis first passes
2914    /// through this outer accessor onto the composite and then
2915    /// dispatches onto the per-axis accessor — the two-level dispatch
2916    /// means every per-`:entrada` reader now routes through a typed
2917    /// dispatch on the substrate primitive at both altitudes.
2918    ///
2919    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2920    /// seed: the Aplicacao-view builder forwards the outer `Option`
2921    /// arm verbatim (no default fold — `:entrada` is inherently
2922    /// optional; a cluster-internal Aplicacao has no external gateway
2923    /// at all, not "an external gateway that defaults to nothing"), so
2924    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2925    /// `Option<&Entrada>`-return accessor observes the same presence
2926    /// bit whether or not the author declared the outer slot. Routing
2927    /// the presence bit through this accessor keeps the
2928    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2929    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2930    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2931    /// hostname/backend/path emission dispatch.
2932    ///
2933    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2934    /// was accessed inline at two production sites — the
2935    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2936    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2937    /// which drives the forward onto the peer inner
2938    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2939    /// Gateway-API fan-out then observes), and the
2940    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2941    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2942    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2943    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2944    /// kind-coherence gate reads) — two open-coded outer-field
2945    /// accesses that expressed no compile-time link back to the typed
2946    /// slot at the [`Caixa`] altitude. A future extension of the
2947    /// `:entrada` outer axis to a richer author surface (a per-cluster
2948    /// `:entrada-overrides` slot the operator materializes at admission
2949    /// time so a cluster-specific hostname can pin the caixa-declared
2950    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2951    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2952    /// CR materializer resolves per-CR at admission time, a promotion
2953    /// of the plain `Option<Entrada>` to a richer
2954    /// `{public, private, internal}` partition once Cilium-identity-
2955    /// scoped internal gateways come into typed scope) would have had
2956    /// to be threaded through both open-coded copies in lockstep or the
2957    /// Aplicacao-composition seed's forward arm would silently
2958    /// disagree with the M3 declared-slot enumerator on which external-
2959    /// gateway composite a given Caixa resolves to — the seed reading
2960    /// an operator-resolved slot while the enumerator's presence probe
2961    /// read the raw slot would silently split the build-time gateway-
2962    /// artifact emission gate from the M3 declared-slot enumerator's
2963    /// kind-coherence gate, a two-consumer split far from the source
2964    /// `caixa.lisp` with no field naming the entry-drift root cause.
2965    /// Lifting the resolution rule to a typed method on the substrate
2966    /// primitive means every downstream consumer of the caixa's per-
2967    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2968    /// surface reaches for exactly one typed dispatch — the resolver's
2969    /// accept-set migrates as a unit on any future axis addition.
2970    ///
2971    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2972    /// return composite-reference accessor — closes the outer-`Caixa`
2973    /// `Option<&Composite>` composite-reference sub-family opened by
2974    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2975    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2976    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2977    /// folds on the "one typed dispatch on the substrate primitive,
2978    /// thin projections at each consumer" discipline extended onto the
2979    /// third and final M3 mesh-slot axis. Peer of the closed inner
2980    /// mesh-slot outer-composite family the sibling
2981    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2982    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2983    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2984    /// accessor pins already close on the inner
2985    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2986    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2987    /// altitudes of the outer-composite reference-return discipline
2988    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2989    /// slot presence) now carry the full five-arm accept-set behind a
2990    /// typed dispatch on the substrate primitive. Returns
2991    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2992    /// because every downstream consumer of the entrada composite
2993    /// treats it as a read-only per-axis dispatch source — the
2994    /// reference-view is the narrowest borrow that supports every
2995    /// present + roadmapped consumer (per-axis accessor dispatch,
2996    /// serde composite-serialization on the programs.yaml overlay,
2997    /// presence-probe early return on the "author-omitted `:entrada`
2998    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2999    /// seed's forward arm) without cloning the composite through every
3000    /// consumer's fast path. The `Option` half of the return-type
3001    /// preserves the load-bearing "author-omitted `:entrada` ⇒
3002    /// cluster-internal Aplicacao" partition (not a default composite
3003    /// the downstream must reject on emptiness — a cluster-internal
3004    /// Aplicacao has no external gateway at all, not "a default gateway
3005    /// that emits nothing"); the accessor projects the raw
3006    /// `Option<Entrada>` slot's presence bit through the reference-
3007    /// return unchanged. Named `entrada()` to match the storage field's
3008    /// name verbatim and the tatara-lisp author-surface term
3009    /// (`:entrada`) the field's own docstring already carries.
3010    #[must_use]
3011    pub const fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
3012        self.entrada.as_ref()
3013    }
3014
3015    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
3016    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
3017    /// an `Option<&CiRun>`, borrowed from the typed slot's own
3018    /// `Option<CiRun>` storage. `None` when the slot is absent (every
3019    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
3020    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
3021    /// not silently accepted).
3022    ///
3023    /// Named `ci()` to match the storage field's name and the
3024    /// tatara-lisp author surface (`:ci`); mirrors the sibling
3025    /// `Option<&Composite>` accessors on this same `Caixa` altitude
3026    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
3027    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
3028    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
3029    /// at every consumer.
3030    #[must_use]
3031    pub const fn ci(&self) -> Option<&canteiro_types::CiRun> {
3032        self.ci.as_ref()
3033    }
3034
3035    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
3036    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
3037    /// accessor every consumer of the top-level manifest's per-Supervisor
3038    /// restart-strategy axis keys off — returns the author-declared
3039    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
3040    /// `Copy`-projected from the typed slot's own
3041    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
3042    /// (`:estrategia` is a flat-spread supervisor-only slot every
3043    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
3044    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3045    /// still omit to defer to [`RestartStrategy::default`] —
3046    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
3047    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
3048    /// [`SupervisorSpec::default`]-inherited strategy without any silent
3049    /// promotion to a fresh explicit variant at the accessor boundary).
3050    ///
3051    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
3052    /// restart-strategy discriminant every substrate-side per-Supervisor
3053    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
3054    /// closed-set `one_for_one | one_for_all | rest_for_one |
3055    /// simple_one_for_one` algebra translated onto pleme-io's typed
3056    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
3057    /// slot algebra the operator's hierarchical reconciliation scheduler
3058    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
3059    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
3060    /// supervisor slots are flat on Caixa (vs nested under a
3061    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
3062    /// level of nesting"), so the accessor's altitude is the outer
3063    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
3064    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
3065    /// (eafb619) accessor keys off. The two typed axes — the outer
3066    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
3067    /// (author-omitted arm carried as `None`) and the inner post-
3068    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
3069    /// (`Option` collapsed through the [`Self::supervisor_view`]
3070    /// `unwrap_or_default()` fold) — now share one accessor discipline for
3071    /// the shared substrate concept "the author-declared OTP-shaped
3072    /// sibling-restart-strategy variant that partitions the downstream
3073    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
3074    /// `None` arm is the pre-composition presence bit every declared-slot
3075    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
3076    /// inner-altitude non-`Option` `RestartStrategy` is the post-
3077    /// composition partition-dispatch input every strategy-arm consumer
3078    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
3079    /// Supervisor sibling-restart branch, the future M4
3080    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
3081    /// webhook) fans on.
3082    ///
3083    /// Prior to this lift the `.estrategia` field was accessed inline at
3084    /// two production sites in `caixa-core/src/manifest.rs` — the
3085    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
3086    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
3087    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3088    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
3089    /// `SupervisorSpec` construction site at `estrategia:
3090    /// self.estrategia.unwrap_or_default()` (which composes the flat-
3091    /// spread outer author-surface `Option<RestartStrategy>` onto the
3092    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
3093    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
3094    /// coded field-accesses that expressed no compile-time link back to
3095    /// the typed slot. A future extension of the outer `:estrategia` axis
3096    /// to a richer author surface (a per-cluster strategy override the
3097    /// operator pins through a future `:estrategia-overrides` overlay the
3098    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
3099    /// a per-tenant strategy-alias table the M4 CR materializer resolves
3100    /// per-CR, a per-Supervisor dynamic strategy derivation the future
3101    /// adaptive-supervision engine computes from child-failure-history
3102    /// topology, a per-child-cohort strategy split the future
3103    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
3104    /// absorption roadmap acknowledges, a promotion of the plain
3105    /// `Option<RestartStrategy>` to a richer
3106    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
3107    /// operator-resolved overlay lands) would have had to be threaded
3108    /// through both open-coded copies in lockstep or the enumerator's
3109    /// presence probe and the composition site's `unwrap_or_default()`
3110    /// fold would silently disagree on which strategy a given [`Caixa`]
3111    /// resolves to (an author's `:estrategia OneForAll` would satisfy
3112    /// the enumerator's presence probe while the composition site
3113    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3114    /// the resolution rule to a typed method on the substrate primitive
3115    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3116    /// altitude sibling-restart-strategy surface reaches for exactly one
3117    /// typed dispatch — the resolver's accept-set migrates as a unit on
3118    /// any future axis addition.
3119    ///
3120    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3121    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3122    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3123    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3124    /// `Option<u32>` and (through the future duration-newtype landing)
3125    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3126    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3127    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3128    /// the post-composition [`SupervisorSpec`] altitude — same "one
3129    /// typed dispatch on the substrate primitive, thin projections at
3130    /// each consumer" discipline extended onto the pre-composition outer
3131    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3132    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3133    /// `Option<&Composite>` composite-reference family the sibling
3134    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3135    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3136    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3137    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3138    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3139    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3140    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3141    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3142    /// pins on the inner-altitude per-`:placement` composite. Named
3143    /// `estrategia()` to match the storage field's name and the
3144    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3145    /// / per-[`crate::aplicacao::Placement`] peer
3146    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3147    /// verbatim; the accessor's identity name maps onto the canonical
3148    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3149    /// docstring already carries.
3150    #[must_use]
3151    pub const fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3152        self.estrategia
3153    }
3154
3155    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3156    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3157    /// scalar accessor every consumer of the top-level manifest's per-
3158    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3159    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3160    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3161    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3162    /// accessor returns by value; no borrow of `&self` past the call).
3163    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3164    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3165    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3166    /// still omit to defer to the [`Self::supervisor_view`]
3167    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3168    ///
3169    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3170    /// `MaxIntensity` restart-budget count that pairs with the sibling
3171    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3172    /// restart-intensity ratio the supervisor trips its own escalation on
3173    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3174    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3175    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3176    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3177    /// the outer top-level `Caixa` (per the field-shape docstring at
3178    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3179    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3180    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3181    /// composed [`SupervisorSpec`] altitude the sibling
3182    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3183    /// off. The two typed axes — the outer author-surface `Option<u32>`
3184    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3185    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3186    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3187    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3188    /// shared substrate concept "the author-declared OTP-shaped
3189    /// restart-budget count every downstream per-Supervisor consumer's
3190    /// restart-intensity budget-vs-count comparator fans on".
3191    ///
3192    /// Prior to this lift the `.max_restarts` field was accessed inline
3193    /// at two production sites in `caixa-core/src/manifest.rs` — the
3194    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3195    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3196    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3197    /// kind-coherence gate's per-slot label push) and the
3198    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3199    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3200    /// flat-spread outer author-surface `Option<u32>` onto the inner
3201    /// post-composition [`SupervisorSpec`] `u32` field the
3202    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3203    /// coded field-accesses that expressed no compile-time link back to
3204    /// the typed slot. A future extension of the outer `:max-restarts`
3205    /// axis to a richer author surface (a per-cluster restart-budget
3206    /// override the operator pins through a future `:max-restarts-overrides`
3207    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3208    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3209    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3210    /// budget derivation the future adaptive-supervision engine computes
3211    /// from child-failure-history topology, a promotion of the plain
3212    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3213    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3214    /// per-child-cohort roadmap lands) would have had to be threaded
3215    /// through both open-coded copies in lockstep or the enumerator's
3216    /// presence probe and the composition site's `unwrap_or(5)` fold
3217    /// would silently disagree on which restart-budget a given [`Caixa`]
3218    /// resolves to (an author's `:max-restarts 10` would satisfy the
3219    /// enumerator's presence probe while the composition site silently
3220    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3221    /// resolution rule to a typed method on the substrate primitive means
3222    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3223    /// restart-budget-count surface reaches for exactly one typed dispatch
3224    /// — the resolver's accept-set migrates as a unit on any future axis
3225    /// addition.
3226    ///
3227    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3228    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3229    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3230    /// projection pattern the sibling per-`Caixa`
3231    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3232    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3233    /// Peer of the inner-altitude
3234    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3235    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3236    /// typed dispatch on the substrate primitive, thin projections at
3237    /// each consumer" discipline extended onto the pre-composition outer
3238    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3239    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3240    /// the storage field's name and the per-[`SupervisorSpec`] peer
3241    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3242    /// discipline verbatim; the accessor's identity maps onto the
3243    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3244    /// field's docstring already carries.
3245    #[must_use]
3246    pub const fn max_restarts(&self) -> Option<u32> {
3247        self.max_restarts
3248    }
3249
3250    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3251    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3252    /// denominator raw-duration-string scalar accessor every consumer of
3253    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3254    /// window axis keys off — returns the author-declared `:restart-window`
3255    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3256    /// from the typed slot's own `Option<String>` storage. `None` when
3257    /// the slot is absent (the canonical "never reset — every restart
3258    /// across the supervisor's lifetime counts against the sibling
3259    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3260    /// `defcaixa` carries by `#[serde(default)]` and every
3261    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3262    /// [`Self::supervisor_view`] `restart_window: None` composition
3263    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3264    /// swallow `.and_then(|s| … .ok())` fold).
3265    ///
3266    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3267    /// shaped `Period` sliding-observation-interval duration string that
3268    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3269    /// budget count to form the `MaxIntensity / Period` restart-intensity
3270    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3271    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3272    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3273    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3274    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3275    /// holds an `Option<Duration>` routed through the shared
3276    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3277    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3278    /// authoring surface) while the inner altitude's
3279    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3280    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3281    /// is closed by the sibling [`Self::validate_restart_window`] gate
3282    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3283    /// the offending value; the view-construction path
3284    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3285    /// `None` to keep the view best-effort.
3286    ///
3287    /// Prior to this lift the `.restart_window` field was accessed inline
3288    /// at three production sites in `caixa-core/src/manifest.rs` — the
3289    /// [`Self::declared_supervisor_slots`]
3290    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3291    /// `if self.restart_window.is_some()` (which drives the
3292    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3293    /// coherence gate's per-slot label push), the
3294    /// [`Self::validate_restart_window`] `let Some(s) =
3295    /// self.restart_window.as_deref()` empty-and-shape gate binding
3296    /// (which folds the raw string through the shared
3297    /// [`crate::supervisor::duration_codec::parse`] to surface
3298    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3299    /// value), and the [`Self::supervisor_view`] `self.restart_window
3300    /// .as_deref().and_then(…)` view-construction fold (which composes
3301    /// the flat-spread outer author-surface `Option<String>` onto the
3302    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3303    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3304    /// three open-coded field-accesses that expressed no compile-time
3305    /// link back to the typed slot. A future extension of the outer
3306    /// `:restart-window` axis to a richer author surface (a per-cluster
3307    /// window override, a per-tenant window-alias table, a per-Supervisor
3308    /// dynamic window derivation the future adaptive-supervision engine
3309    /// computes from child-failure-history topology, a promotion of the
3310    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3311    /// once the future author-surface parser lands at the [`Caixa`]
3312    /// altitude and the raw-string form is retired) would have had to be
3313    /// threaded through every open-coded copy in lockstep or the three
3314    /// consumers would silently disagree on which raw string a given
3315    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3316    /// method on the substrate primitive means every downstream consumer
3317    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3318    /// string surface reaches for exactly one typed dispatch — the
3319    /// resolver's accept-set migrates as a unit on any future axis
3320    /// addition.
3321    ///
3322    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3323    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3324    /// spread projection pattern the sibling per-`Caixa`
3325    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3326    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3327    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3328    /// string arm (the outer altitude's raw-string form; the inner
3329    /// altitude's parsed [`Duration`] form is the peer
3330    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3331    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3332    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3333    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3334    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3335    /// sub-family already carries — same "one typed dispatch on the
3336    /// substrate primitive, thin projections at each consumer"
3337    /// discipline extended onto the M2 supervisor-tree flat-spread
3338    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3339    /// to match the storage field's name and the per-[`SupervisorSpec`]
3340    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3341    /// method-name discipline verbatim; the accessor's identity maps
3342    /// onto the canonical OTP-shape supervision vocabulary the
3343    /// `:restart-window` field's docstring already carries.
3344    #[must_use]
3345    pub const fn restart_window(&self) -> Option<&str> {
3346        match &self.restart_window {
3347            Some(s) => Some(s.as_str()),
3348            None => None,
3349        }
3350    }
3351
3352    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3353    /// outer-composite OTP-appup-shaped per-prior-version migration-
3354    /// entry-list slice accessor every consumer of the top-level
3355    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3356    /// slice-view keys off — returns the author-declared `:upgrade-from`
3357    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3358    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3359    /// the raw `self.upgrade_from.as_slice()` field access borrows
3360    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3361    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3362    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3363    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3364    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3365    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3366    /// degenerates to an empty slice on that arm without any silent
3367    /// `None` collapse).
3368    ///
3369    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3370    /// migration block — the load-bearing container of every per-
3371    /// prior-`:versao` migration-instruction list the wasm-operator
3372    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3373    /// `.appup` per-prior-version `LoadModule | StateChange |
3374    /// SoftPurge | Purge | Restart` instruction algebra translated
3375    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3376    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3377    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3378    /// threads through a lifted per-entry accessor on the
3379    /// [`UpgradeFromEntry`] type: the
3380    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3381    /// version scalar accessor and the
3382    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3383    /// return per-entry instruction-list accessor (0137e5a). Every
3384    /// downstream consumer of the hot-upgrade path first passes
3385    /// through this outer accessor onto the slice and then dispatches
3386    /// per-entry through the inner accessors — the two-level dispatch
3387    /// means every per-`:upgrade-from` reader now routes through a
3388    /// typed dispatch on the substrate primitive at both altitudes.
3389    ///
3390    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3391    /// slot was accessed inline at production sites across three
3392    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3393    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3394    /// (caixa-core/src/manifest.rs, which drives the
3395    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3396    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3397    /// gate reads), the [`crate::StandardLayout::verify`] per-
3398    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3399    /// layout.rs, which fans onto the
3400    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3401    /// cross-entry duplicate gate, the
3402    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3403    /// SemVer-precedence cross-slot gate, the
3404    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3405    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3406    /// gate, and the per-instruction script-path existence-probe walk
3407    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3408    /// resolve every declared migration script against the layout
3409    /// root), and the [`crate::render::servico_m2_overlay`] per-
3410    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3411    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3412    /// projection (caixa-core/src/render.rs, which drives the
3413    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3414    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3415    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3416    /// A future extension of the outer `:upgrade-from` axis (a per-
3417    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3418    /// resolves at admission time so a cluster-specific migration
3419    /// policy can tighten a caixa-declared step without re-authoring
3420    /// the `caixa.lisp`, promotion of the plain
3421    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3422    /// partition once runtime-resolved hot-upgrade instructions land,
3423    /// per-entry priority annotation once multi-strategy fan-out
3424    /// lands) would have had to be threaded through all six open-
3425    /// coded copies in lockstep or one consumer would silently
3426    /// disagree with the peers on which upgrade slice a given Caixa
3427    /// resolves to — a six-consumer split at the enumerator, the
3428    /// three-stage validate pass, the script-path probe walk, and the
3429    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3430    /// field naming the upgrade-drift root cause. Lifting the
3431    /// resolution rule to a typed method on the substrate primitive
3432    /// means every downstream consumer of the caixa's per-`Caixa`
3433    /// OTP-appup outer-slice surface reaches for exactly one typed
3434    /// dispatch — the resolver's accept-set migrates as a unit on any
3435    /// future axis addition.
3436    ///
3437    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3438    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3439    /// outer-`Caixa` `&[Composite]` composite-slice projection
3440    /// pattern the sibling `:children`
3441    /// [`crate::supervisor::ChildSpec`] / `:membros`
3442    /// [`crate::aplicacao::Membro`] / `:contratos`
3443    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3444    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3445    /// `Option<&Composite>` composite-reference family the sibling
3446    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3447    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3448    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3449    /// `Option<&Composite>` altitude, extended here to the outer-
3450    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3451    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3452    /// (0137e5a) — same "one typed dispatch on the substrate
3453    /// primitive, thin projections at each consumer" discipline
3454    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3455    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3456    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3457    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3458    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3459    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3460    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3461    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3462    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3463    /// slice" projection pattern onto the sibling M2 typed-composite-
3464    /// element axis (`UpgradeFromEntry` composite, matching the
3465    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3466    /// different altitude).
3467    ///
3468    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3469    /// because every downstream consumer of the hot-upgrade list
3470    /// treats it as a read-only sequence — the slice-view is the
3471    /// narrowest borrow that supports every present + roadmapped
3472    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3473    /// serialization through
3474    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3475    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3476    /// the typed view reaches for (the storage-side `Vec` remains
3477    /// reachable through the `pub upgrade_from` field for the
3478    /// mutation-carrying serde round-trip and per-test fixture-
3479    /// mutation paths). Named `upgrade_from()` to match the storage
3480    /// field's `snake_case` name; the kebab-case author-surface tag
3481    /// `:upgrade-from` is the same axis after tatara-lisp's
3482    /// kebab↔snake fold and the accessor's identity maps onto the
3483    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3484    /// already carries.
3485    #[must_use]
3486    pub const fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3487        self.upgrade_from.as_slice()
3488    }
3489
3490    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3491    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3492    /// slice accessor every consumer of the top-level manifest's per-
3493    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3494    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3495    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3496    /// the same backing buffer the raw `self.children.as_slice()` field
3497    /// access borrows from. Empty-slice-carrying (the "no static children
3498    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3499    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3500    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3501    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3502    /// on those arms without any silent `None` collapse).
3503    ///
3504    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3505    /// static-child list — the load-bearing container of every per-
3506    /// child `{caixa, versao, restart}` triple the wasm-operator's
3507    /// hierarchical reconciler dispatches on at supervisor-tree
3508    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3509    /// static-child list translated onto pleme-io's typed
3510    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3511    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3512    /// dispatch fans on). Every per-child axis threads through a lifted
3513    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3514    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3515    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3516    /// version-requirement scalar accessor, and the
3517    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3518    /// per-child post-exit restart-decision-policy discriminant
3519    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3520    /// tree path first passes through this outer accessor onto the
3521    /// slice and then dispatches per-child through the inner accessors
3522    /// — the two-level dispatch means every per-`:children` reader now
3523    /// routes through a typed dispatch on the substrate primitive at
3524    /// both altitudes.
3525    ///
3526    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3527    /// accessed inline at three production sites across two files —
3528    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3529    /// declared-slot enumerator's `!self.children.is_empty()` presence
3530    /// probe (caixa-core/src/manifest.rs, which drives the
3531    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3532    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3533    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3534    /// per-supervisor typed-view composer's `self.children.clone()`
3535    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3536    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3537    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3538    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3539    /// `:children :caixa` self-parent refusal probe's
3540    /// `&caixa.children`-borrowed
3541    /// [`crate::supervisor::validate_no_self_supervision`] input
3542    /// (caixa-core/src/layout.rs, which pins the "no child names the
3543    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3544    /// extension of the outer `:children` axis (a per-cluster
3545    /// `:children-overrides` overlay the wasm-engine operator resolves
3546    /// at admission time so a cluster-specific child-set can tighten
3547    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3548    /// promotion of the plain `Vec<ChildSpec>` to a richer
3549    /// `{static, dynamic}` partition once Erlang/OTP's
3550    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3551    /// axis, per-child priority annotation once multi-strategy fan-out
3552    /// lands) would have had to be threaded through all three open-
3553    /// coded copies in lockstep or one consumer would silently
3554    /// disagree with the peers on which child slice a given Caixa
3555    /// resolves to — the enumerator's presence probe reading the raw
3556    /// slot while the peer view-composer's fold-in path read an
3557    /// operator-resolved slot would silently split the paired
3558    /// declared-slot enumerator and typed-view composition, and the
3559    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3560    /// refusal probe reading a third borrow would silently drift the
3561    /// cross-slot coherence gate's traversal input from the two peers,
3562    /// a three-consumer split at the enumerator, the view composer,
3563    /// and the self-parent gate far from the source `caixa.lisp` with
3564    /// no field naming the child-set-drift root cause. Lifting the
3565    /// resolution rule to a typed method on the substrate primitive
3566    /// means every downstream consumer of the caixa's per-`Caixa`
3567    /// OTP-supervisor outer-slice surface reaches for exactly one
3568    /// typed dispatch — the resolver's accept-set migrates as a unit
3569    /// on any future axis addition.
3570    ///
3571    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3572    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3573    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3574    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3575    /// at the outer altitude of the closed inner-`SupervisorSpec`
3576    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3577    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3578    /// borrow-shared" outer-accessor discipline extended onto the
3579    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3580    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3581    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3582    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3583    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3584    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3585    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3586    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3587    /// M2 typed-composite-element axis
3588    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3589    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3590    /// different altitude).
3591    ///
3592    /// Returns `&[crate::supervisor::ChildSpec]` (not
3593    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3594    /// child list treats it as a read-only sequence — the slice-view
3595    /// is the narrowest borrow that supports every present +
3596    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3597    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3598    /// input, `serde` slice-serialization) without leaking the backing
3599    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3600    /// reaches for (the storage-side `Vec` remains reachable through
3601    /// the `pub children` field for the mutation-carrying serde round-
3602    /// trip and per-test fixture-mutation paths, including the
3603    /// [`Self::supervisor_view`] fold-in path that clones the slot
3604    /// into the typed view). Named `children()` to match the storage
3605    /// field's name verbatim and the tatara-lisp author-surface term
3606    /// (`:children`) the field's own docstring already carries; the
3607    /// accessor's identity maps onto the canonical OTP supervision
3608    /// vocabulary the [`Caixa::children`] field's docstring already
3609    /// reaches for ("Static children of a supervisor").
3610    #[must_use]
3611    pub const fn children(&self) -> &[crate::supervisor::ChildSpec] {
3612        self.children.as_slice()
3613    }
3614
3615    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3616    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3617    /// accessor every consumer of the top-level manifest's per-Aplicacao
3618    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3619    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3620    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3621    /// same backing buffer the raw `self.membros.as_slice()` field access
3622    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3623    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3624    /// and every partially-authored Aplicacao carries before the
3625    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3626    /// `&[Membro]` degenerates to an empty slice on those arms without any
3627    /// silent `None` collapse).
3628    ///
3629    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3630    /// per-Aplicacao member list — the load-bearing container of every
3631    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3632    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3633    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3634    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3635    /// the `:entrada :para` external-gateway destination validates
3636    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3637    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3638    /// threads through a lifted per-entry accessor on the
3639    /// [`crate::aplicacao::Membro`] type: the
3640    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3641    /// identity scalar accessor (4a32abf) and the peer
3642    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3643    /// version-requirement scalar accessor (a40b0e3). Every downstream
3644    /// consumer of the mesh-graph path first passes through this outer
3645    /// accessor onto the slice and then dispatches per-member through
3646    /// the inner accessors — the two-level dispatch means every per-
3647    /// `:membros` reader now routes through a typed dispatch on the
3648    /// substrate primitive at both altitudes.
3649    ///
3650    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3651    /// inline at three production sites across two files — the
3652    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3653    /// enumerator's `!self.membros.is_empty()` presence probe
3654    /// (caixa-core/src/manifest.rs, which drives the
3655    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3656    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3657    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3658    /// composer's `self.membros.clone()` per-member fold-in path
3659    /// (caixa-core/src/manifest.rs, which materializes the typed
3660    /// [`crate::aplicacao::AplicacaoSpec`] view every
3661    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3662    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3663    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3664    /// [`crate::aplicacao::validate_no_self_membership`] input
3665    /// (caixa-core/src/layout.rs, which pins the "no member names the
3666    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3667    /// extension of the outer `:membros` axis (a per-cluster
3668    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3669    /// admission time so a cluster-specific member-set can tighten a
3670    /// caixa-declared list without re-authoring the `caixa.lisp`,
3671    /// promotion of the plain `Vec<Membro>` to a richer
3672    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3673    /// members land as a typed axis, per-member priority annotation once
3674    /// multi-strategy fan-out lands) would have had to be threaded
3675    /// through all three open-coded copies in lockstep or one consumer
3676    /// would silently disagree with the peers on which member slice a
3677    /// given Caixa resolves to — the enumerator's presence probe reading
3678    /// the raw slot while the peer view-composer's fold-in path read an
3679    /// operator-resolved slot would silently split the paired
3680    /// declared-slot enumerator and typed-view composition, and the
3681    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3682    /// refusal probe reading a third borrow would silently drift the
3683    /// cross-slot coherence gate's traversal input from the two peers, a
3684    /// three-consumer split at the enumerator, the view composer, and
3685    /// the self-membership gate far from the source `caixa.lisp` with no
3686    /// field naming the member-set-drift root cause. Lifting the
3687    /// resolution rule to a typed method on the substrate primitive
3688    /// means every downstream consumer of the caixa's per-`Caixa`
3689    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3690    /// typed dispatch — the resolver's accept-set migrates as a unit on
3691    /// any future axis addition.
3692    ///
3693    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3694    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3695    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3696    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3697    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3698    /// altitude. Peer at the outer altitude of the closed inner-
3699    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3700    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3701    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3702    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3703    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3704    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3705    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3706    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3707    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3708    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3709    /// pattern onto the sibling M3 typed-composite-element axis
3710    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3711    /// [`crate::AplicacaoSpec::membros`] element type at a different
3712    /// altitude).
3713    ///
3714    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3715    /// because every downstream consumer of the member list treats it
3716    /// as a read-only sequence — the slice-view is the narrowest borrow
3717    /// that supports every present + roadmapped consumer (`.iter()`,
3718    /// `.len()`, `.is_empty()`, the
3719    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3720    /// input, `serde` slice-serialization) without leaking the backing
3721    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3722    /// reaches for (the storage-side `Vec` remains reachable through the
3723    /// `pub membros` field for the mutation-carrying serde round-trip
3724    /// and per-test fixture-mutation paths, including the
3725    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3726    /// the typed view). Named `membros()` to match the storage field's
3727    /// name verbatim and the tatara-lisp author-surface term
3728    /// (`:membros`) the field's own docstring already carries; the
3729    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3730    /// vocabulary the [`Caixa::membros`] field's docstring already
3731    /// reaches for ("Member Servicos that make up this Aplicacao").
3732    #[must_use]
3733    pub const fn membros(&self) -> &[crate::aplicacao::Membro] {
3734        self.membros.as_slice()
3735    }
3736
3737    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3738    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3739    /// inter-Servico contract-list slice accessor every consumer of the
3740    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3741    /// slice-view keys off — returns the author-declared `:contratos`
3742    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3743    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3744    /// backing buffer the raw `self.contratos.as_slice()` field access
3745    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3746    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3747    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3748    /// single member with no inter-Servico edge carries; the returned
3749    /// `&[WitContract]` degenerates to an empty slice on those arms
3750    /// without any silent `None` collapse).
3751    ///
3752    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3753    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3754    /// container of every per-edge `{de, para, wit, endpoint | subject |
3755    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3756    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3757    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3758    /// adjacency-list seed dispatch on at mesh-artifact materialization
3759    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3760    /// `:membros` vertex set resolves against, closed by the
3761    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3762    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3763    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3764    /// per-edge axis threads through a lifted per-entry accessor on the
3765    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3766    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3767    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3768    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3769    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3770    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3771    /// and the WIT-world discriminant. Every downstream consumer of the
3772    /// mesh-graph edge path first passes through this outer accessor
3773    /// onto the slice and then dispatches per-contract through the
3774    /// inner accessors — the two-level dispatch means every
3775    /// per-`:contratos` reader now routes through a typed dispatch on
3776    /// the substrate primitive at both altitudes.
3777    ///
3778    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3779    /// accessed inline at two production sites in
3780    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3781    /// mesh-slot declared-slot enumerator's
3782    /// `!self.contratos.is_empty()` presence probe (which drives the
3783    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3784    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3785    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3786    /// typed-view composer's `self.contratos.clone()` per-contract
3787    /// fold-in path (which materializes the typed
3788    /// [`crate::aplicacao::AplicacaoSpec`] view every
3789    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3790    /// downstream `caixa-mesh` renderer dispatches on). A future
3791    /// extension of the outer `:contratos` axis (a per-cluster
3792    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3793    /// at admission time so a cluster-specific edge-set can tighten a
3794    /// caixa-declared list without re-authoring the `caixa.lisp`,
3795    /// promotion of the plain `Vec<WitContract>` to a richer
3796    /// `{static, dynamic}` partition once runtime-resolved contract
3797    /// edges land, per-edge policy annotation once the M4 per-edge
3798    /// policy overlay axis lands) would have had to be threaded through
3799    /// both open-coded copies in lockstep or one consumer would
3800    /// silently disagree with the peer on which edge slice a given
3801    /// Caixa resolves to — the enumerator's presence probe reading the
3802    /// raw slot while the peer view-composer's fold-in path read an
3803    /// operator-resolved slot would silently split the paired
3804    /// declared-slot enumerator and typed-view composition, a
3805    /// two-consumer split at the enumerator and the view composer far
3806    /// from the source `caixa.lisp` with no field naming the edge-set-
3807    /// drift root cause. Lifting the resolution rule to a typed method
3808    /// on the substrate primitive means every downstream consumer of
3809    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3810    /// reaches for exactly one typed dispatch — the resolver's
3811    /// accept-set migrates as a unit on any future axis addition.
3812    ///
3813    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3814    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3815    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3816    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3817    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3818    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3819    /// mesh-slot arm of the composite-slice sub-family the sibling
3820    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3821    /// Peer at the outer altitude of the closed inner-
3822    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3823    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3824    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3825    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3826    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3827    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3828    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3829    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3830    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3831    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3832    /// pattern onto the sibling M3 typed-composite-element axis
3833    /// ([`crate::aplicacao::WitContract`] composite, matching the
3834    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3835    /// different altitude).
3836    ///
3837    /// Returns `&[crate::aplicacao::WitContract]` (not
3838    /// `&Vec<WitContract>`) because every downstream consumer of the
3839    /// contract list treats it as a read-only sequence — the slice-view
3840    /// is the narrowest borrow that supports every present + roadmapped
3841    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3842    /// discriminant dispatch, `serde` slice-serialization) without
3843    /// leaking the backing `Vec`'s grow/push/reserve surface no
3844    /// consumer of the typed view reaches for (the storage-side `Vec`
3845    /// remains reachable through the `pub contratos` field for the
3846    /// mutation-carrying serde round-trip and per-test fixture-mutation
3847    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3848    /// clones the slot into the typed view). Named `contratos()` to
3849    /// match the storage field's name verbatim and the tatara-lisp
3850    /// author-surface term (`:contratos`) the field's own docstring
3851    /// already carries; the accessor's identity maps onto the canonical
3852    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3853    /// docstring already reaches for ("WIT-typed inter-Servico
3854    /// contracts").
3855    #[must_use]
3856    pub const fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3857        self.contratos.as_slice()
3858    }
3859
3860    /// Compose the Aplicacao-related flat slots into a single typed
3861    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3862    /// downstream renderer consumption. Returns `None` when the
3863    /// caixa isn't a `:kind Aplicacao`.
3864    #[must_use]
3865    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3866        if !self.kind().is_aplicacao() {
3867            return None;
3868        }
3869        Some(crate::aplicacao::AplicacaoSpec {
3870            membros: self.membros().to_vec(),
3871            contratos: self.contratos().to_vec(),
3872            politicas: self.politicas().cloned().unwrap_or_default(),
3873            placement: self.placement().cloned().unwrap_or_default(),
3874            entrada: self.entrada().cloned(),
3875        })
3876    }
3877
3878    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3879    /// *declares* a value on, in canonical declaration order
3880    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3881    /// `:entrada`). A slot counts as declared when its backing field
3882    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3883    ///
3884    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3885    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3886    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3887    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3888    /// caixa-flux / caixa-helm renderers only emit them for an
3889    /// Aplicacao. On any *other* kind a declared mesh slot is the
3890    /// manifest field's documented "ignored otherwise" (see the
3891    /// `:membros` … `:entrada` field docs): it silently passes
3892    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3893    /// rendered — far from the source caixa.lisp.
3894    /// [`crate::StandardLayout::verify`] consults this to reject that
3895    /// silent-drop at caixa-build time
3896    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3897    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3898    /// a slot foreign to the kind is a build error, not a silent drop.
3899    ///
3900    /// Lifted as a typed method (rather than an inline disjunction at
3901    /// the verify call site) so the mesh-slot set lives in one place —
3902    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3903    /// overlay, distributed-app takeover config) is one push here, and
3904    /// every consumer reaching for "which mesh slots are set" (the
3905    /// verify gate, a future `feira lint` kind-coherence advisory)
3906    /// inherits the canonical order without rolling its own.
3907    ///
3908    /// Each per-arm kebab-case label is routed through the peer
3909    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3910    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3911    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3912    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3913    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3914    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3915    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3916    /// kebab-case label + renderer-side artifact key) route through one
3917    /// canonical declaration per arm — same discipline the peer
3918    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3919    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3920    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3921    /// axis, extended here to close the M3 mesh-slot author-facing-label
3922    /// axis so both altitudes of the typed-slot algebra
3923    /// (per-Servico M2 + per-Aplicacao M3) share the same
3924    /// "one canonical byte-string per arm, next to the axis" discipline.
3925    #[must_use]
3926    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3927        let mut slots = Vec::new();
3928        if !self.membros().is_empty() {
3929            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3930        }
3931        if !self.contratos().is_empty() {
3932            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3933        }
3934        if self.politicas().is_some() {
3935            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3936        }
3937        if self.placement().is_some() {
3938            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3939        }
3940        if self.entrada().is_some() {
3941            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3942        }
3943        slots
3944    }
3945
3946    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3947    /// caixa *declares* a value on, in canonical declaration order
3948    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3949    /// `:children`). A slot counts as declared when its backing field
3950    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3951    ///
3952    /// The supervisor-tree slots compose the typed OTP supervisor of a
3953    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3954    /// `:children` field docs above). [`Self::supervisor_view`] only
3955    /// folds them into a validatable [`SupervisorSpec`] when the kind
3956    /// matches (returns `None` otherwise), and the wasm-operator's
3957    /// hierarchical reconciler only consumes them for a Supervisor. On
3958    /// any *other* kind a declared supervisor slot is the manifest
3959    /// field's documented "ignored otherwise" (see the `:estrategia` …
3960    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3961    /// and then vanishes — never validated, never reconciled — far from
3962    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3963    /// this to reject that silent-drop at caixa-build time
3964    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3965    /// exact mirror of the [`Self::declared_mesh_slots`] /
3966    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3967    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3968    /// error, not a silent drop.
3969    #[must_use]
3970    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3971        let mut slots = Vec::new();
3972        if self.estrategia().is_some() {
3973            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3974        }
3975        if self.max_restarts().is_some() {
3976            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3977        }
3978        if self.restart_window().is_some() {
3979            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3980        }
3981        if !self.children().is_empty() {
3982            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3983        }
3984        slots
3985    }
3986
3987    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3988    /// caixa *declares* a value on, in canonical declaration order
3989    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3990    /// declared when its backing field carries a value — a `Some(...)`,
3991    /// or a non-empty `Vec`.
3992    ///
3993    /// The M2 slots configure the runtime of a long-running wasm
3994    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3995    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3996    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3997    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3998    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3999    /// emit these slots for a Servico; on any *other* kind a declared M2
4000    /// slot is the manifest field's documented "ignored otherwise": its
4001    /// well-formedness is checked by [`crate::StandardLayout::verify`]
4002    /// but the value is never rendered into a chart / programs.yaml entry
4003    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
4004    /// vanishes, far from the source caixa.lisp.
4005    /// [`crate::StandardLayout::verify`] consults this to reject that
4006    /// silent-drop at caixa-build time
4007    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
4008    /// mirror of the [`Self::declared_mesh_slots`] /
4009    /// [`Self::declared_supervisor_slots`] gates on the peer
4010    /// kind-exclusive slot sets: a slot foreign to the kind is a build
4011    /// error, not a silent drop.
4012    ///
4013    /// Each per-arm kebab-case label is routed through the peer
4014    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
4015    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
4016    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
4017    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
4018    /// both halves of the M2 top-level slot's dual axis (author-facing
4019    /// kebab-case label + renderer-side camelCase overlay-container wire
4020    /// key) route through one canonical declaration per arm — same
4021    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
4022    /// author-label consts (889dc18) establish on the sibling
4023    /// per-callback axis inside the `:behavior` overlay block.
4024    #[must_use]
4025    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
4026        let mut slots = Vec::new();
4027        if self.limits().is_some() {
4028            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
4029        }
4030        if self.behavior().is_some() {
4031            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
4032        }
4033        if !self.upgrade_from().is_empty() {
4034            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
4035        }
4036        slots
4037    }
4038
4039    /// The kebab-case `:slot` tags of every code-surface slot this caixa
4040    /// declares a value on that its [`CaixaKind`] doesn't natively own,
4041    /// in canonical declaration order (`:exe` → `:servicos`). A
4042    /// code-surface slot is owned by exactly one kind: `:exe` by
4043    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
4044    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
4045    /// `ComputeUnit` daemon surface).
4046    ///
4047    /// Each is silently ignored when declared on the wrong kind: the
4048    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
4049    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
4050    /// code-running kind a declared `:exe` / `:servicos` is the manifest
4051    /// field's documented "ignored otherwise" — its path is checked for
4052    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
4053    /// (which run after [`Caixa::from_lisp`]), but the value is never
4054    /// rendered into a build target or programs.yaml entry. It silently
4055    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
4056    /// caixa.lisp, with no field naming which slot is foreign.
4057    ///
4058    /// [`crate::StandardLayout::verify`] consults this to reject that
4059    /// silent-drop at caixa-build time
4060    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
4061    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
4062    /// gates ([`Self::declared_servico_slots`] /
4063    /// [`Self::declared_supervisor_slots`] /
4064    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
4065    /// axis to be closed on the typed surface. The Supervisor /
4066    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
4067    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
4068    /// diagnostics — they fire ahead of this gate on the same `verify`
4069    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
4070    /// and this method is moot. For Biblioteca / Binario / Servico, this
4071    /// gate fires when a code-running kind declares another code-running
4072    /// kind's exclusive code surface.
4073    ///
4074    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
4075    /// may legitimately ship a `lib/` helper that the underlying
4076    /// substrate (the nix flake for Binario, the wasm component build
4077    /// for Servico) bundles into its build, so the slot's
4078    /// declared-on-wrong-kind cardinality isn't a structural error on
4079    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
4080    /// is the native case (the slot's owning kind). Supervisor /
4081    /// Aplicacao declaring `:bibliotecas` is gated upstream by
4082    /// [`crate::LayoutError::SupervisorOwnsCode`] /
4083    /// [`crate::LayoutError::AplicacaoOwnsCode`].
4084    ///
4085    /// Lifted as a typed method (rather than an inline disjunction at
4086    /// the verify call site) so the foreign-code-slot set lives in one
4087    /// place — a future kind that gains its own code-surface slot is
4088    /// one push here, and every consumer reaching for "which code
4089    /// surfaces are foreign to this kind" (the verify gate, a future
4090    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
4091    /// per-caixa build-target classifier) inherits the canonical order
4092    /// without rolling its own.
4093    #[must_use]
4094    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
4095        let mut slots = Vec::new();
4096        if !self.exe().is_empty() && !self.kind().requires_exe() {
4097            slots.push(":exe");
4098        }
4099        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
4100            slots.push(":servicos");
4101        }
4102        slots
4103    }
4104
4105    /// Validate every entry of `:deps` and `:deps-dev` through
4106    /// [`Dep::validate`] — closing the parity loop with the per-axis
4107    /// `:versao` gates already wired into the typed-graph
4108    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
4109    /// 9888b13) and typed supervisor tree
4110    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
4111    ///
4112    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
4113    /// were the only `:versao` axes still untyped past
4114    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
4115    /// as a String without parsing it, so a malformed-but-non-empty
4116    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4117    /// silently passed parse and the `semver::Error` surfaced at
4118    /// lacre-resolve time, far from the source caixa.lisp, with no
4119    /// field naming which `:deps` entry carried the typo. Lifting the
4120    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4121    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4122    /// every requirement string past `validate_deps` is round-trippable
4123    /// through [`crate::parse_requirement`] without re-checking at the
4124    /// resolver layer.
4125    ///
4126    /// Both lists run through the same per-entry validator so a typo
4127    /// in `:deps-dev` surfaces with the same diagnostic as one in
4128    /// `:deps` — neither axis is a second-class citizen of the typed
4129    /// surface.
4130    ///
4131    /// Within each list, [`DepError::DuplicateNome`] closes the
4132    /// set-not-multiset discipline on the `:nome` axis: two entries
4133    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4134    /// triples that the caixa-resolver's lacre pipeline collapses to one
4135    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4136    /// silently overwrites the first at `concrete_versao`-resolve time
4137    /// (the same "second wins / one silently overwrites the other"
4138    /// shape the peer typed-graph duplicate gates already close on every
4139    /// other Vec-shaped authoring surface that keys by name). The
4140    /// duplicate check fires per-list and runs *after* each per-entry
4141    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4142    /// surfaces its narrower per-entry diagnostic
4143    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4144    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4145    /// diagnostic — the canonical "per-entry shape before cross-entry
4146    /// uniqueness" precedence the peer `:children :caixa`
4147    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4148    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4149    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4150    /// ([`crate::AplicacaoSpec::validate_placement`]),
4151    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4152    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4153    /// and the within-`:upgrade-from`-entry per-instruction-class
4154    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4155    /// [`crate::UpgradeError::DuplicateStateChange`],
4156    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4157    ///
4158    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4159    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4160    /// same name in both tables (the dev table's pin overrides the
4161    /// runtime table's pin in test/dev contexts), and caixa's surface
4162    /// mirrors that convention until a deliberate choice retires the
4163    /// override pattern. Only within-list duplicates are structurally
4164    /// incoherent — those are what this gate closes.
4165    ///
4166    /// Compound per-`Caixa` entry gate on the dep-graph axis: folds the
4167    /// two standalone dep-list validators — the per-entry + within-list
4168    /// duplicate-`:nome` walk (the [`Dep::validate`] +
4169    /// [`crate::render::insert_first_seen`] cascade this method opened
4170    /// on) and the cross-slot self-edge gate
4171    /// ([`crate::dep::validate_no_self_dep`]) — onto one substrate
4172    /// primitive on [`Caixa`]. The two arms run in the same canonical
4173    /// order the layout pipeline
4174    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4175    /// author-time gate) has always sequenced them (per-entry +
4176    /// cross-entry duplicate → cross-slot self-edge), so the fold is
4177    /// byte-for-byte equivalent to the pre-fold two-block cascade at
4178    /// that call site (pinned by the paired
4179    /// `validate_deps_folds_per_entry_arm_matches_gate` /
4180    /// `validate_deps_folds_self_edge_arm_matches_gate` equivalence
4181    /// pins and the `validate_deps_per_entry_arm_fires_before_self_edge_arm`
4182    /// ordering pin). Self-contained on `&self` — resolves its three
4183    /// inputs ([`Self::deps`], [`Self::deps_dev`], [`Self::nome`])
4184    /// through the substrate primitives' own accessor family, the same
4185    /// posture every peer per-slot compound gate
4186    /// ([`crate::AplicacaoSpec::validate_contratos`],
4187    /// [`crate::MeshPolicy::validate`],
4188    /// [`crate::SupervisorSpec::validate_children`],
4189    /// [`Self::validate_upgrade_from`]) already carries.
4190    ///
4191    /// Prior to this lift [`crate::dep::validate_no_self_dep`] lived
4192    /// only open-coded at the layout wire-up site
4193    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs)
4194    /// as a standalone two-arg dispatch immediately after this method's
4195    /// per-entry + cross-entry walk, both wrapped through the same
4196    /// [`crate::LayoutError::DepsViolation`] envelope: every future
4197    /// consumer that wanted to gate the dep-graph as a whole — the
4198    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4199    /// per-CR admission webhook re-checking `:deps` / `:deps-dev` after
4200    /// a per-entry patch, a future `feira validate --deps` per-caixa
4201    /// admission verb, a per-`:deps` overlay resolver a per-cluster
4202    /// overlay lift would materialize (each the deferred consumer this
4203    /// method's peer [`Self::deps`] / [`Self::deps_dev`] accessors'
4204    /// docstrings already name) — was structurally forced to either
4205    /// re-inline the two-dispatch cascade in lockstep with the layout
4206    /// wire-up (the duplication the PRIME DIRECTIVE names as a bug) or
4207    /// call the whole [`crate::layout::StandardLayout::verify`] pipeline
4208    /// and pay every peer per-Caixa gate to re-check one slot. Post-fold
4209    /// each such consumer reaches the two-arm compound gate through one
4210    /// call on the substrate primitive.
4211    pub fn validate_deps(&self) -> Result<(), DepError> {
4212        for &list in crate::dep::DepList::ALL {
4213            let mut seen = std::collections::HashSet::new();
4214            for dep in self.deps_of(list) {
4215                dep.validate()?;
4216                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4217                    DepError::DuplicateNome {
4218                        nome: dep.nome().to_string(),
4219                        list: list.as_str(),
4220                    }
4221                })?;
4222            }
4223        }
4224        crate::dep::validate_no_self_dep(self.deps(), self.deps_dev(), self.nome())?;
4225        Ok(())
4226    }
4227
4228    /// Run a per-slot typed validator on `self` and, on the per-arm
4229    /// parser-side error arm, thread the error into a paired
4230    /// [`crate::LayoutError`] wrap under `self.nome()`. Substrate
4231    /// primitive folding the 18 self-similar layout-pipeline wire-up
4232    /// sites at [`crate::layout::StandardLayout::verify`] that carry
4233    /// the identical
4234    /// `caixa.validate_<slot>().map_err(|err| crate::LayoutError::<slot>_violation(caixa, err))?;`
4235    /// cascade onto one dispatch. Each of the eighteen sites (`:nome`,
4236    /// `:nome`-chart-name-budget, `:versao`, `:deps`, `:etiquetas`,
4237    /// `:autores`, `:repositorio`, `:descricao`, `:licenca`, `:edicao`,
4238    /// `:bibliotecas`/`:exe`/`:servicos` code-path shape, `:limits`,
4239    /// `:behavior`, `:upgrade-from`, `:restart-window`, per-Supervisor
4240    /// shape, per-Aplicacao shape, per-Acao shape) carried the same
4241    /// four-line "run a per-slot typed validator on `caixa` and, on the
4242    /// per-arm parser-side error arm, thread it into the paired
4243    /// [`crate::LayoutError`] one-slot envelope through the substrate-
4244    /// canonical `layout_violation_ctors!` family (131ca0d)" cascade,
4245    /// differing only in the two names bound at each site — the
4246    /// validator (`Caixa::validate_deps` / `validate_nome` / ...) and
4247    /// the paired ctor (`LayoutError::deps_violation` / ...). Eighteen
4248    /// consumers, one identical shape, one substrate primitive on
4249    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
4250    /// a bug — on the second half of the per-slot cascade the peer
4251    /// substrate primitives on the [`crate::LayoutError`]-wrap side
4252    /// (the `layout_violation_ctors!` macro 131ca0d, the
4253    /// `layout_slot_kind_ctors!` macro 0419438, the `layout_nome_only_ctors!`
4254    /// macro 3fe3dd7, the [`crate::LayoutError::missing_entry`] ctor
4255    /// 1b09f9d, the [`crate::layout::StandardLayout::probe_declared_entry`]
4256    /// primitive fda1e35) each closed on their sibling envelopes; the
4257    /// first half of the cascade (the per-slot compound gates
4258    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
4259    /// baa4688, [`Self::validate_behavior`] 0d2877a,
4260    /// [`Self::validate_upgrade_from`] d6801df,
4261    /// [`Self::validate_aplicacao_shape`] 949a7a0,
4262    /// [`Self::validate_supervisor_shape`] 4c70105,
4263    /// [`Self::validate_acao_shape`] 5d6df54,
4264    /// [`Self::validate_kind_slot_coherence`] f0d286e,
4265    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
4266    /// [`Self::validate_ci_kind_coherence`] 9b55beb,
4267    /// [`Self::validate_required_kind_slot`] 9c385d8) each closed on
4268    /// their per-slot compound gates.
4269    ///
4270    /// Composes the [`crate::layout::LayoutError`] wrap and the per-slot
4271    /// typed validator through two typed callables: `gate` runs on
4272    /// `self` and yields a per-slot error `E`; on the `Err(E)` arm
4273    /// `wrap` re-wraps that error under `self` into a
4274    /// [`crate::layout::LayoutError`]. The `Ok(())` arm passes through
4275    /// verbatim as the fold's identity element — byte-equal to the
4276    /// pre-lift `Result::map_err` short-circuit at the `?;` marker
4277    /// every wire-up site formerly carried. Every future consumer that
4278    /// wants to run one of the per-slot gates and thread its error
4279    /// through the layout wrap (the deferred
4280    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4281    /// webhook per-slot re-check, a future `feira validate --<slot>`
4282    /// per-caixa admission verb, an overlay resolver re-running one
4283    /// gate after a per-slot patch) reaches the two-callable dispatch
4284    /// through one call rather than re-inlining the four-line cascade
4285    /// in lockstep with the pre-existing 18 wire-ups. The two callables
4286    /// reach the primitive as first-class type-checked references
4287    /// rather than the pre-lift `.map_err(|err| CTOR(caixa, err))`
4288    /// closure body — so a mismatch between the validator's `E` type
4289    /// and the ctor's `E` bound trips at the wire-up site (compile-
4290    /// time) rather than at the closure body (also compile-time, but
4291    /// with a diagnostic pointing at the closure expression rather
4292    /// than the two named callables).
4293    pub fn run_layout_gate<E, W>(
4294        &self,
4295        gate: impl FnOnce(&Caixa) -> Result<(), E>,
4296        wrap: W,
4297    ) -> Result<(), crate::LayoutError>
4298    where
4299        W: FnOnce(&Caixa, E) -> crate::LayoutError,
4300    {
4301        gate(self).map_err(|err| wrap(self, err))
4302    }
4303
4304    /// Run one arm of the cross-family kind ↔ owned-slot-family
4305    /// coherence cascade on `self`: on a caixa whose [`Self::kind`] does
4306    /// not own the typed-slot family named by `is_owner`, refuse when
4307    /// the paired `accumulator` reports any declared slot in that
4308    /// family; otherwise pass. Substrate primitive folding the three
4309    /// self-similar four-line
4310    /// `if !self.kind().is_<owner>() { let slots = self.declared_<family>_slots();
4311    /// if !slots.is_empty() { return Err(<wrap>(self, slots)); } }`
4312    /// arms at [`Self::validate_kind_slot_coherence`] onto one dispatch.
4313    /// Three consumers (M3 mesh — Aplicacao-owner, supervisor-tree —
4314    /// Supervisor-owner, M2 Servico-runtime — Servico-owner), one
4315    /// identical shape, one substrate primitive on [`Caixa`] closing
4316    /// the duplication the PRIME DIRECTIVE names as a bug on the
4317    /// outer kind-coherence arm shape — peer with the substrate
4318    /// primitives on the two adjacent halves of the same three-arm
4319    /// cascade the sibling [`Self::declared_mesh_slots`] /
4320    /// [`Self::declared_supervisor_slots`] /
4321    /// [`Self::declared_servico_slots`] accumulator family closes on
4322    /// the inner slot-set enumerator axis and the sibling
4323    /// [`crate::layout::layout_slot_kind_ctors!`] macro (0419438)
4324    /// closes on the inner wrap-envelope ctor axis. Each of the three
4325    /// [`Self::validate_kind_slot_coherence`] arms now reads through
4326    /// one call across every altitude of the per-arm cascade:
4327    /// one dispatch on this primitive for the outer guard shape, one
4328    /// dispatch on `Self::declared_<family>_slots` for the accumulator,
4329    /// one dispatch on `crate::LayoutError::<family>_on_non_<owner>`
4330    /// for the wrap ctor.
4331    ///
4332    /// Composes the outer owner-kind guard, the per-family accumulator,
4333    /// and the per-family wrap ctor through three typed callables:
4334    /// `is_owner` runs on `&self.kind()` (a `&CaixaKind` borrow so the
4335    /// `gen_platform::IsVariant`-derived `fn(&CaixaKind) -> bool`
4336    /// per-arm predicates — [`crate::CaixaKind::is_aplicacao`] /
4337    /// [`crate::CaixaKind::is_supervisor`] / [`crate::CaixaKind::is_servico`]
4338    /// — pass verbatim as function references), `accumulator` runs on
4339    /// `&self` and yields the
4340    /// per-family declared-slot list, and `wrap` runs on `(&self,
4341    /// Vec<&'static str>)` and yields the per-family
4342    /// [`crate::LayoutError`] wrap. The `is_owner` short-circuit fires
4343    /// before the accumulator dispatch (so the owner kind of each
4344    /// family passes without invoking `accumulator`, byte-equal to the
4345    /// pre-lift `if !self.kind().is_<owner>() { … }` outer guard's
4346    /// short-circuit — pinned by
4347    /// `run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator`),
4348    /// and the accumulator's `is_empty` short-circuit fires before the
4349    /// wrap dispatch (so a non-owner kind with no declared slot in that
4350    /// family passes without invoking `wrap`, byte-equal to the pre-lift
4351    /// `if !<slots>.is_empty() { … }` inner guard's short-circuit —
4352    /// pinned by
4353    /// `run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap`).
4354    /// The wrap ctor is `FnOnce(&Caixa, Vec<&'static str>) ->
4355    /// crate::LayoutError` — matching the [`crate::layout::layout_slot_kind_ctors!`]
4356    /// macro's per-variant `fn(&Caixa, Vec<&'static str>) -> LayoutError`
4357    /// substrate-canonical ctor shape verbatim, so
4358    /// [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
4359    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
4360    /// [`crate::LayoutError::servico_slots_on_non_servico`] pass as
4361    /// function references without a closure wrap. A mismatch between
4362    /// the ctor's signature and this bound trips at the wire-up site
4363    /// (compile-time) rather than at a closure body.
4364    ///
4365    /// The sibling [`crate::LayoutError::ForeignCodeSlot`] gate on the
4366    /// code-surface family sits outside this primitive because
4367    /// [`Self::declared_foreign_code_slots`] bakes the per-arm kind-
4368    /// check into the accumulator itself (each arm's
4369    /// `!self.kind().requires_<slot>()` guard fires inside the
4370    /// accumulator, not around it), so the code-surface arm carries no
4371    /// outer `is_owner`-shaped guard and its dispatch reads through
4372    /// [`Self::validate_foreign_code_kind_coherence`] verbatim without
4373    /// this primitive — the same posture the `_no_code_` /
4374    /// `_ci_kind_` coherence axes take on their respective per-arm
4375    /// shapes. The primitive here is specific to the "outer
4376    /// non-owner-kind guard + inner accumulator + inner emptiness
4377    /// guard + wrap" arm shape that fires three times in
4378    /// [`Self::validate_kind_slot_coherence`].
4379    ///
4380    /// Every future consumer that wants to gate one kind-owned slot
4381    /// family as a unit outside the composed cascade (the deferred
4382    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4383    /// webhook per-family re-check after a per-slot patch, a future
4384    /// `feira validate --<family>-coherence` per-caixa admission verb,
4385    /// a per-`Caixa` overlay resolver rejecting a kind-foreign patch
4386    /// on one family) reaches the four-line arm through one call
4387    /// rather than re-inlining the outer-guard + accumulator +
4388    /// emptiness-guard + wrap cascade in lockstep with the pre-existing
4389    /// three arms. Every future kind-owned typed-slot family (an
4390    /// `Actor`-owned per-virtual-actor grain slot the M5 Orleans-
4391    /// inspired kind reaches through, a per-Aplicacao overlay slot the
4392    /// M4 CR materializer consults) folds onto
4393    /// [`Self::validate_kind_slot_coherence`] as one additional
4394    /// dispatch on this primitive rather than a fourth open-coded
4395    /// four-line block.
4396    pub fn run_kind_owned_slot_family_gate<F, A, W>(
4397        &self,
4398        is_owner: F,
4399        accumulator: A,
4400        wrap: W,
4401    ) -> Result<(), crate::LayoutError>
4402    where
4403        F: FnOnce(&crate::CaixaKind) -> bool,
4404        A: FnOnce(&Caixa) -> Vec<&'static str>,
4405        W: FnOnce(&Caixa, Vec<&'static str>) -> crate::LayoutError,
4406    {
4407        if is_owner(&self.kind()) {
4408            return Ok(());
4409        }
4410        let slots = accumulator(self);
4411        if slots.is_empty() {
4412            return Ok(());
4413        }
4414        Err(wrap(self, slots))
4415    }
4416
4417    /// Reject `:nome` values the K8s apiserver would refuse at admission
4418    /// time. The top-level Caixa identity flows directly into every
4419    /// substrate-side artifact's `metadata.name` axis: the
4420    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4421    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4422    /// aggregator keys ComputeUnit derivation off
4423    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4424    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4425    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4426    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4427    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4428    /// ([`caixa-mesh::lib::cilium_network_policies`],
4429    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4430    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4431    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4432    /// schema enforces the DNS-1123 label rule on admission; a
4433    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4434    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4435    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4436    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4437    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4438    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4439    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4440    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4441    /// Invalid value` rejection on whichever derived artifact admitted
4442    /// first, far from the source `caixa.lisp` and without any field
4443    /// naming the offending `:nome`.
4444    ///
4445    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4446    /// substrate-side predicate the per-axis name gates already share:
4447    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4448    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4449    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4450    /// diagnostic is self-locating (the offending `:nome` is named
4451    /// verbatim) and the author can grep their `caixa.lisp` for
4452    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4453    /// every per-axis sibling gate already exposes
4454    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4455    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4456    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4457    ///
4458    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4459    /// derive macro stores the raw String) is gated by the narrower
4460    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4461    /// consulted, mirroring the empty-first cascade every per-axis
4462    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4463    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4464    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4465        // Routes through the shared
4466        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4467        // name axes each land on so drift between the eight axes'
4468        // accepted DNS-1123-label sets is structurally impossible.
4469        let nome = self.nome();
4470        crate::render::require_valid_dns_1123_label(
4471            nome,
4472            || ManifestError::NomeEmpty,
4473            |reason| ManifestError::NomeInvalid {
4474                nome: nome.to_string(),
4475                reason,
4476            },
4477        )
4478    }
4479
4480    /// Reject `:nome` values whose joint length with the canonical
4481    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4482    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4483    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4484    /// substrate carries materializes the caixa's `:nome` through the
4485    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4486    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4487    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4488    /// `ChartDir.name` + `Chart.yaml::name`
4489    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4490    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4491    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4492    /// `oci://<registry>/lareira-<nome>` chart ref
4493    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4494    /// admission rule strict-parses against DNS-1123-label, the Helm
4495    /// operator's tracking-secret name is derived from `release_name`
4496    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4497    /// K8s object `metadata.name` axes embed the chart name as a
4498    /// prefix — every one fails admission on a > 63-byte chart name.
4499    ///
4500    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4501    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4502    /// `:nome` of 56–63 bytes silently passed validate (the inner
4503    /// DNS-1123 check accepts the bare `:nome`) but produced a
4504    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4505    /// rejected at admission — far from the source `caixa.lisp`, with
4506    /// no field naming the overflow root cause. The
4507    /// [`lareira_chart_name`] helper's own doc comment
4508    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4509    /// "the M4 admission webhook will pin the joint-length invariant
4510    /// when it lands". This gate lands the invariant at the
4511    /// manifest-validate layer rather than waiting for the apiserver
4512    /// — the same fail-at-the-source posture every peer per-axis
4513    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4514    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4515    /// `:edicao`, etc.) takes.
4516    ///
4517    /// Thin wrapper around
4518    /// [`crate::render::is_lareira_chart_name_shape`] (the
4519    /// substrate-side predicate that composes [`lareira_chart_name`] +
4520    /// [`is_dns_1123_label`] via the lifted
4521    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4522    /// shared parser-shaped reason into the
4523    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4524    /// diagnostic is self-locating (the offending `:nome` is named
4525    /// verbatim alongside the rendered chart name and the budget) and
4526    /// the author can shorten in one edit. The gate runs across every
4527    /// `:kind` — `:nome` is the substrate-wide identity axis any
4528    /// future renderer the substrate adds can derive a
4529    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4530    /// the drift footgun where a future kind grows a chart-emitting
4531    /// render path while the validate cascade doesn't catch it.
4532    ///
4533    /// Runs *after* [`Self::validate_nome`] so the narrower
4534    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4535    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4536    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4537    /// specific shape error rather than the chart-name-budget error,
4538    /// preserving the legitimate "well-shaped `:nome` that happens to
4539    /// overflow the joint cap" arm for this gate.
4540    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4541        let nome = self.nome();
4542        crate::render::is_lareira_chart_name_shape(nome).map_err(|reason| {
4543            ManifestError::NomeChartNameBudgetExceeded {
4544                nome: nome.to_string(),
4545                reason,
4546            }
4547        })
4548    }
4549
4550    /// Reject `:versao` values that don't parse as [`semver::Version`].
4551    /// The top-level Caixa version flows directly into every
4552    /// substrate-side artifact that carries a "this is which version of
4553    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4554    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4555    /// SemVer-2-strict at `helm template` / `helm install` time per
4556    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4557    /// `feira publish` Zig-style `v<versao>` git tag
4558    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4559    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4560    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4561    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4562    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4563    /// `skopeo push`, the lacre closure's pinned versions
4564    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4565    /// `:upgrade-from :from` references peers in this exact `versao`
4566    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4567    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4568    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4569    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4570    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4571    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4572    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4573    /// into the version field a peer `:deps :versao` accepts;
4574    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4575    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4576    /// derive macro stores the raw String) and the failure surfaced at
4577    /// the *first* downstream consumer that strict-parses it: at
4578    /// `helm install` time as a chart-version rejection, at
4579    /// `feira publish` time as a malformed git tag, at lacre-resolve
4580    /// time as a `semver::Error` not naming the offending caixa, at
4581    /// `feira upgrade --to <versao>` time as an unresolvable
4582    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4583    /// and without any field naming the offending `:versao`.
4584    ///
4585    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4586    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4587    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4588    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4589    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4590    /// variant, carrying the offending `:versao` verbatim + a
4591    /// parser-shaped reason naming the specific violation, so the
4592    /// diagnostic is self-locating (the author can grep their
4593    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4594    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4595    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4596    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4597    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4598    /// now structurally equivalent (every value past validate is
4599    /// round-trippable through [`semver::Version::parse`] without
4600    /// re-checking at the renderer, resolver, or operator hot-upgrade
4601    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4602    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4603    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4604    ///
4605    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4606    /// the derive macro stores the raw String) is gated by the
4607    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4608    /// consulted, mirroring the empty-first cascade every per-axis
4609    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4610    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4611    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4612    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4613        let versao = self.versao();
4614        if versao.is_empty() {
4615            return Err(ManifestError::VersaoEmpty);
4616        }
4617        semver::Version::parse(versao).map_err(|e| ManifestError::VersaoInvalid {
4618            versao: versao.to_string(),
4619            reason: e.to_string(),
4620        })?;
4621        Ok(())
4622    }
4623
4624    /// Compound per-`Caixa` entry gate on the M2 `:upgrade-from` slot:
4625    /// folds the three [`crate::upgrade`] top-level validators — the
4626    /// per-entry shape + cross-entry duplicate-`:from` gate
4627    /// ([`crate::upgrade::validate_upgrade_from`]), the cross-slot
4628    /// `:from < :versao` SemVer-2 precedence gate
4629    /// ([`crate::upgrade::validate_upgrade_from_against_versao`]), and the
4630    /// cross-slot `:state-change` ↔ `:on-state-change` composition gate
4631    /// ([`crate::upgrade::validate_upgrade_from_against_behavior`]) — onto
4632    /// one substrate primitive on [`Caixa`]. The three dispatches run in
4633    /// the same order the layout pipeline
4634    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4635    /// author-time gate) has always sequenced them, so the fold is
4636    /// byte-for-byte equivalent to the pre-fold three-block cascade at
4637    /// that call site (pinned by the per-arm
4638    /// `validate_upgrade_from_folds_per_entry_arm_matches_gate` /
4639    /// `_folds_versao_arm_matches_gate` / `_folds_behavior_arm_matches_gate`
4640    /// equivalence pins and by the cross-arm
4641    /// `validate_upgrade_from_per_entry_arm_fires_before_versao_arm` /
4642    /// `_versao_arm_fires_before_behavior_arm` ordering pins).
4643    ///
4644    /// Prior to this lift the three [`crate::upgrade`] top-level validators
4645    /// lived only open-coded at the layout wire-up site
4646    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4647    /// each threaded through the same `self.upgrade_from()` slice and each
4648    /// paired with the same [`crate::LayoutError::UpgradeViolation`]-wrap
4649    /// envelope: every future consumer that wanted to gate `:upgrade-from`
4650    /// as a whole — the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
4651    /// materializer's per-CR admission webhook re-checking `:upgrade-from`
4652    /// after a per-`(:from … :instructions …)` patch, a future `feira
4653    /// validate --upgrade` per-caixa admission verb, a per-`:upgrade-from`
4654    /// overlay resolver a per-cluster overlay lift would materialize —
4655    /// was structurally forced to either re-inline the three-dispatch
4656    /// cascade in lockstep with the layout wire-up (the duplication the
4657    /// PRIME DIRECTIVE names as a bug) or call the whole
4658    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4659    /// peer per-Caixa gate to re-check one slot. Post-fold each such
4660    /// consumer reaches the three-arm compound gate through one call on
4661    /// the substrate primitive.
4662    ///
4663    /// The three arms together name one contract with three axes:
4664    ///
4665    ///   - **per-entry + cross-entry graph-edge invariant** — every entry's
4666    ///     `:from` parses as SemVer-2 and every per-instruction / within-
4667    ///     entry ordering / singularity gate on each entry's
4668    ///     `:instructions` list passes, and no two entries share the same
4669    ///     parsed `:from` (the wasm-operator's OTP appup
4670    ///     `release_handler:install_release/1` analog picks at most one
4671    ///     matching block per running version — two entries with the same
4672    ///     parsed semver are an ambiguous edge in the typed upgrade graph).
4673    ///   - **cross-slot reachability invariant** — every entry's `:from`
4674    ///     is strictly less than the caixa's own `:versao` under SemVer-2
4675    ///     precedence. An entry whose `:from >= :versao` is structurally
4676    ///     unreachable by the operator's `:from`-match dispatch (the
4677    ///     operator loads the current `:versao` and matches the *running*
4678    ///     version against each entry's `:from`; an entry whose `:from >=
4679    ///     :versao` is never reached because the operator never runs a
4680    ///     version >= the current one that it could then upgrade *to* the
4681    ///     current one).
4682    ///   - **cross-slot composition invariant** — every entry carrying a
4683    ///     `(:state-change …)` instruction has a `:behavior
4684    ///     :on-state-change` callback declared on the same caixa. The
4685    ///     per-version migration script is the `gen_server:code_change/3`
4686    ///     analog and the runtime hook it is delivered through during hot
4687    ///     upgrade is the `:on-state-change` callback (the upgrade.rs
4688    ///     module doc pins the composition verbatim: "Composes with the
4689    ///     `:behavior :on-state-change` callback to deliver state migration
4690    ///     during hot upgrades").
4691    ///
4692    /// All three axes must hold together — every consumer's
4693    /// `:upgrade-from` accept-set past this compound gate is the same
4694    /// set the `feira build` author-time gate admits.
4695    ///
4696    /// The per-slot compound entry gate discipline lifted here onto the
4697    /// M2 `:upgrade-from` axis is the sibling of the peer per-kind
4698    /// compound entry gates ([`crate::render::require_supervisor_view`]
4699    /// / [`crate::render::require_aplicacao_view`] /
4700    /// [`crate::render::require_v0_servico_shape`]) that fold every
4701    /// per-kind cascade at the per-kind altitude, and of the peer
4702    /// per-slot compound gates ([`crate::AplicacaoSpec::validate_contratos`],
4703    /// [`crate::MeshPolicy::validate`],
4704    /// [`crate::SupervisorSpec::validate_children`]) that fold every
4705    /// structural axis on their slot onto one substrate primitive.
4706    /// Extended here to the last unlifted compound-cascade wire-up at
4707    /// the layout-pipeline altitude — the three-dispatch M2
4708    /// `:upgrade-from` cascade that lived only open-coded at the layout
4709    /// wire-up site.
4710    ///
4711    /// The per-instruction script-path on-disk existence-probe walk that
4712    /// [`crate::layout::StandardLayout::verify`] runs immediately after
4713    /// this gate (which resolves each entry's `:instructions
4714    /// (:state-change :script)` against the layout root) stays open-coded
4715    /// at the layout wire-up site — that arm needs the filesystem oracle
4716    /// on the [`crate::LayoutInvariants`] trait, not the pure per-Caixa
4717    /// typed-shape surface this compound gate folds. Same posture the
4718    /// peer [`Self::validate_code_paths`] takes on the sibling code-path
4719    /// axes: the typed-shape gate fires on the per-Caixa surface, the
4720    /// on-disk existence check fires on the [`crate::StandardLayout`]
4721    /// surface.
4722    ///
4723    /// # Errors
4724    ///
4725    /// Returns [`crate::UpgradeError::FromInvalid`] /
4726    /// [`crate::UpgradeError::ModuleEmpty`] /
4727    /// [`crate::UpgradeError::ModuleInvalid`] /
4728    /// [`crate::UpgradeError::EmptyScript`] /
4729    /// [`crate::UpgradeError::AbsoluteScript`] /
4730    /// [`crate::UpgradeError::ParentEscapeScript`] /
4731    /// [`crate::UpgradeError::NonLispExtensionScript`] /
4732    /// [`crate::UpgradeError::RestartNotExclusive`] /
4733    /// [`crate::UpgradeError::StateChangeWithoutPriorLoad`] /
4734    /// [`crate::UpgradeError::PurgeWithoutPriorLoad`] /
4735    /// [`crate::UpgradeError::StateChangeAfterCleanup`] /
4736    /// [`crate::UpgradeError::DuplicateLoadModule`] /
4737    /// [`crate::UpgradeError::DuplicateStateChange`] /
4738    /// [`crate::UpgradeError::DuplicateCleanup`] /
4739    /// [`crate::UpgradeError::DuplicateFrom`] on the per-entry +
4740    /// cross-entry axis; [`crate::UpgradeError::FromNotBeforeVersao`] on
4741    /// the cross-slot `:from ↔ :versao` axis;
4742    /// [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
4743    /// on the cross-slot `:state-change ↔ :on-state-change` axis.
4744    pub fn validate_upgrade_from(&self) -> Result<(), crate::UpgradeError> {
4745        crate::upgrade::validate_upgrade_from(self.upgrade_from())?;
4746        crate::upgrade::validate_upgrade_from_against_versao(self.upgrade_from(), self.versao())?;
4747        crate::upgrade::validate_upgrade_from_against_behavior(
4748            self.upgrade_from(),
4749            self.behavior(),
4750        )?;
4751        Ok(())
4752    }
4753
4754    /// Compound per-`Caixa` entry gate on the M2 `:limits` slot — folds
4755    /// the [`crate::LimitsSpec::validate`] four-axis cascade (`:memory`
4756    /// wasm32 zero-floor / below-page / above-cap / non-page-multiple;
4757    /// `:fuel` zero-floor / cap; `:wall-clock` zero-floor / cap; `:cpu`
4758    /// zero-floor / cap) onto one substrate primitive on [`Caixa`]. The
4759    /// `#[serde(default)]` absent-slot arm (`limits: None`, the
4760    /// canonical "no bound declared — engine-default applies" author
4761    /// shape [`crate::LimitsSpec::is_empty`]'s per-axis `None` cascade
4762    /// reads) is the fold's identity element and passes trivially; the
4763    /// present-slot arm (`limits: Some(l)`) dispatches to
4764    /// [`crate::LimitsSpec::validate`] verbatim, threading its per-axis
4765    /// [`crate::LimitsError`] Display through untouched.
4766    ///
4767    /// Prior to this lift the M2 `:limits` slot lived only wired
4768    /// open-coded at the layout wire-up site
4769    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4770    /// through the `if let Some(l) = caixa.limits() { l.validate() … }`
4771    /// three-line `Option::None → Ok(()) | Some(_) → …` unwrap-and-
4772    /// dispatch pattern paired with the same
4773    /// [`crate::LayoutError::LimitsViolation`]-wrap envelope: every
4774    /// future consumer that wanted to gate `:limits` as a whole — the
4775    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4776    /// per-CR admission webhook re-checking `:limits` after a per-
4777    /// `{:memory, :fuel, :wall-clock, :cpu}` patch (the exact case the
4778    /// [`Self::limits`] accessor docstring names as the second
4779    /// consumer of the slot), a future `feira validate --limits` per-
4780    /// caixa admission verb, a per-`:limits` overlay resolver a per-
4781    /// cluster `:limits-overrides` overlay lift would materialize — was
4782    /// structurally forced to either re-inline the two-line
4783    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4784    /// pattern in lockstep with the layout wire-up (the duplication the
4785    /// PRIME DIRECTIVE names as a bug) or call the whole
4786    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4787    /// peer per-Caixa gate ([`Self::validate_nome`],
4788    /// [`Self::validate_versao`], [`Self::validate_deps`],
4789    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4790    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4791    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4792    /// [`Self::validate_upgrade_from`], [`Self::validate_code_paths`],
4793    /// plus the per-kind `require_supervisor_view` /
4794    /// `require_aplicacao_view` gates, plus the on-disk existence
4795    /// walks) to re-check one slot. Post-lift each such consumer
4796    /// reaches the [`crate::LimitsSpec::validate`] four-axis cascade
4797    /// (and its identity-element on the absent slot) through one call
4798    /// on the substrate primitive.
4799    ///
4800    /// The per-slot compound entry-gate discipline lifted here onto the
4801    /// M2 `:limits` axis is the sibling of the peer per-slot compound
4802    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4803    /// [`crate::MeshPolicy::validate`],
4804    /// [`crate::SupervisorSpec::validate_children`],
4805    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`]) that
4806    /// fold every structural + cross-slot axis on their slot onto one
4807    /// substrate primitive. Extended here to the M2 `:limits` slot, the
4808    /// first of the two M2 typed slots (`:limits`, `:behavior`) whose
4809    /// per-Caixa compound-gate wire-up still lived open-coded at the
4810    /// layout altitude after the [`Self::validate_upgrade_from`] lift
4811    /// (d6801df) closed the sibling M2 slot's cascade.
4812    ///
4813    /// # Errors
4814    ///
4815    /// Returns every [`crate::LimitsError`] variant on the present-slot
4816    /// arm — verbatim from [`crate::LimitsSpec::validate`]. Passes
4817    /// trivially on the absent-slot arm (`limits: None`, the fold's
4818    /// identity element).
4819    pub fn validate_limits(&self) -> Result<(), crate::LimitsError> {
4820        match self.limits() {
4821            Some(l) => l.validate(),
4822            None => Ok(()),
4823        }
4824    }
4825
4826    /// Compound per-`Caixa` entry gate on the M2 `:behavior` slot's
4827    /// pure typed-shape surface — folds the
4828    /// [`crate::BehaviorSpec::validate`] six-slot value-shape cascade
4829    /// (each declared `:on-init` / `:on-call` / `:on-cast` / `:on-info`
4830    /// / `:on-state-change` / `:on-terminate` callback-path is
4831    /// non-empty / relative / no-`..`-parent-escape / terminating-
4832    /// `.lisp`-extension, routed through the shared
4833    /// [`crate::render::require_sandboxed_lisp_path`] arm-set) onto one
4834    /// substrate primitive on [`Caixa`]. The `#[serde(default)]`
4835    /// absent-slot arm (`behavior: None`, the canonical "no callback
4836    /// declared — the runtime falls back to the wasm-engine's default
4837    /// callback per arm" author shape [`crate::BehaviorSpec::is_empty`]'s
4838    /// per-slot `None` cascade reads) is the fold's identity element
4839    /// and passes trivially; the present-slot arm (`behavior: Some(b)`)
4840    /// dispatches to [`crate::BehaviorSpec::validate`] verbatim,
4841    /// threading its per-slot [`crate::BehaviorError`] Display through
4842    /// untouched.
4843    ///
4844    /// Scope note — the on-disk callback-path existence walk paired
4845    /// with the value-shape gate at
4846    /// [`crate::layout::StandardLayout::verify`] stays open-coded at
4847    /// the layout altitude, because it needs the
4848    /// [`crate::layout::LayoutInvariants`] filesystem oracle
4849    /// ([`crate::layout::LayoutInvariants::exists`]) that the pure
4850    /// per-Caixa typed-shape surface this compound gate folds onto has
4851    /// no reference to. Same posture the peer M2 `:upgrade-from`
4852    /// per-Caixa compound gate ([`Self::validate_upgrade_from`]
4853    /// d6801df) already carries: the pure typed-shape surface folds
4854    /// onto the substrate primitive; the per-instruction script-path
4855    /// existence probe on the paired axis (there `:state-change
4856    /// :script`; here `:on-*`) stays at the layout altitude.
4857    ///
4858    /// Prior to this lift the pure value-shape surface of the M2
4859    /// `:behavior` slot lived only wired open-coded at the layout
4860    /// wire-up site ([`crate::layout::StandardLayout::verify`],
4861    /// caixa-core/src/layout.rs), through the
4862    /// `if let Some(b) = caixa.behavior() { b.validate() … }`
4863    /// unwrap-and-dispatch pattern paired with the same
4864    /// [`crate::LayoutError::BehaviorViolation`]-wrap envelope: every
4865    /// future consumer that wanted to gate the `:behavior` slot's
4866    /// value-shape as a whole — the deferred
4867    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4868    /// admission webhook re-checking `:behavior` after a per-`{:on-init,
4869    /// :on-call, :on-cast, :on-info, :on-state-change, :on-terminate}`
4870    /// patch (the exact case the peer `:on-*` accessor docstrings on
4871    /// [`crate::BehaviorSpec`] already name as deferred consumers of
4872    /// the slot), a future `feira validate --behavior` per-caixa
4873    /// admission verb, a per-`:behavior` overlay resolver a future
4874    /// per-cluster callback-overlay lift would materialize — was
4875    /// structurally forced to either re-inline the two-line
4876    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4877    /// pattern in lockstep with the layout wire-up (the duplication the
4878    /// PRIME DIRECTIVE names as a bug) or call the whole
4879    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4880    /// peer per-Caixa gate ([`Self::validate_nome`],
4881    /// [`Self::validate_versao`], [`Self::validate_deps`],
4882    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4883    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4884    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4885    /// [`Self::validate_limits`], [`Self::validate_upgrade_from`],
4886    /// [`Self::validate_code_paths`], plus the per-kind
4887    /// `require_supervisor_view` / `require_aplicacao_view` gates, plus
4888    /// the on-disk existence walks) to re-check one slot. Post-lift
4889    /// each such consumer reaches the [`crate::BehaviorSpec::validate`]
4890    /// six-slot cascade (and its identity-element on the absent slot)
4891    /// through one call on the substrate primitive.
4892    ///
4893    /// The per-slot compound entry-gate discipline lifted here onto the
4894    /// M2 `:behavior` axis is the sibling of the peer per-slot compound
4895    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4896    /// [`crate::MeshPolicy::validate`],
4897    /// [`crate::SupervisorSpec::validate_children`],
4898    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`],
4899    /// [`Self::validate_limits`]) that fold every structural + cross-
4900    /// slot axis on their slot onto one substrate primitive. Extended
4901    /// here to the M2 `:behavior` slot, the last of the four M2 typed
4902    /// slots (`:limits`, `:behavior`, `:upgrade-from`, plus the
4903    /// supervisor-only `:children` peer) whose per-Caixa compound-gate
4904    /// wire-up still lived open-coded at the layout altitude after the
4905    /// [`Self::validate_limits`] lift (baa4688) closed the sibling M2
4906    /// `:limits` slot's cascade. With this lift the "one named per-slot
4907    /// / per-Caixa compound gate per typed slot folding every structural
4908    /// axis on that slot (plus the `Option::None` identity element for
4909    /// the `Option`-shaped slots) onto one substrate primitive"
4910    /// discipline spans every M2 typed slot uniformly, so a reader who
4911    /// has learned any peer M2 gate reads `:behavior` without a per-
4912    /// slot exception carve-out.
4913    ///
4914    /// # Errors
4915    ///
4916    /// Returns every [`crate::BehaviorError`] variant on the present-
4917    /// slot arm — verbatim from [`crate::BehaviorSpec::validate`].
4918    /// Passes trivially on the absent-slot arm (`behavior: None`, the
4919    /// fold's identity element).
4920    pub fn validate_behavior(&self) -> Result<(), crate::BehaviorError> {
4921        match self.behavior() {
4922            Some(b) => b.validate(),
4923            None => Ok(()),
4924        }
4925    }
4926
4927    /// Reject `:restart-window` values the shared
4928    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4929    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4930    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4931    /// `Option<Duration>` routed through the shared codec via `with =
4932    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4933    /// view-construction path ([`Self::supervisor_view`]) folds the
4934    /// raw string through the same shared codec and soft-swallows the
4935    /// parse error as `None` to keep the view best-effort. Without
4936    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4937    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4938    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4939    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4940    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4941    /// edge case) silently produced a `SupervisorSpec` with
4942    /// `restart_window: None`, indistinguishable from the canonical
4943    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4944    /// `MaxIntensity / Period` invariant turns into a never-reset
4945    /// supervisor far from the source `caixa.lisp`, with no field
4946    /// naming the offending `:restart-window`. Lifting the gate to a
4947    /// Caixa-level validator mirrors the trajectory of the peer
4948    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4949    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4950    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4951    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4952    ///
4953    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4954    /// (the shared codec backing `:supervisor :restart-window` as
4955    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4956    /// `:politicas :circuit-breaker :window` — all three covered by
4957    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4958    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4959    /// variant, carrying the offending raw string + a parser-shaped
4960    /// reason naming the canonical authoring form, so the diagnostic
4961    /// is self-locating (the author can grep their `caixa.lisp` for
4962    /// `:restart-window "<value>"` and fix it in one edit) and
4963    /// uniform with every other manifest-level validate diagnostic.
4964    /// With this gate the four `:restart-window`-shaped surfaces (the
4965    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4966    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4967    /// now structurally equivalent — every value past the codec is in
4968    /// one accepted set, by construction.
4969    ///
4970    /// `None` (the canonical "omit the slot to express no reset"
4971    /// shape) is accepted trivially — the gate is a no-op when the
4972    /// author didn't author a window. The empty string is rejected by
4973    /// the shared codec (its digit-only gate refuses an empty
4974    /// magnitude), surfacing the same `RestartWindowMalformed`
4975    /// diagnostic as every other rejected non-canonical shape.
4976    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4977        let Some(s) = self.restart_window() else {
4978            return Ok(());
4979        };
4980        crate::supervisor::duration_codec::parse(s)
4981            .map(|_| ())
4982            .map_err(|reason| ManifestError::RestartWindowMalformed {
4983                restart_window: s.to_string(),
4984                reason,
4985            })
4986    }
4987
4988    /// Compound per-`Caixa` entry gate on the Aplicacao-kind mesh-slot
4989    /// family — folds the paired [`crate::AplicacaoSpec::validate`]
4990    /// typed-shape cascade (per-slot gates on `:membros`, `:contratos`,
4991    /// `:entrada`, `:placement`, `:politicas`, in that declared order)
4992    /// plus the cross-slot self-edge gate
4993    /// ([`crate::aplicacao::validate_no_self_membership`], the
4994    /// `:membros :caixa` ≠ `:nome` invariant the typed view cannot
4995    /// enforce on its own because it carries the membros but not the
4996    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4997    /// non-Aplicacao kinds the fold is the identity element — the paired
4998    /// [`Self::aplicacao_view`] accessor returns `None` off the
4999    /// Aplicacao arm (peer with the [`Self::validate_limits`] /
5000    /// [`Self::validate_behavior`] M2 `Option`-arm identity element),
5001    /// so the gate passes trivially without touching the mesh slots.
5002    ///
5003    /// Prior to this lift the paired cascade lived only wired open-coded
5004    /// at the layout wire-up site
5005    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
5006    /// as the three-line `let view = caixa.aplicacao_view().expect(...);
5007    /// view.validate() … validate_no_self_membership(...) …` pattern
5008    /// paired with two `.map_err(|err| LayoutError::AplicacaoViolation
5009    /// { caixa, issue })` wraps — every future consumer that wanted to
5010    /// gate the Aplicacao-shape cascade as a whole (the deferred
5011    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
5012    /// admission webhook re-checking `:membros` / `:contratos` after a
5013    /// per-slot patch, a future `feira validate --aplicacao` per-caixa
5014    /// admission verb, a per-Aplicacao overlay resolver) was structurally
5015    /// forced to either re-inline the two-dispatch cascade in lockstep
5016    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5017    /// names as a bug) or call the whole
5018    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5019    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5020    /// such consumer reaches the two-arm compound gate through one call
5021    /// on the substrate primitive.
5022    ///
5023    /// Peer to the [`crate::render::require_aplicacao_view`] compound
5024    /// entry gate every per-Aplicacao *renderer* routes through
5025    /// (3aefefb folded `validate_no_self_membership` onto the renderer
5026    /// path) — this gate mirrors the same fold on the *layout* path, so
5027    /// the two consumers of the Aplicacao-shape cascade (the author-time
5028    /// gate and every per-Aplicacao renderer) share one substrate
5029    /// primitive rather than two open-coded cascades kept in lockstep.
5030    /// Same lift discipline the peer per-slot compound gates
5031    /// ([`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5032    /// b5dd55e, [`Self::validate_limits`] baa4688,
5033    /// [`Self::validate_behavior`] 0d2877a) each carry.
5034    ///
5035    /// # Errors
5036    ///
5037    /// Returns every [`crate::AplicacaoError`] variant on the present-
5038    /// kind arm — the typed-shape cascade's per-slot arms first
5039    /// (matching [`crate::AplicacaoSpec::validate`]'s declared order),
5040    /// then the cross-slot self-edge arm
5041    /// ([`crate::AplicacaoError::MembroIsSelfAplicacao`]). Passes
5042    /// trivially on non-Aplicacao kinds (the fold's identity element).
5043    pub fn validate_aplicacao_shape(&self) -> Result<(), crate::AplicacaoError> {
5044        let Some(view) = self.aplicacao_view() else {
5045            return Ok(());
5046        };
5047        view.validate()?;
5048        crate::aplicacao::validate_no_self_membership(self.membros(), self.nome())?;
5049        Ok(())
5050    }
5051
5052    /// Compound per-`Caixa` entry gate on the Supervisor-kind
5053    /// supervision-tree slot family — folds the paired
5054    /// [`crate::SupervisorSpec::validate`] typed-shape cascade
5055    /// (`:estrategia` ↔ `:children` invariants, `:max-restarts` /
5056    /// `:restart-window` bounds, per-child DNS-1123 `:caixa` names,
5057    /// semver-valid `:versao` constraints, the set-not-multiset
5058    /// duplicate-child gate) plus the cross-slot self-edge gate
5059    /// ([`crate::supervisor::validate_no_self_supervision`], the
5060    /// `:children :caixa` ≠ `:nome` invariant the typed view cannot
5061    /// enforce on its own because it carries the children but not the
5062    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
5063    /// non-Supervisor kinds the fold is the identity element — the paired
5064    /// [`Self::supervisor_view`] accessor returns `None` off the
5065    /// Supervisor arm (peer with the [`Self::validate_limits`] /
5066    /// [`Self::validate_behavior`] M2 `Option`-arm identity element and
5067    /// the sibling per-Aplicacao [`Self::validate_aplicacao_shape`]),
5068    /// so the gate passes trivially without touching the supervision-tree
5069    /// slots.
5070    ///
5071    /// Prior to this lift the paired cascade lived only wired open-coded
5072    /// at the layout wire-up site
5073    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
5074    /// as the three-line `let view = caixa.supervisor_view().expect(...);
5075    /// view.validate() … validate_no_self_supervision(...) …` pattern
5076    /// paired with two `.map_err(|err| LayoutError::SupervisorViolation
5077    /// { caixa, issue })` wraps — every future consumer that wanted to
5078    /// gate the Supervisor-shape cascade as a whole (the wasm-operator's
5079    /// hierarchical reconciliation scheduler re-checking `:children` /
5080    /// `:estrategia` after a per-slot patch, the M4
5081    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
5082    /// webhook, a future `feira validate --supervisor` per-caixa
5083    /// admission verb, a per-Supervisor overlay resolver) was structurally
5084    /// forced to either re-inline the two-dispatch cascade in lockstep
5085    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5086    /// names as a bug) or call the whole
5087    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5088    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5089    /// such consumer reaches the two-arm compound gate through one call
5090    /// on the substrate primitive.
5091    ///
5092    /// Peer to the [`crate::render::require_supervisor_view`] compound
5093    /// entry gate every per-Supervisor *renderer* would route through
5094    /// (which already folds the same `spec.validate()` +
5095    /// `validate_no_self_supervision` two-arm cascade behind its
5096    /// `require_kind` + `validate_restart_window` prelude) — this gate
5097    /// mirrors the same fold on the *layout* path, so the two consumers
5098    /// of the Supervisor-shape cascade (the author-time gate and every
5099    /// per-Supervisor renderer) share one substrate primitive rather
5100    /// than two open-coded cascades kept in lockstep. Same lift
5101    /// discipline the peer per-slot compound gates
5102    /// ([`Self::validate_aplicacao_shape`] 949a7a0,
5103    /// [`Self::validate_upgrade_from`] d6801df,
5104    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5105    /// baa4688, [`Self::validate_behavior`] 0d2877a) each carry.
5106    ///
5107    /// # Errors
5108    ///
5109    /// Returns every [`crate::SupervisorError`] variant on the present-
5110    /// kind arm — the typed-shape cascade's per-slot arms first
5111    /// (matching [`crate::SupervisorSpec::validate`]'s declared order),
5112    /// then the cross-slot self-edge arm
5113    /// ([`crate::SupervisorError::ChildSupervisesSelf`]). Passes
5114    /// trivially on non-Supervisor kinds (the fold's identity element).
5115    pub fn validate_supervisor_shape(&self) -> Result<(), crate::SupervisorError> {
5116        let Some(view) = self.supervisor_view() else {
5117            return Ok(());
5118        };
5119        view.validate()?;
5120        crate::supervisor::validate_no_self_supervision(self.children(), self.nome())?;
5121        Ok(())
5122    }
5123
5124    /// Compound per-`Caixa` entry gate on the Acao-kind `:ci` slot
5125    /// family — folds the [`crate::decompose_ci`] typed decompose gate
5126    /// (`canteiro_types::decompose` refusing every illegal
5127    /// [`canteiro_types::CiRun`] shape: duplicate node name, dependency
5128    /// on an undeclared node, dependency cycle) onto one substrate
5129    /// primitive on [`Caixa`]. On non-`Acao` kinds the fold is the
5130    /// identity element — the paired [`Self::kind`] `is_acao()` guard
5131    /// short-circuits before the decompose gate ever fires (peer with
5132    /// the [`Self::validate_aplicacao_shape`] /
5133    /// [`Self::validate_supervisor_shape`] typed-view identity element
5134    /// and the [`Self::validate_limits`] / [`Self::validate_behavior`]
5135    /// M2 `Option`-arm identity element), so the gate passes trivially
5136    /// without touching the `:ci` slot. An `:kind Acao` caixa with
5137    /// `ci = None` is also an identity-element pass: the presence gate
5138    /// is the sibling axis owned by [`crate::LayoutError::MissingCi`] /
5139    /// [`crate::require_ci`] / [`crate::MissingCiSlot`], not by the
5140    /// decompose gate — a caixa that carries no `:ci` slot has no run
5141    /// to decompose. Same split the peer per-Servico
5142    /// [`crate::LayoutError::ServicoWithoutServicos`] presence gate and
5143    /// per-Binario [`crate::LayoutError::BinarioWithoutExe`] presence
5144    /// gate keep from their sibling per-slot shape gates, so the two
5145    /// axes stay separately diagnosable at the layout altitude.
5146    ///
5147    /// Prior to this lift the decompose gate lived only wired
5148    /// open-coded at the [`caixa_actions::validate`] renderer-side
5149    /// entry gate (routed through the substrate-canonical
5150    /// [`crate::require_acao_view`] compound helper) — the *layout*
5151    /// pipeline ([`crate::layout::StandardLayout::verify`], caixa-core/
5152    /// src/layout.rs) only checked `:ci` *presence* via
5153    /// [`crate::LayoutError::MissingCi`], so a `:kind Acao` caixa
5154    /// carrying a structurally illegal `:ci` (a duplicate node name, a
5155    /// dependency on an undeclared node, a dependency cycle) passed
5156    /// `feira build` cleanly and surfaced the diagnostic only when
5157    /// [`caixa_actions::validate`] later refused it — far from the
5158    /// source `caixa.lisp` on the author-time gate side. Every future
5159    /// consumer that wanted to gate the Acao-shape cascade as a whole
5160    /// (a per-`Acao` CR materializer's admission webhook re-checking
5161    /// `:ci` after a per-node patch, a future `feira validate --acao`
5162    /// per-caixa admission verb, a per-`Acao` overlay resolver
5163    /// rejecting an added / renamed node against a cluster-local
5164    /// snapshot) was structurally forced to either re-inline the
5165    /// decompose dispatch in lockstep with the renderer-side wire-up
5166    /// (the duplication the PRIME DIRECTIVE names as a bug) or call
5167    /// the whole [`caixa_actions::validate`] renderer and pay the
5168    /// per-node accumulation to re-check one slot. Post-fold each such
5169    /// consumer reaches the decompose gate through one call on the
5170    /// substrate primitive.
5171    ///
5172    /// Peer to the [`crate::require_acao_view`] compound entry gate
5173    /// every per-`Acao` *renderer* routes through (which already folds
5174    /// the same `require_ci + decompose_ci` two-arm cascade behind its
5175    /// `require_kind` prelude) — this gate mirrors the same fold on
5176    /// the *layout* path, so the two consumers of the Acao-shape
5177    /// cascade (the author-time gate and every per-`Acao` renderer)
5178    /// share one substrate primitive rather than two open-coded
5179    /// cascades kept in lockstep. Same lift discipline the peer
5180    /// per-kind compound gates ([`Self::validate_aplicacao_shape`]
5181    /// 949a7a0, [`Self::validate_supervisor_shape`] 4c70105,
5182    /// [`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5183    /// b5dd55e, [`Self::validate_limits`] baa4688,
5184    /// [`Self::validate_behavior`] 0d2877a) each carry. Closes the
5185    /// last per-kind asymmetry: with this lift the four typed
5186    /// named-caixa kinds (`Servico` / `Aplicacao` / `Supervisor` /
5187    /// `Acao`) each carry a compound per-`Caixa` shape gate on the
5188    /// substrate, and the layout pipeline routes through the same one
5189    /// substrate primitive per kind rather than four open-coded
5190    /// cascades.
5191    ///
5192    /// # Errors
5193    ///
5194    /// Returns the [`crate::CiDecomposeFailure`] typed view on the
5195    /// present-slot arm — the caixa's `:nome` alongside the borrowed
5196    /// [`canteiro_types::DecomposeError`] source (`DuplicateNode` /
5197    /// `UnknownDep` / `Cycle`) verbatim, so a consumer that fans on
5198    /// the specific arm reaches for `err.source` directly rather than
5199    /// re-parsing the Display bytes. Passes trivially on non-`Acao`
5200    /// kinds and on `:kind Acao` caixas with absent `:ci` (the fold's
5201    /// two identity-element arms).
5202    pub fn validate_acao_shape(&self) -> Result<(), crate::CiDecomposeFailure> {
5203        if !self.kind().is_acao() {
5204            return Ok(());
5205        }
5206        let Some(ci) = self.ci() else {
5207            return Ok(());
5208        };
5209        crate::render::decompose_ci(self, ci).map(|_| ())
5210    }
5211
5212    /// Compound per-`Caixa` kind ↔ typed-slot coherence gate on the
5213    /// three "declared but ignored" typed-slot families — M3 mesh
5214    /// (`:membros` / `:contratos` / `:politicas` / `:placement` /
5215    /// `:entrada`, owned by `:kind Aplicacao`, MESH-COMPOSITION §III.1),
5216    /// supervisor-tree (`:estrategia` / `:max-restarts` /
5217    /// `:restart-window` / `:children`, owned by `:kind Supervisor`,
5218    /// INSPIRATIONS §II.2), and M2 Servico-runtime (`:limits` /
5219    /// `:behavior` / `:upgrade-from`, owned by `:kind Servico`,
5220    /// INSPIRATIONS §III.1 / §II.3 / §II.4). Folds the three sibling
5221    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5222    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5223    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
5224    /// gates — each pre-lift a self-similar five-line
5225    /// `if !caixa.kind().is_<owner>() { let slots = caixa.declared_
5226    /// <family>_slots(); if !slots.is_empty() { return
5227    /// Err(LayoutError::<family>_on_non_<owner>(caixa, slots)); } }`
5228    /// block at [`crate::layout::StandardLayout::verify`] — onto one
5229    /// substrate primitive on [`Caixa`]. Every arm passes as an
5230    /// identity element on the owner kind (the paired
5231    /// [`Self::kind`] `is_<owner>()` guard short-circuits before the
5232    /// per-family `declared_*_slots` gate fires) and on non-owner
5233    /// kinds carrying no declared slot in that family (the
5234    /// [`Vec::is_empty`] check short-circuits before the wrap fires),
5235    /// so a bare no-code caixa on any kind passes the fold trivially
5236    /// on all three arms.
5237    ///
5238    /// Prior to this lift the three-arm cascade lived only wired
5239    /// open-coded at the layout wire-up site
5240    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/
5241    /// layout.rs) as three self-similar five-line blocks paired with
5242    /// three [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5243    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5244    /// [`crate::LayoutError::servico_slots_on_non_servico`] ctor
5245    /// dispatches (each of which the peer
5246    /// [`crate::layout::layout_slot_kind_ctors!`] macro already folds
5247    /// onto one substrate primitive per typed variant, 0419438) —
5248    /// every future consumer that wanted to gate the whole
5249    /// kind-coherence cascade as a unit (the deferred
5250    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5251    /// webhook re-checking every typed-slot family after a per-slot
5252    /// patch, a future `feira validate --kind-coherence` per-caixa
5253    /// admission verb, a per-`Caixa` overlay resolver rejecting a
5254    /// kind-foreign patch against a cluster-local snapshot) was
5255    /// structurally forced to either re-inline the three-block
5256    /// cascade in lockstep with the layout wire-up (the duplication
5257    /// the PRIME DIRECTIVE names as a bug) or call the whole
5258    /// [`crate::layout::StandardLayout::verify`] pipeline and pay
5259    /// every peer per-`Caixa` gate to re-check three slot families.
5260    /// Post-fold each such consumer reaches the three-arm cascade
5261    /// through one call on the substrate primitive.
5262    ///
5263    /// Diagnostic order matches the pre-fold layout wire-up
5264    /// canonical sequence — mesh → supervisor → servico — pinned by
5265    /// the load-bearing
5266    /// `validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm`
5267    /// / `_supervisor_arm_fires_before_servico_arm` ordering pins
5268    /// below. The three arms enumerate every typed-slot family the
5269    /// substrate carries whose "declared but ignored" footgun is
5270    /// gated at the layout altitude by a `{ caixa, kind, slots }`
5271    /// wrap variant — the peer
5272    /// [`crate::LayoutError::ForeignCodeSlot`] gate on the
5273    /// code-surface family sits outside this fold because
5274    /// [`Self::declared_foreign_code_slots`] bakes the kind-check
5275    /// into the helper (so the layout wire-up carries no outer
5276    /// `if !caixa.kind().is_<owner>()` guard), and the peer
5277    /// [`crate::LayoutError::CiOnNonAcao`] gate on the `:ci` axis
5278    /// carries a distinct `{ caixa, kind }` wrap shape (no `slots`
5279    /// field — `:ci` is a single `Option` not a `Vec`-of-named-slots)
5280    /// and rides on its own peer substrate primitive
5281    /// [`Self::validate_ci_kind_coherence`] (the direct sibling to
5282    /// this fold on the `:ci` axis) — the two folds share the same
5283    /// altitude and diagnostic order at the layout wire-up site but
5284    /// keep their distinct envelope shapes, so no consumer of
5285    /// `CiOnNonAcao` sees a variant rename.
5286    ///
5287    /// Peer to the per-kind compound entry gates every substrate
5288    /// primitive on the M2/M3 typed-slot family already carries
5289    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5290    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5291    /// [`Self::validate_upgrade_from`] d6801df,
5292    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5293    /// [`Self::validate_supervisor_shape`] 4c70105,
5294    /// [`Self::validate_acao_shape`] 5d6df54): the author-time gate
5295    /// axis on the *per-slot* algebra now shares one substrate
5296    /// primitive per compound gate, and this lift closes the
5297    /// symmetric axis on the *cross-family* kind ↔ slot coherence
5298    /// algebra so the layout pipeline routes the three self-similar
5299    /// gates through one substrate primitive rather than three
5300    /// open-coded blocks. Every future kind that adds its own
5301    /// exclusive typed-slot family (an `Actor`-owned per-virtual-
5302    /// actor grain slot the M5 Orleans-inspired kind reaches
5303    /// through, a per-Aplicacao overlay slot the M4 CR materializer
5304    /// consults) folds onto this compound gate as one arm addition
5305    /// rather than a fourth open-coded block at the wire-up site.
5306    ///
5307    /// # Errors
5308    ///
5309    /// Returns the first [`crate::LayoutError`] variant surfacing
5310    /// under the canonical mesh → supervisor → servico order:
5311    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] on a non-
5312    /// Aplicacao caixa with a declared M3 mesh slot,
5313    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] on a
5314    /// non-Supervisor caixa with a declared supervisor-tree slot,
5315    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] on a
5316    /// non-Servico caixa with a declared M2 slot. Passes trivially
5317    /// on the owner kind of each family and on non-owner kinds
5318    /// carrying no declared slot in that family (the fold's identity
5319    /// element on both axes).
5320    pub fn validate_kind_slot_coherence(&self) -> Result<(), crate::LayoutError> {
5321        // Each of the three arms routes through the shared
5322        // [`Self::run_kind_owned_slot_family_gate`] substrate primitive
5323        // — the outer non-owner-kind guard + inner accumulator + inner
5324        // emptiness-guard + wrap arm shape now lands on one dispatch
5325        // per family rather than a four-line open-coded block in
5326        // lockstep across all three arms. Canonical mesh → supervisor
5327        // → servico order preserved (the primitive short-circuits
5328        // arm-by-arm; the outer `?;` cascade at this altitude threads
5329        // the first surfaced arm's error verbatim). Each of the three
5330        // ctors ([`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5331        // [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5332        // [`crate::LayoutError::servico_slots_on_non_servico`]) was
5333        // already lifted onto the substrate by the peer
5334        // [`crate::layout::layout_slot_kind_ctors!`] macro, so each arm
5335        // routes through the same substrate-canonical
5336        // `Self::<variant> { caixa, kind, slots }` wrap per arm as the
5337        // pre-lift open-coded blocks — byte-equal, pinned by the
5338        // paired `validate_kind_slot_coherence_folds_<family>_arm_matches_gate`
5339        // equivalence pins and the peer
5340        // `validate_kind_slot_coherence_{mesh,supervisor}_arm_fires_before_<next>_arm`
5341        // ordering pins.
5342        self.run_kind_owned_slot_family_gate(
5343            crate::CaixaKind::is_aplicacao,
5344            Caixa::declared_mesh_slots,
5345            crate::LayoutError::mesh_slots_on_non_aplicacao,
5346        )?;
5347        self.run_kind_owned_slot_family_gate(
5348            crate::CaixaKind::is_supervisor,
5349            Caixa::declared_supervisor_slots,
5350            crate::LayoutError::supervisor_slots_on_non_supervisor,
5351        )?;
5352        self.run_kind_owned_slot_family_gate(
5353            crate::CaixaKind::is_servico,
5354            Caixa::declared_servico_slots,
5355            crate::LayoutError::servico_slots_on_non_servico,
5356        )?;
5357        Ok(())
5358    }
5359
5360    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on
5361    /// the three no-code kinds — `Supervisor` (supervises other
5362    /// caixas, INSPIRATIONS §II.2), `Aplicacao` (composes Servicos,
5363    /// MESH-COMPOSITION §III.1), and `Acao` (owns a typed CI run,
5364    /// CANTEIRO §7.1-C). Each carries no code of its own, so
5365    /// declaring any of `:bibliotecas` / `:exe` / `:servicos`
5366    /// silently passes the layout's path-existence loops (the paths
5367    /// still resolve on disk) and then vanishes downstream — the
5368    /// per-kind renderers gate emission on
5369    /// [`crate::render::require_kind`] and only emit the code
5370    /// surface for its owning kind, so a declared code slot on a
5371    /// no-code kind is the manifest field's documented "ignored
5372    /// otherwise" footgun.
5373    ///
5374    /// Pre-lift each of the three arms lived as a self-similar
5375    /// `if !caixa.kind().is_<no-code-kind>() { … } else if has_code
5376    /// { return Err(LayoutError::<kind>_owns_code(caixa)); }` block
5377    /// at [`crate::layout::StandardLayout::verify`] — three
5378    /// consumers, three identical shapes. Every future consumer
5379    /// that wanted to gate the whole code-surface coherence cascade
5380    /// as a unit (the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
5381    /// materializer's admission webhook re-checking after a
5382    /// per-slot patch, a future `feira validate --no-code-kind`
5383    /// per-caixa admission verb, a per-`Caixa` overlay resolver
5384    /// rejecting a kind-foreign patch) was structurally forced to
5385    /// either re-inline the three-block cascade in lockstep with
5386    /// the layout wire-up (the duplication the PRIME DIRECTIVE
5387    /// names as a bug) or call the whole
5388    /// [`crate::layout::StandardLayout::verify`] pipeline. Post-fold
5389    /// each such consumer reaches the three-arm cascade through
5390    /// one call.
5391    ///
5392    /// Mirror of the sibling [`Self::validate_kind_slot_coherence`]
5393    /// fold (f0d286e) on the author-time typed-slot coherence axis:
5394    /// that gate closes the "non-owner kind declares owner-only
5395    /// typed slots" three-arm cascade on the M2 / supervisor-tree /
5396    /// M3 slot families; this gate closes the reciprocal
5397    /// "no-code kind declares code" three-arm cascade on the
5398    /// `:bibliotecas` / `:exe` / `:servicos` code surface. Together
5399    /// the two folds route every kind ↔ author-shape coherence
5400    /// diagnostic at the layout altitude through one substrate
5401    /// primitive per axis.
5402    ///
5403    /// The gate carries two identity elements:
5404    /// - **`has_code == false`** — any kind (including the three
5405    ///   no-code kinds) that declares no code passes the paired
5406    ///   `!has_code` short-circuit before every per-arm dispatch.
5407    /// - **Code-owning kinds** (`Biblioteca` owning
5408    ///   `:bibliotecas`, `Binario` owning `:exe`, `Servico` owning
5409    ///   `:servicos`) — the three no-code arm-firing predicates
5410    ///   short-circuit on every code-owning kind, so the gate
5411    ///   passes trivially regardless of what code they declare.
5412    ///   Foreign-code-slot violations on a code-owning kind (e.g.
5413    ///   `:kind Servico` declaring `:exe`) surface through the
5414    ///   sibling [`crate::LayoutError::ForeignCodeSlot`] gate on
5415    ///   [`Self::declared_foreign_code_slots`], not through this
5416    ///   gate.
5417    ///
5418    /// Unlike the sibling cross-family
5419    /// [`Self::validate_kind_slot_coherence`], the three arms of
5420    /// this fold are mutually exclusive by construction — `:kind`
5421    /// is a single-valued [`CaixaKind`] discriminator so at most
5422    /// one arm can fire per caixa — and no cross-arm ordering pin
5423    /// is meaningful (the pre-fold three-block cascade at the
5424    /// wire-up site was already unreachable past the first
5425    /// matching arm).
5426    ///
5427    /// Peer to the per-kind compound entry gates every substrate
5428    /// primitive on the M2/M3 typed-slot family already carries
5429    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5430    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5431    /// [`Self::validate_upgrade_from`] d6801df,
5432    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5433    /// [`Self::validate_supervisor_shape`] 4c70105,
5434    /// [`Self::validate_acao_shape`] 5d6df54,
5435    /// [`Self::validate_kind_slot_coherence`] f0d286e): the
5436    /// author-time gate axis on the *per-slot* and *cross-family
5437    /// typed-slot* algebras each share one substrate primitive per
5438    /// compound gate, and this lift closes the third axis on the
5439    /// *code-surface* algebra so the layout pipeline routes all
5440    /// three coherence axes through one substrate primitive rather
5441    /// than nine open-coded blocks. Every future no-code kind
5442    /// (an `Actor` virtual-actor arm the M5 Orleans-inspired kind
5443    /// reaches through if it lands as a no-code composer, a future
5444    /// `Namespace` grouping kind) folds onto this compound gate
5445    /// as one arm addition rather than a fourth open-coded block
5446    /// at the wire-up site.
5447    ///
5448    /// # Errors
5449    ///
5450    /// Returns the [`crate::LayoutError`] variant naming the
5451    /// offending no-code kind:
5452    /// [`crate::LayoutError::SupervisorOwnsCode`] on a `:kind
5453    /// Supervisor` caixa with any declared code,
5454    /// [`crate::LayoutError::AplicacaoOwnsCode`] on a `:kind
5455    /// Aplicacao` caixa with any declared code,
5456    /// [`crate::LayoutError::AcaoOwnsCode`] on a `:kind Acao` caixa
5457    /// with any declared code. Passes trivially on every kind with
5458    /// no declared code and on every code-owning kind regardless
5459    /// of declared code (the fold's two identity-element arms).
5460    pub fn validate_no_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5461        let has_code =
5462            !self.bibliotecas().is_empty() || !self.exe().is_empty() || !self.servicos().is_empty();
5463        if !has_code {
5464            return Ok(());
5465        }
5466        if self.kind().is_supervisor() {
5467            return Err(crate::LayoutError::supervisor_owns_code(self));
5468        }
5469        if self.kind().is_aplicacao() {
5470            return Err(crate::LayoutError::aplicacao_owns_code(self));
5471        }
5472        if self.kind().is_acao() {
5473            return Err(crate::LayoutError::acao_owns_code(self));
5474        }
5475        Ok(())
5476    }
5477
5478    /// Compound per-`Caixa` kind ↔ `:ci` coherence gate — the `Acao`
5479    /// axis-only companion to the sibling three-arm
5480    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) on the
5481    /// M3 mesh / supervisor-tree / M2 Servico-runtime typed-slot
5482    /// families. `:ci` carries a typed CI run
5483    /// ([`canteiro_types::CiRun`], CANTEIRO §7.1-C) that only the
5484    /// `caixa-actions` renderer decomposes + validates and only for a
5485    /// `:kind Acao`. On any *other* kind a declared `:ci` is the
5486    /// manifest field's documented "ignored otherwise" — it silently
5487    /// passes verify and then vanishes (never decomposed, never
5488    /// rendered), far from the source `caixa.lisp`.
5489    ///
5490    /// Pre-lift the arm lived as a self-similar
5491    /// `if caixa.ci().is_some() && !caixa.kind().is_acao() { return
5492    /// Err(LayoutError::CiOnNonAcao { caixa: caixa.nome().to_string(),
5493    /// kind: caixa.kind() }); }` block at
5494    /// [`crate::layout::StandardLayout::verify`] — one consumer today
5495    /// but every future consumer that wanted to gate the `:ci`
5496    /// coherence axis as a unit (the deferred
5497    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5498    /// webhook re-checking after a per-slot patch, a future
5499    /// `feira validate --ci-coherence` per-caixa admission verb, a
5500    /// per-`Caixa` overlay resolver rejecting a kind-foreign `:ci`
5501    /// patch) was structurally forced to either re-inline the
5502    /// two-condition guard in lockstep with the layout wire-up (the
5503    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5504    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5505    /// Post-fold each such consumer reaches the arm through one call.
5506    ///
5507    /// Peer of the sibling three-arm
5508    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5509    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5510    /// axes under a uniform `{ caixa, kind, slots }` envelope
5511    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5512    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5513    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]). The `:ci`
5514    /// axis stays on its own primitive because
5515    /// [`crate::LayoutError::CiOnNonAcao`] carries a distinct
5516    /// `{ caixa, kind }` wrap shape (no `slots` field — `:ci` is a
5517    /// single `Option` not a `Vec`-of-named-slots) whose reshape
5518    /// onto the sibling `{ caixa, kind, slots }` envelope would
5519    /// force a variant rename touching every consumer of
5520    /// `CiOnNonAcao`; the two folds share the same
5521    /// author-time-vs-renderer split and diagnostic altitude, and
5522    /// route through peer substrate primitives on the same
5523    /// [`Caixa`] surface.
5524    ///
5525    /// Peer to the per-kind compound entry gates every substrate
5526    /// primitive on the M2/M3 typed-slot family already carries
5527    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5528    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5529    /// [`Self::validate_upgrade_from`] d6801df,
5530    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5531    /// [`Self::validate_supervisor_shape`] 4c70105,
5532    /// [`Self::validate_acao_shape`] 5d6df54,
5533    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5534    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2): every
5535    /// author-time coherence axis on the typed [`Caixa`] surface now
5536    /// routes through one substrate primitive per axis rather than
5537    /// an open-coded block at the layout wire-up site.
5538    ///
5539    /// The gate carries two identity elements:
5540    /// - **`ci().is_none()`** — a caixa that declares no `:ci`
5541    ///   passes the first short-circuit before every per-arm
5542    ///   dispatch, on every kind. The canonical shape of the four
5543    ///   non-`Acao` kinds (`Biblioteca` / `Binario` / `Servico` /
5544    ///   `Supervisor` / `Aplicacao`) is `ci = None` — the arm
5545    ///   never fires on a well-shaped fixture.
5546    /// - **`:kind Acao`** — the owner-kind arm short-circuits on
5547    ///   every `Acao` caixa regardless of its `:ci` shape; a
5548    ///   malformed `:ci` on an `Acao` surfaces through the peer
5549    ///   [`Self::validate_acao_shape`] compound decompose gate
5550    ///   (5d6df54), not through this coherence gate.
5551    ///
5552    /// # Errors
5553    ///
5554    /// Returns [`crate::LayoutError::CiOnNonAcao`] naming the
5555    /// offending caixa's nome + kind on any non-`Acao` caixa with
5556    /// `:ci` declared. Passes trivially on every kind that declares
5557    /// no `:ci` and on every `:kind Acao` caixa regardless of
5558    /// declared `:ci` (the fold's two identity-element arms).
5559    pub fn validate_ci_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5560        if self.ci().is_some() && !self.kind().is_acao() {
5561            return Err(crate::LayoutError::CiOnNonAcao {
5562                caixa: self.nome().to_string(),
5563                kind: self.kind(),
5564            });
5565        }
5566        Ok(())
5567    }
5568
5569    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on the
5570    /// two exclusive code-surface slots — `:exe` (owned only by
5571    /// [`crate::CaixaKind::Binario`], the nix-built executable surface)
5572    /// and `:servicos` (owned only by [`crate::CaixaKind::Servico`],
5573    /// the wasm-component + `ComputeUnit` daemon surface). The
5574    /// `caixa-helm` / `caixa-flux` / `caixa-flake` renderers gate
5575    /// emission on [`crate::render::require_kind`]`(_, <owning-kind>)`
5576    /// and only emit the slot for its owning kind — so on any *other*
5577    /// code-running kind a declared `:exe` / `:servicos` is the
5578    /// manifest field's documented "ignored otherwise": the path is
5579    /// validated by the per-kind path-existence loops in
5580    /// [`crate::layout::StandardLayout::verify`], but the value is
5581    /// never rendered into a build target or programs.yaml entry —
5582    /// it silently passes `feira build` and then vanishes, far from
5583    /// the source `caixa.lisp`, with no field naming which slot is
5584    /// foreign.
5585    ///
5586    /// Pre-lift the arm lived as a self-similar four-line `let
5587    /// foreign_code_slots = caixa.declared_foreign_code_slots(); if
5588    /// !foreign_code_slots.is_empty() { return
5589    /// Err(LayoutError::foreign_code_slot(caixa, foreign_code_slots));
5590    /// }` block at [`crate::layout::StandardLayout::verify`] — one
5591    /// consumer today but every future consumer that wanted to gate
5592    /// the code-surface coherence axis as a unit (the deferred
5593    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5594    /// webhook re-checking after a per-slot patch, a future
5595    /// `feira validate --foreign-code` per-caixa admission verb, a
5596    /// per-`Caixa` overlay resolver rejecting a kind-foreign code-
5597    /// slot patch) was structurally forced to either re-inline the
5598    /// two-condition guard in lockstep with the layout wire-up (the
5599    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5600    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5601    /// Post-fold each such consumer reaches the arm through one call.
5602    ///
5603    /// Peer of the sibling three-arm
5604    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5605    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5606    /// axes under the uniform `{ caixa, kind, slots }` envelope
5607    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5608    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5609    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]); this gate
5610    /// carries the code-surface axis under the same
5611    /// `{ caixa, kind, slots }` envelope
5612    /// ([`crate::LayoutError::ForeignCodeSlot`]). The two folds share
5613    /// the envelope shape but stay separate primitives because the
5614    /// per-arm predicate differs: the cross-family fold rides on the
5615    /// outer `!self.kind().is_<owner>()` guard *paired* with a
5616    /// per-family `declared_<family>_slots` accumulator, while this
5617    /// fold's per-arm kind-check is baked into
5618    /// [`Self::declared_foreign_code_slots`] itself (each arm's
5619    /// `!self.kind().requires_<slot>()` guard fires inside the
5620    /// accumulator, not around it) — so a `:kind Binario` declaring
5621    /// `:servicos` and a `:kind Servico` declaring `:exe` are both
5622    /// caught by one accumulator sweep rather than by two independent
5623    /// arm dispatches. Peer with [`Self::validate_ci_kind_coherence`]
5624    /// (9b55beb) which carries the `:ci` axis on its own primitive
5625    /// for the same "distinct per-arm predicate shape, shared
5626    /// diagnostic altitude" reason.
5627    ///
5628    /// Peer to the per-kind and per-slot compound entry gates every
5629    /// substrate primitive on the M2/M3 typed-slot family already
5630    /// carries ([`Self::validate_deps`] b5dd55e,
5631    /// [`Self::validate_limits`] baa4688,
5632    /// [`Self::validate_behavior`] 0d2877a,
5633    /// [`Self::validate_upgrade_from`] d6801df,
5634    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5635    /// [`Self::validate_supervisor_shape`] 4c70105,
5636    /// [`Self::validate_acao_shape`] 5d6df54,
5637    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5638    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5639    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5640    /// author-time coherence axis on the typed [`Caixa`] surface now
5641    /// routes through one substrate primitive per axis rather than an
5642    /// open-coded block at the layout wire-up site. This closes the
5643    /// last open-coded kind ↔ slot coherence gate at the layout
5644    /// altitude — every kind-coherence diagnostic is now a substrate
5645    /// primitive.
5646    ///
5647    /// The gate carries three identity elements:
5648    /// - **Code-owning kinds on their native slot** — a
5649    ///   [`crate::CaixaKind::Binario`] declaring `:exe`, a
5650    ///   [`crate::CaixaKind::Servico`] declaring `:servicos` — each
5651    ///   arm's `!requires_<slot>()` predicate short-circuits inside
5652    ///   [`Self::declared_foreign_code_slots`], so the accumulator
5653    ///   returns an empty `Vec` and the outer `is_empty` short-
5654    ///   circuits before the wrap fires.
5655    /// - **Bare caixas** — a caixa with no declared code on any kind
5656    ///   passes the same accumulator's `is_empty` short-circuit on
5657    ///   every arm.
5658    /// - **No-code kinds** ([`crate::CaixaKind::Supervisor`] /
5659    ///   [`crate::CaixaKind::Aplicacao`] / [`crate::CaixaKind::Acao`])
5660    ///   declaring code — dominated upstream by the sibling
5661    ///   [`Self::validate_no_code_kind_coherence`] (3bbf6a2) which
5662    ///   surfaces [`crate::LayoutError::SupervisorOwnsCode`] /
5663    ///   [`crate::LayoutError::AplicacaoOwnsCode`] /
5664    ///   [`crate::LayoutError::AcaoOwnsCode`] first at the layout
5665    ///   wire-up site, so this gate never fires on a no-code kind
5666    ///   through the layout pipeline. A standalone caller reaching
5667    ///   this primitive without the sibling `_no_code_` gate first
5668    ///   would see a no-code kind's declared `:exe` / `:servicos`
5669    ///   surface `ForeignCodeSlot` here (the two folds partition the
5670    ///   diagnostic responsibility along the "declared no-code slot"
5671    ///   axis: no-code kinds get `OwnsCode`, code-running kinds get
5672    ///   `ForeignCodeSlot`), and the layout wire-up's canonical
5673    ///   `_no_code_` → `_foreign_code_` ordering keeps the
5674    ///   [`crate::LayoutError::SupervisorOwnsCode`] / … arm the one
5675    ///   that surfaces in the composed pipeline.
5676    ///
5677    /// Diagnostic order within the arm matches the pre-fold layout
5678    /// wire-up canonical sequence — `:exe` → `:servicos` — pinned by
5679    /// [`Self::declared_foreign_code_slots`]'s per-arm push order.
5680    ///
5681    /// # Errors
5682    ///
5683    /// Returns [`crate::LayoutError::ForeignCodeSlot`] naming the
5684    /// offending caixa's nome + kind + declared foreign-code slot
5685    /// list on any code-running kind ([`crate::CaixaKind::Biblioteca`]
5686    /// / [`crate::CaixaKind::Binario`] / [`crate::CaixaKind::Servico`])
5687    /// declaring another code-running kind's exclusive code surface.
5688    /// Passes trivially on every native-slot declaration (Binario
5689    /// with `:exe`, Servico with `:servicos`), on every bare caixa,
5690    /// and on every no-code kind (dominated upstream by the sibling
5691    /// [`Self::validate_no_code_kind_coherence`] `OwnsCode` gates —
5692    /// see the identity-element notes above).
5693    pub fn validate_foreign_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5694        let foreign_code_slots = self.declared_foreign_code_slots();
5695        if !foreign_code_slots.is_empty() {
5696            return Err(crate::LayoutError::foreign_code_slot(
5697                self,
5698                foreign_code_slots,
5699            ));
5700        }
5701        Ok(())
5702    }
5703
5704    /// Compound per-`Caixa` required-slot gate on the three
5705    /// [`crate::CaixaKind`] arms whose sole payload is a canonical
5706    /// typed slot: `Binario`'s `:exe`, `Servico`'s `:servicos`,
5707    /// `Acao`'s `:ci`. Each arm refuses a caixa on its owner kind
5708    /// that declares no value in the corresponding required slot,
5709    /// so `feira build` (the canonical author-time gate) surfaces the
5710    /// self-locating "this kind needs this slot" diagnostic at the
5711    /// source `caixa.lisp` rather than deferring the failure to a
5712    /// downstream consumer (a nix build with no `:exe` to build, a
5713    /// programs.yaml fan-out with no `:servicos` to enumerate, a
5714    /// `caixa-actions` decompose with no `:ci` to walk).
5715    ///
5716    /// Pre-lift each of the three arms lived as a self-similar
5717    /// `if caixa.kind().requires_<slot>() && caixa.<slot>().is_<empty>() {
5718    /// return Err(LayoutError::<kind>_without_<slot>(caixa)); }`
5719    /// block at [`crate::layout::StandardLayout::verify`] — three
5720    /// consumers, three identical shapes, one substrate primitive on
5721    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
5722    /// a bug. Each of the three inner ctors
5723    /// ([`crate::LayoutError::binario_without_exe`] /
5724    /// [`crate::LayoutError::servico_without_servicos`] /
5725    /// [`crate::LayoutError::missing_ci`]) was already lifted onto
5726    /// the substrate by the peer [`crate::layout::layout_nome_only_ctors!`]
5727    /// macro, so the primitive routes through the same
5728    /// `Self::<variant>(caixa.nome().to_string())` tuple-literal
5729    /// wrap per arm as the pre-lift open-coded blocks.
5730    ///
5731    /// The paired `Biblioteca`-arm required-slot check
5732    /// ([`crate::LayoutError::MissingLib`]) stays open-coded at the
5733    /// layout wire-up site by design: it needs the filesystem oracle
5734    /// on [`crate::layout::LayoutInvariants`] to check the default
5735    /// `lib/<nome>.lisp` fallback path, which the pure per-`Caixa`
5736    /// typed-shape surface this fold rides on has no reference to.
5737    /// Same posture the peer [`Self::validate_no_code_kind_coherence`]
5738    /// fold takes on the on-disk existence loops.
5739    ///
5740    /// Diagnostic order at the primitive matches the pre-fold layout
5741    /// wire-up canonical sequence — `:exe` → `:servicos` → `:ci` —
5742    /// the same three-arm sweep the peer [`crate::CaixaKind`]
5743    /// discriminator carries at its `requires_*` accessors. Unlike
5744    /// the sibling cross-family [`Self::validate_kind_slot_coherence`]
5745    /// fold, the three arms of this fold are mutually exclusive by
5746    /// construction — `:kind` is a single-valued [`crate::CaixaKind`]
5747    /// discriminator so at most one arm can fire per caixa — and no
5748    /// cross-arm ordering pin is meaningful (the pre-fold three-block
5749    /// cascade at the wire-up site was already unreachable past the
5750    /// first matching arm).
5751    ///
5752    /// Peer to the per-kind and per-slot compound entry gates every
5753    /// substrate primitive on the M2/M3 typed-slot family already
5754    /// carries ([`Self::validate_deps`] b5dd55e,
5755    /// [`Self::validate_limits`] baa4688,
5756    /// [`Self::validate_behavior`] 0d2877a,
5757    /// [`Self::validate_upgrade_from`] d6801df,
5758    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5759    /// [`Self::validate_supervisor_shape`] 4c70105,
5760    /// [`Self::validate_acao_shape`] 5d6df54,
5761    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5762    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5763    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5764    /// author-time coherence axis on the typed [`Caixa`] surface
5765    /// now routes through one substrate primitive per axis rather
5766    /// than an open-coded block at the layout wire-up site.
5767    ///
5768    /// The gate carries two identity elements:
5769    /// - **Non-owner kinds** — each per-arm predicate is
5770    ///   `self.kind().requires_<slot>()`, which returns `true` only
5771    ///   for the owning kind ([`crate::CaixaKind::Binario`] on `:exe`,
5772    ///   [`crate::CaixaKind::Servico`] on `:servicos`,
5773    ///   [`crate::CaixaKind::Acao`] on `:ci`). Every non-owner kind
5774    ///   passes each per-arm dispatch trivially.
5775    /// - **Owner kinds with the required slot present** — a
5776    ///   [`crate::CaixaKind::Binario`] with a non-empty `:exe`, a
5777    ///   [`crate::CaixaKind::Servico`] with a non-empty `:servicos`,
5778    ///   an [`crate::CaixaKind::Acao`] with `ci = Some(_)` — passes
5779    ///   its arm's `is_empty` / `is_none` short-circuit.
5780    ///
5781    /// # Errors
5782    ///
5783    /// Returns the [`crate::LayoutError`] variant naming the
5784    /// offending owner kind:
5785    /// [`crate::LayoutError::BinarioWithoutExe`] on a
5786    /// [`crate::CaixaKind::Binario`] caixa with no declared `:exe`,
5787    /// [`crate::LayoutError::ServicoWithoutServicos`] on a
5788    /// [`crate::CaixaKind::Servico`] caixa with no declared
5789    /// `:servicos`, [`crate::LayoutError::MissingCi`] on a
5790    /// [`crate::CaixaKind::Acao`] caixa with no declared `:ci`.
5791    /// Passes trivially on every non-owner kind and on every owner
5792    /// kind with its required slot present.
5793    pub fn validate_required_kind_slot(&self) -> Result<(), crate::LayoutError> {
5794        if self.kind().requires_exe() && self.exe().is_empty() {
5795            return Err(crate::LayoutError::binario_without_exe(self));
5796        }
5797        if self.kind().requires_servicos() && self.servicos().is_empty() {
5798            return Err(crate::LayoutError::servico_without_servicos(self));
5799        }
5800        if self.kind().requires_ci() && self.ci().is_none() {
5801            return Err(crate::LayoutError::missing_ci(self));
5802        }
5803        Ok(())
5804    }
5805
5806    /// Reject per-entry values on the three Caixa-level code-surface
5807    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
5808    /// layout checker's `root.join(p)` sandbox would silently subvert.
5809    /// Same three structural footguns the peer
5810    /// [`BehaviorSpec::validate`] (b0c8389) and
5811    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
5812    /// (26da2c7) already close on the M2 `:behavior :on-*` and
5813    /// `:upgrade-from :state-change :script` axes, here lifted onto
5814    /// the three top-level code-path axes through the shared
5815    /// [`is_sandboxed_relative_path`] predicate:
5816    ///
5817    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
5818    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
5819    ///     [`Path::join`] as the base itself — `root.join("")` ==
5820    ///     `root`, so the existence check (`self.exists(&root)`)
5821    ///     trivially passes (the project root exists), and the layout
5822    ///     silently treats the project root as a biblioteca / exe /
5823    ///     servico entry. The `:bibliotecas` loop then hands the root
5824    ///     to `tatara_lisp::read` at `feira build` time as if the root
5825    ///     directory itself were a Lisp source file — a parse error
5826    ///     far from the source `caixa.lisp` with no field naming the
5827    ///     offending entry.
5828    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
5829    ///     [`Path::join`] *replaces* the base when the right-hand side
5830    ///     is absolute, so `root.join("/etc/passwd")` resolves to
5831    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
5832    ///     The existence check then silently consults whatever the
5833    ///     escaped path resolves to — for `:bibliotecas`, the layout
5834    ///     has no `starts_with`-fence (only `:exe` is fenced under
5835    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
5836    ///     `:bibliotecas` entry that happens to resolve on disk
5837    ///     silently passes. For `:exe` / `:servicos` the fence catches
5838    ///     the absolute case downstream as `ExeOutsideDir` /
5839    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
5840    ///     doesn't exist), but with a downstream-shaped diagnostic
5841    ///     that names the resolved escape path rather than the
5842    ///     authoring footgun at the source.
5843    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
5844    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
5845    ///     [`std::path::Component::ParentDir`] anywhere round-trips
5846    ///     through [`Path::join`] as a traversal above the caixa root.
5847    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
5848    ///     *component-aware* (not canonical-path-aware), so
5849    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
5850    ///     is **true** even though the canonical resolution
5851    ///     `{parent of root}/escape.lisp` lives outside the caixa root
5852    ///     — the fence silently lets the parent-escape through, and
5853    ///     the existence check passes if that escape-target happens
5854    ///     to exist. Caught regardless of where the `..` sits
5855    ///     (leading, mid-path, trailing) so the gate matches the peer
5856    ///     predicate's full coverage.
5857    ///
5858    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
5859    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
5860    /// same per-slot diagnostic shape every peer per-axis path-gate
5861    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
5862    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
5863    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
5864    /// order [`Caixa::declared_foreign_code_slots`] uses for its
5865    /// canonical foreign-code-slot diagnostic, so a manifest with
5866    /// multiple malformed slots surfaces the lexicographically-earliest
5867    /// slot's diagnostic deterministically.
5868    ///
5869    /// Lifted to the typed surface as a Caixa-level validator (peer
5870    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
5871    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
5872    /// and wired into [`crate::StandardLayout::verify`] before the
5873    /// existence-check loops so the diagnostic names the offending
5874    /// slot at the source caixa.lisp rather than reporting a
5875    /// downstream `MissingEntry` / `ExeOutsideDir` /
5876    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
5877    /// The fourth typed code-path surface — every author-supplied
5878    /// path on the manifest — is now structurally accept-shaped
5879    /// past validate, peer with `:behavior :on-*` and
5880    /// `:upgrade-from :state-change :script`.
5881    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
5882        /// Per-slot file-type contract for the three Caixa-level
5883        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
5884        /// Each variant names the predicate the per-entry file-type
5885        /// gate consults; [`Self::None`] opts the slot out of any
5886        /// file-type contract. Lifted as a typed local enum so the
5887        /// per-slot dispatch is exhaustive at the `match` — adding a
5888        /// future axis to the typed-substrate `:` slot set (the
5889        /// future `:assets` resource axis the M5 roadmap names, the
5890        /// future `:nix-flake` derivation axis the caixa-flake
5891        /// emitter consults) lands as one variant + one `match` arm,
5892        /// not a coordinated rewrite of every per-slot bool flag.
5893        ///
5894        /// Peer of the typed-substrate per-slot variant disciplines
5895        /// already established on this surface
5896        /// ([`crate::supervisor::RestartStrategy`] +
5897        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
5898        /// supervision-tree axis,
5899        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
5900        /// placement axis, [`crate::aplicacao::WitTarget`] on the
5901        /// `:contratos` payload-target axis): the typed `enum` is
5902        /// the substrate's single source of truth for the per-axis
5903        /// dispatch, and every consumer (the per-arm body here, the
5904        /// future feira-lint per-slot diagnostic renderer, the M4
5905        /// per-axis admission webhook) reaches for the same typed
5906        /// surface rather than re-deriving the partition from inline
5907        /// flag combinations.
5908        enum CodePathFileType {
5909            /// `:exe` — nix-build derivation output, no terminating-
5910            /// extension contract (the canonical `"exe/<name>"`
5911            /// fixtures the layout's `ExeOutsideDir` error message
5912            /// documents carry no extension by convention).
5913            None,
5914            /// `:bibliotecas` — tatara-lisp source files the
5915            /// `feira build` loop reads through `tatara_lisp::read`
5916            /// at parse time. Routes to [`is_lisp_extension`].
5917            LispSource,
5918            /// `:servicos` — ComputeUnit-CR YAML files the
5919            /// caixa-helm / caixa-flux renderers consume through
5920            /// `serde_yaml::from_str`. Routes to
5921            /// [`is_computeunit_yaml_extension`].
5922            ComputeUnitYaml,
5923        }
5924
5925        // The per-slot [`CodePathFileType`] selects which axes carry the
5926        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
5927        // source axis (the `feira build` loop at
5928        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
5929        // `tatara_lisp::read` at parse time) — the lifted
5930        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
5931        // `:exe` is the nix-built executable surface (per the canonical
5932        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
5933        // error message documents and every in-tree
5934        // `caixa_with_code_paths` positive control uses) — its file-type
5935        // contract is "nix-build derivation output", not a typed source
5936        // file, so [`CodePathFileType::None`] opts the slot out of any
5937        // file-type gate. `:servicos` is the `.computeunit.yaml`
5938        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
5939        // renderers consume each entry through `serde_yaml::from_str` as
5940        // a typed `ComputeUnit` CR) — the lifted
5941        // [`is_computeunit_yaml_extension`] predicate gates the compound
5942        // `.computeunit.yaml` suffix. All three axes are surfaced through
5943        // the same iteration so the sandbox-shape + duplicate gates
5944        // apply uniformly; the typed file-type dispatch fires per-slot
5945        // exactly where the downstream consumer's accepted set demands
5946        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
5947        // compounding lift on the peer 64772a9 `:bibliotecas`
5948        // `.lisp`-gate trajectory — the second of the three code-path
5949        // axes to land on a typed compound-suffix gate, with the same
5950        // self-locating per-slot diagnostic shape every peer per-axis
5951        // file-type lift uses (`*NonLispExtension { slot, path }` /
5952        // `*NonComputeUnitYamlExtension { slot, path }`).
5953        for (slot, list, file_type) in [
5954            (
5955                ":bibliotecas",
5956                &self.bibliotecas,
5957                CodePathFileType::LispSource,
5958            ),
5959            (":exe", &self.exe, CodePathFileType::None),
5960            (
5961                ":servicos",
5962                &self.servicos,
5963                CodePathFileType::ComputeUnitYaml,
5964            ),
5965        ] {
5966            // Per-slot set-not-multiset gate on the typed code-path axis.
5967            // Every peer Vec-shaped author-supplied list past validate is
5968            // a set, not a multiset: `:membros :caixa`
5969            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
5970            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
5971            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
5972            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
5973            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
5974            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
5975            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
5976            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
5977            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
5978            // the three code-path lists are the last Vec-shaped author-
5979            // supplied slots on the typed Caixa surface still admitting a
5980            // duplicate entry silently. Scope is per-list (`:bibliotecas`
5981            // duplicates are flagged within `:bibliotecas`, not across
5982            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
5983            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
5984            // legitimate dev-vs-runtime shape on the dep axis, fenced
5985            // separately by [`crate::dep::validate_no_self_dep`]). On the
5986            // code-path axis a cross-slot collision is structurally
5987            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
5988            // fence — `:exe` and `:servicos` entries are confined to their
5989            // own directory trees, so the only way a string could appear
5990            // on two code-path lists is the (rare, structurally invalid)
5991            // case where `:bibliotecas` carries an `"exe/<x>"` or
5992            // `"servicos/<x>.yaml"`-shaped path.
5993            //
5994            // Without the gate three authoring footguns silently passed:
5995            //
5996            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
5997            //     canonical copy-paste-the-wrong-file footgun. `feira
5998            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
5999            //     list and re-parses the same file twice, wasting work
6000            //     and silently masking the author's intent to declare a
6001            //     *second* biblioteca.
6002            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
6003            //     Binario surface. The future `caixa-flake` `nix flake`
6004            //     emitter that materializes each `:exe` entry as a flake
6005            //     `packages.<exe-name>` derivation would collide on the
6006            //     duplicate package name and surface a flake-eval error
6007            //     far from the source `caixa.lisp`.
6008            //   - `:servicos ("servicos/x.computeunit.yaml"
6009            //     "servicos/x.computeunit.yaml")` — the same footgun on
6010            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
6011            //     renderers already refuse `:servicos.len() != 1` with
6012            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
6013            //     that diagnostic surfaces "too many servicos" without
6014            //     naming "duplicate entry" — the typed self-locating
6015            //     "which entry is the duplicate" framing only lands at
6016            //     this gate.
6017            //
6018            // Same `seen.insert(entry.as_str())` shape every peer per-list
6019            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
6020            // 86c769b, `:deps` 359fba5) and the same "structural shape
6021            // checks fire before the duplicate check on the same entry"
6022            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
6023            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
6024            // empty entry first, not the duplicate on the later pair).
6025            let mut seen = std::collections::HashSet::new();
6026            for entry in list {
6027                let path = Path::new(entry);
6028                match is_sandboxed_relative_path(path) {
6029                    Ok(()) => {}
6030                    Err(PathShapeViolation::Empty) => {
6031                        return Err(ManifestError::CodePathEmpty { slot });
6032                    }
6033                    Err(PathShapeViolation::Absolute) => {
6034                        return Err(ManifestError::CodePathAbsolute {
6035                            slot,
6036                            path: path.to_path_buf(),
6037                        });
6038                    }
6039                    Err(PathShapeViolation::ParentEscape) => {
6040                        return Err(ManifestError::CodePathParentEscape {
6041                            slot,
6042                            path: path.to_path_buf(),
6043                        });
6044                    }
6045                }
6046                // The per-slot file-type gate dispatched through the
6047                // typed [`CodePathFileType`] selector above. Each variant
6048                // routes to the lifted predicate the downstream consumer
6049                // demands:
6050                //
6051                //   - [`LispSource`] → [`is_lisp_extension`] for
6052                //     `:bibliotecas` (the `feira build` loop's
6053                //     `tatara_lisp::read` consumer);
6054                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
6055                //     for `:servicos` (the caixa-helm / caixa-flux
6056                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
6057                //     accepted set);
6058                //   - [`None`] for `:exe` — the nix-build derivation-
6059                //     output axis has no terminating-extension contract.
6060                //
6061                // Fires after the sandbox-shape arms so a path that is
6062                // *both* sandbox-escaping and wrong-extension surfaces
6063                // the more fundamental sandbox-shape diagnostic first
6064                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
6065                // `ParentEscape` → `NonLispExtension` arm-ordering on
6066                // `:behavior :on-*` c97815a, and `EmptyScript` →
6067                // `AbsoluteScript` → `ParentEscapeScript` →
6068                // `NonLispExtensionScript` on
6069                // `:upgrade-from :state-change :script` 33cc830), and
6070                // before the duplicate gate so the narrower per-entry
6071                // file-type shape dominates the cross-entry uniqueness
6072                // diagnostic (a
6073                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
6074                // `:servicos` surfaces
6075                // `CodePathNonComputeUnitYamlExtension` on the first
6076                // entry rather than `CodePathDuplicate` on the pair —
6077                // peer with the 64772a9 `:bibliotecas`
6078                // `("lib/x.txt" "lib/x.txt")` ordering).
6079                match file_type {
6080                    CodePathFileType::None => {}
6081                    CodePathFileType::LispSource => {
6082                        if !is_lisp_extension(path) {
6083                            return Err(ManifestError::CodePathNonLispExtension {
6084                                slot,
6085                                path: path.to_path_buf(),
6086                            });
6087                        }
6088                    }
6089                    CodePathFileType::ComputeUnitYaml => {
6090                        if !is_computeunit_yaml_extension(path) {
6091                            return Err(ManifestError::CodePathNonComputeUnitYamlExtension {
6092                                slot,
6093                                path: path.to_path_buf(),
6094                            });
6095                        }
6096                    }
6097                }
6098                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
6099                    ManifestError::CodePathDuplicate {
6100                        slot,
6101                        path: path.to_path_buf(),
6102                    }
6103                })?;
6104            }
6105        }
6106        Ok(())
6107    }
6108
6109    /// Reject `:etiquetas` lists with an empty entry or with two entries
6110    /// agreeing on the same string. `:etiquetas` is the universal
6111    /// registry-search-tag axis on [`Caixa`] (every kind carries the
6112    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6113    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
6114    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
6115    /// a [`std::collections::BTreeSet`] alongside the four substrate-
6116    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
6117    /// Two authoring footguns silently passed validate without this gate:
6118    ///
6119    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
6120    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
6121    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
6122    ///     `chart.metadata.keywords` admits the value without a strict
6123    ///     parser-side gate, but the empty keyword has no operational
6124    ///     meaning — it indexes nothing in the future caixa-registry
6125    ///     search axis and clutters the rendered chart with a no-op tag.
6126    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
6127    ///     copy-paste-the-wrong-tag footgun) silently passed validate
6128    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
6129    ///     at chart render — a "second wins / one silently disappears"
6130    ///     shape divergent from every peer typed-graph set gate
6131    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
6132    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
6133    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6134    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6135    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6136    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
6137    ///     on `:upgrade-from`, the per-instruction-class singularity
6138    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
6139    ///     [`crate::UpgradeError::DuplicateStateChange`] /
6140    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
6141    ///     discipline is uniform: every Vec-shaped author-supplied list
6142    ///     past validate is set-not-multiset, by construction.
6143    ///
6144    /// Past the empty arm the gate enforces the chart-keyword shape
6145    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
6146    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
6147    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
6148    /// continuation. Closes the canonical paste-from-doc footguns the
6149    /// bare empty + duplicate arms left open: paste-from-aligned-doc
6150    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
6151    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
6152    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
6153    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
6154    /// — the author meant three separate list entries), path-separator
6155    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
6156    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
6157    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
6158    /// control bytes that would silently land as malformed search tags
6159    /// in the rendered Chart.yaml `keywords:` array and break the
6160    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
6161    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
6162    /// established on the sibling universal-axis `Vec<String>` surface
6163    /// — the second universal-axis Vec<String> surface to land the
6164    /// empty-first-then-shape-then-duplicate per-entry cascade.
6165    ///
6166    /// Same empty-first cascade discipline every peer per-axis gate
6167    /// uses: the per-entry empty arm fires before the per-entry shape
6168    /// arm fires before the cross-entry duplicate arm, so an
6169    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
6170    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
6171    /// has no value" defect) before either the shape or the duplicate
6172    /// diagnostic. Walks the list in declaration order so the
6173    /// first-collision diagnostic surfaces the lexicographically-
6174    /// earliest offending position, peer with every other duplicate
6175    /// gate on this surface.
6176    ///
6177    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
6178    /// caixa-build gate alongside the peer universal gates
6179    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6180    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
6181    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6182    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6183    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6184    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6185    /// slot sets. The future caixa-registry search axis can reach for
6186    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
6187    /// chart-keyword-shaped string without re-deriving the precondition.
6188    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
6189        let mut seen = std::collections::HashSet::new();
6190        for etiqueta in self.etiquetas() {
6191            if etiqueta.is_empty() {
6192                return Err(ManifestError::EtiquetaEmpty);
6193            }
6194            crate::render::is_chart_keyword_shape(etiqueta).map_err(|reason| {
6195                ManifestError::EtiquetaInvalid {
6196                    etiqueta: etiqueta.clone(),
6197                    reason,
6198                }
6199            })?;
6200            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
6201                ManifestError::EtiquetaDuplicate {
6202                    etiqueta: etiqueta.clone(),
6203                }
6204            })?;
6205        }
6206        Ok(())
6207    }
6208
6209    /// Reject `:autores` lists with an empty entry or with two entries
6210    /// agreeing on the same string. `:autores` is the universal
6211    /// maintainer-axis on [`Caixa`] (every kind carries the
6212    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6213    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
6214    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
6215    /// to a `Maintainer { name, email: None }` without dedup). Two
6216    /// authoring footguns silently passed validate without this gate:
6217    ///
6218    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
6219    ///     blank-doc footgun) rendered as
6220    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
6221    ///     empty maintainer name has no operational meaning — it
6222    ///     identifies no one in the substrate's authorship index and
6223    ///     clutters the rendered chart with a no-op maintainer.
6224    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
6225    ///     the copy-paste-the-wrong-author footgun) silently passed
6226    ///     validate and rendered as two identical maintainer entries.
6227    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
6228    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
6229    ///     rendered `keywords:` array at chart-render time), the
6230    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
6231    ///     entries stack verbatim in the chart, divergent from every
6232    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
6233    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
6234    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6235    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6236    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6237    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
6238    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
6239    ///     `:etiquetas`).
6240    ///
6241    /// Past the empty arm the gate enforces the chart-maintainer-name
6242    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
6243    /// the structural single-line printable-UTF-8 floor every realistic
6244    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
6245    /// or trailing whitespace, no ASCII control characters anywhere,
6246    /// Unicode bytes accepted. Closes the canonical paste-from-doc
6247    /// footguns the bare empty + duplicate arms left open:
6248    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
6249    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
6250    /// pasted a multi-line block of author records into one `:autores`
6251    /// entry instead of splitting into one entry per author),
6252    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
6253    /// and the paste-from-binary-blob control bytes that would silently
6254    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
6255    /// `maintainers:` array. Mirrors the shape-predicate cascade
6256    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6257    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
6258    /// establish past their own empty arms on the sibling universal-axis
6259    /// `Option<String>` surfaces — the first universal-axis Vec<String>
6260    /// surface to land the empty-first-then-shape-then-duplicate per-entry
6261    /// cascade.
6262    ///
6263    /// Same empty-first cascade discipline every peer per-axis gate
6264    /// uses: the per-entry empty arm fires before the per-entry shape
6265    /// arm before the cross-entry duplicate arm. Walks the list in
6266    /// declaration order so the first-collision diagnostic surfaces the
6267    /// lexicographically-earliest offending position, peer with every
6268    /// other duplicate gate on this surface.
6269    ///
6270    /// Universal-axis (every kind carries `:autores`), so wired at the
6271    /// caixa-build gate alongside the peer universal gates
6272    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6273    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6274    /// [`Self::validate_code_paths`] — before the kind-coherence gates
6275    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6276    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6277    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6278    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6279    /// slot sets.
6280    pub fn validate_autores(&self) -> Result<(), ManifestError> {
6281        let mut seen = std::collections::HashSet::new();
6282        for autor in self.autores() {
6283            if autor.is_empty() {
6284                return Err(ManifestError::AutorEmpty);
6285            }
6286            crate::render::is_chart_maintainer_name_shape(autor).map_err(|reason| {
6287                ManifestError::AutorInvalid {
6288                    autor: autor.clone(),
6289                    reason,
6290                }
6291            })?;
6292            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
6293                ManifestError::AutorDuplicate {
6294                    autor: autor.clone(),
6295                }
6296            })?;
6297        }
6298        Ok(())
6299    }
6300
6301    /// Reject `:repositorio` values whose shape the shared
6302    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
6303    /// `repositorio: Option<String>` slot on [`Caixa`] is the
6304    /// universal git-shaped homepage axis every kind carries — the
6305    /// substrate routes the same string through two load-bearing
6306    /// consumers:
6307    ///
6308    ///   - [`caixa-helm`] folds it verbatim into the rendered
6309    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
6310    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
6311    ///     the chart `README.md` `repo = …` interpolation
6312    ///     (`caixa-helm/src/lib.rs:359`).
6313    ///   - [`caixa-flux`] folds it verbatim into the standalone
6314    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
6315    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
6316    ///     `GitRepository.spec.url` the cluster's source-controller
6317    ///     polls — the load-bearing deploy-time axis.
6318    ///
6319    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6320    /// substitute a placeholder when the slot is absent (`None` → the
6321    /// fallback fires); a `Some("")` *skips the fallback* and silently
6322    /// passes the empty string through to `Chart.yaml home: ""` /
6323    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
6324    /// controller both reject the empty URL far from the source
6325    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
6326    /// Similarly a malformed `:repositorio` (whitespace, control char,
6327    /// missing `:` separator, leading `-`) silently lands in the
6328    /// rendered artifacts and breaks at `git clone` / `helm template`
6329    /// / `flux reconcile` time.
6330    ///
6331    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
6332    /// same shared predicate the peer [`crate::DepSource::validate`]
6333    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
6334    /// gate the two `git URL`-shaped surfaces on the typed Caixa
6335    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
6336    /// structurally equivalent: every value past validate is
6337    /// guaranteed-acceptable by the predicate's union of constraints
6338    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
6339    /// control chars, ASCII only, no leading `:`, contains a `:`
6340    /// separator). The predicate accepts every documented authoring
6341    /// shape — `github:org/repo` shorthand, `https://host/path`,
6342    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
6343    /// scp-style SSH, `file:///path` — and refuses the canonical
6344    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
6345    /// injection footguns at validate time. Maps the predicate's
6346    /// `String` reason verbatim into the
6347    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
6348    /// offending value + parser-shaped reason so the diagnostic is
6349    /// self-locating (the author can grep their `caixa.lisp` for
6350    /// `:repositorio "<value>"` and fix it in one edit).
6351    ///
6352    /// `None` (the canonical "omit the slot to express no published
6353    /// homepage" shape) is accepted trivially — the gate is a no-op
6354    /// when the author didn't declare a value. `Some("")` is gated by
6355    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
6356    /// shape predicate is consulted, mirroring the empty-first cascade
6357    /// every peer per-axis identity gate uses
6358    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
6359    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
6360    /// [`crate::DepError::FonteRepoEmpty`] →
6361    /// [`crate::DepError::FonteRepoInvalid`]).
6362    ///
6363    /// Universal-axis (every kind carries `:repositorio`), so wired at
6364    /// the caixa-build gate alongside the peer universal gates
6365    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6366    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6367    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
6368    /// before the kind-coherence gates
6369    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6370    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6371    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6372    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6373    /// specific slot sets.
6374    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
6375        let Some(s) = self.repositorio() else {
6376            return Ok(());
6377        };
6378        if s.is_empty() {
6379            return Err(ManifestError::RepositorioEmpty);
6380        }
6381        is_git_repo_url(s).map_err(|reason| ManifestError::RepositorioInvalid {
6382            repositorio: s.to_string(),
6383            reason,
6384        })
6385    }
6386
6387    /// Reject `:descricao` values that are the empty string. The flat
6388    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
6389    /// free-form-prose homepage axis every kind carries — the
6390    /// substrate routes the same string through two load-bearing
6391    /// consumers in the [`caixa-helm`] renderer:
6392    ///
6393    ///   - `build_chart_yaml` folds it verbatim into the rendered
6394    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
6395    ///     field (`caixa-helm/src/lib.rs:232-235`).
6396    ///   - `build_readme` folds it verbatim into the rendered chart
6397    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
6398    ///
6399    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6400    /// substitute a `caixa.nome`-derived placeholder when the slot is
6401    /// absent (`None` → the fallback fires); a `Some("")` *skips the
6402    /// fallback* and silently passes the empty string through to
6403    /// `Chart.yaml description: ""` / a blank chart `README.md`
6404    /// header. Helm's chart spec requires a non-empty `description:`
6405    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
6406    /// `WARNING [chart.metadata.description]: description is required`),
6407    /// so the empty `Some("")` silently lands in the rendered
6408    /// artifacts and breaks at `helm lint` / `helm install` time far
6409    /// from the source `caixa.lisp`, with no field naming the
6410    /// offending `:descricao`.
6411    ///
6412    /// `None` (the canonical "omit the slot to defer to the renderer's
6413    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
6414    /// the gate is a no-op when the author didn't declare a value.
6415    /// `Some("")` is gated by the narrower
6416    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
6417    /// shape every peer per-axis empty gate uses
6418    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6419    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6420    /// [`ManifestError::RepositorioEmpty`]).
6421    ///
6422    /// Universal-axis (every kind carries `:descricao`), so wired at
6423    /// the caixa-build gate alongside the peer universal gates
6424    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6425    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6426    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6427    /// [`Self::validate_code_paths`] — before the kind-coherence
6428    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6429    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6430    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6431    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6432    /// specific slot sets.
6433    ///
6434    /// Past the empty arm the gate enforces the chart-description
6435    /// shape predicate via [`crate::render::is_chart_description_shape`]:
6436    /// the structural single-line UTF-8 floor every realistic chart
6437    /// description in the wild matches — 1..=512 bytes, no leading
6438    /// or trailing whitespace, no ASCII control characters anywhere
6439    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
6440    /// carriage return, and every other control byte), Unicode
6441    /// continuation bytes accepted (the canonical fixtures carry
6442    /// `→` and `—`). Closes the canonical paste-from-doc footguns
6443    /// the bare empty-arm gate left open: paste-from-aligned-doc
6444    /// leading / trailing whitespace (`" Checkout flow."`,
6445    /// `"Checkout flow. "`), paste-from-multiline-doc newline
6446    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
6447    /// (`"Checkout\rflow."`), tab-from-aligned-doc
6448    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
6449    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
6450    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
6451    /// [`Self::validate_edicao`] establish past their own empty arms
6452    /// on the sibling universal-axis `Option<String>` Caixa-level
6453    /// value-shape surfaces.
6454    ///
6455    /// The empty-first cascade discipline mirrors every peer per-axis
6456    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
6457    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
6458    /// diagnostic surfaces on `Some("")` rather than the broader
6459    /// shape-predicate diagnostic — peer with how
6460    /// [`ManifestError::LicencaEmpty`] runs before
6461    /// [`ManifestError::LicencaInvalid`],
6462    /// [`ManifestError::EdicaoEmpty`] runs before
6463    /// [`ManifestError::EdicaoInvalid`],
6464    /// [`ManifestError::RepositorioEmpty`] runs before
6465    /// [`ManifestError::RepositorioInvalid`].
6466    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
6467        let Some(s) = self.descricao() else {
6468            return Ok(());
6469        };
6470        if s.is_empty() {
6471            return Err(ManifestError::DescricaoEmpty);
6472        }
6473        crate::render::is_chart_description_shape(s).map_err(|reason| {
6474            ManifestError::DescricaoInvalid {
6475                descricao: s.to_string(),
6476                reason,
6477            }
6478        })?;
6479        Ok(())
6480    }
6481
6482    /// Reject `:licenca` values that are the empty string. The flat
6483    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
6484    /// SPDX-shaped license-expression axis every kind carries — the
6485    /// substrate routes the same string through the [`caixa-helm`]
6486    /// renderer's `build_readme` which folds it verbatim into the
6487    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
6488    /// section (`caixa-helm/src/lib.rs:361`) via
6489    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
6490    /// fallback only fires on `None`; a `Some("")` *skips the
6491    /// fallback* and silently passes the empty string through to a
6492    /// chart `README.md` whose `License` section renders as the bare
6493    /// trailing period (`.\n`) — peer footgun with the
6494    /// `Some("")`-skips-`unwrap_or_else` shape the
6495    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
6496    /// gates close on the sibling free-form-prose and git-URL axes.
6497    ///
6498    /// `None` (the canonical "omit the slot to defer to the
6499    /// renderer's `MIT` fallback" shape every existing fixture
6500    /// carries) is accepted trivially — the gate is a no-op when the
6501    /// author didn't declare a value. `Some("")` is gated by the
6502    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
6503    /// empty-arm shape every peer per-axis empty gate uses
6504    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6505    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6506    /// [`ManifestError::RepositorioEmpty`],
6507    /// [`ManifestError::DescricaoEmpty`]).
6508    ///
6509    /// Universal-axis (every kind carries `:licenca`), so wired at
6510    /// the caixa-build gate alongside the peer universal gates
6511    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6512    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6513    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6514    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
6515    /// — before the kind-coherence gates
6516    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6517    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6518    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6519    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6520    /// specific slot sets.
6521    ///
6522    /// Past the empty arm the gate enforces the SPDX-expression shape
6523    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
6524    /// structural alphabet floor every realistic SPDX expression in
6525    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
6526    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
6527    /// single ASCII space (token separator). Closes the canonical
6528    /// paste-from-doc footguns the bare empty-arm gate left open:
6529    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
6530    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
6531    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
6532    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
6533    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
6534    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
6535    /// Apache-2.0"`), and semicolon-list-separator confusion
6536    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
6537    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
6538    /// establish past their own empty arms.
6539    ///
6540    /// The empty-first cascade discipline mirrors every peer per-axis
6541    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
6542    /// [`ManifestError::LicencaInvalid`], so the narrower empty
6543    /// diagnostic surfaces on `Some("")` rather than the broader
6544    /// shape-predicate diagnostic — peer with how
6545    /// [`ManifestError::EdicaoEmpty`] runs before
6546    /// [`ManifestError::EdicaoInvalid`],
6547    /// [`ManifestError::RepositorioEmpty`] runs before
6548    /// [`ManifestError::RepositorioInvalid`].
6549    ///
6550    /// A future tightening on this axis can extend the alphabet
6551    /// floor into a full SPDX expression parser + license-id
6552    /// allowlist (rejecting alphabet-valid values that don't name a
6553    /// real SPDX license identifier — e.g., `"NotAReal"` is
6554    /// alphabet-valid but no `NotAReal` license-id exists). That
6555    /// parser only becomes meaningful past a real SPDX-spec
6556    /// dependency; this gate establishes the structural floor by
6557    /// refusing every non-SPDX-alphabet value at validate time.
6558    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
6559        let Some(s) = self.licenca() else {
6560            return Ok(());
6561        };
6562        if s.is_empty() {
6563            return Err(ManifestError::LicencaEmpty);
6564        }
6565        crate::render::is_spdx_expression_shape(s).map_err(|reason| {
6566            ManifestError::LicencaInvalid {
6567                licenca: s.to_string(),
6568                reason,
6569            }
6570        })?;
6571        Ok(())
6572    }
6573
6574    /// Reject `:edicao` values that are the empty string. The flat
6575    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
6576    /// language-edition axis every kind carries — it determines the
6577    /// tatara-lisp macro surface + compatibility flags the substrate
6578    /// applies when building a caixa, and lands verbatim in the
6579    /// `Caixa::template` author-time scaffold (the canonical
6580    /// `:edicao "2026"` line every `feira init` emits via
6581    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
6582    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
6583    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
6584    /// `caixa-core/src/render.rs:2510`) via
6585    /// `edicao: Some("2026".into())`.
6586    ///
6587    /// `None` (the canonical "omit the slot to defer to the
6588    /// substrate's default edition" shape every existing
6589    /// [`caixa-resolver`] integration test fixture carries via
6590    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6591    /// is accepted trivially — the gate is a no-op when the author
6592    /// didn't declare a value. `Some("")` is gated by the narrower
6593    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
6594    /// shape every peer per-axis empty gate uses
6595    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6596    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6597    /// [`ManifestError::RepositorioEmpty`],
6598    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
6599    ///
6600    /// Universal-axis (every kind carries `:edicao`), so wired at
6601    /// the caixa-build gate alongside the peer universal gates
6602    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6603    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6604    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6605    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6606    /// [`Self::validate_code_paths`] — before the kind-coherence
6607    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6608    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6609    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6610    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6611    /// specific slot sets.
6612    ///
6613    /// Past the empty arm the gate enforces the canonical year-shape
6614    /// predicate: every documented tatara-lisp edition is a 4-digit
6615    /// ASCII decimal year (`"2026"` is the only edition currently
6616    /// minted; future-introduced siblings will follow the same
6617    /// shape, peer with Cargo's `[package] edition` grammar which
6618    /// every value Cargo has ever accepted matches — `"2015"`,
6619    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
6620    /// 4 ASCII decimal bytes is rejected with the narrower
6621    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
6622    /// shape-predicate cascade [`Self::validate_repositorio`]
6623    /// establishes past its own empty arm
6624    /// ([`ManifestError::RepositorioEmpty`] →
6625    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
6626    /// paste-from-doc footguns the bare empty-arm gate left open:
6627    ///
6628    ///   - leading / trailing whitespace from a paste-from-doc
6629    ///     (`"2026 "`, `" 2026"`)
6630    ///   - control characters / CRLF from a paste-from-multiline-doc
6631    ///     (`"2026\n"`)
6632    ///   - non-ASCII look-alikes from a fullwidth keyboard
6633    ///     (`"2026"`) which would silently land as a non-ASCII
6634    ///     string in the rendered caixa.lisp
6635    ///   - free-form non-year values (`"x"`, `"latest"`,
6636    ///     `"nightly"`) that have no operational meaning on the
6637    ///     substrate's build-time edition selector
6638    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
6639    ///     `"r2026"`) — common version-tag idioms that don't apply
6640    ///     to the year-shaped edition axis
6641    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
6642    ///     edition is a year, not a fractional version
6643    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
6644    ///     `"00026"`) that don't name a year
6645    ///
6646    /// `None` (the canonical "omit the slot to defer to the
6647    /// substrate's default edition" shape every existing
6648    /// [`caixa-resolver`] integration test fixture carries via
6649    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6650    /// is accepted trivially — the gate is a no-op when the author
6651    /// didn't declare a value. The empty-first cascade discipline
6652    /// mirrors every peer per-axis identity gate:
6653    /// [`ManifestError::EdicaoEmpty`] runs before
6654    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
6655    /// diagnostic surfaces on `Some("")` rather than the broader
6656    /// shape-predicate diagnostic — peer with how
6657    /// [`ManifestError::NomeEmpty`] runs before
6658    /// [`ManifestError::NomeInvalid`],
6659    /// [`ManifestError::VersaoEmpty`] runs before
6660    /// [`ManifestError::VersaoInvalid`],
6661    /// [`ManifestError::RepositorioEmpty`] runs before
6662    /// [`ManifestError::RepositorioInvalid`].
6663    ///
6664    /// A future tightening on this axis can extend the shape
6665    /// predicate into a known-edition allowlist (rejecting
6666    /// year-shaped values that don't name a tatara-lisp edition
6667    /// the substrate actually understands — e.g., `"1999"` is
6668    /// year-shaped but no `1999` edition exists). That allowlist
6669    /// only becomes meaningful past the introduction of a sibling
6670    /// edition to `"2026"`; this gate establishes the structural
6671    /// floor by refusing every non-year-shaped value at validate
6672    /// time.
6673    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
6674        let Some(s) = self.edicao() else {
6675            return Ok(());
6676        };
6677        if s.is_empty() {
6678            return Err(ManifestError::EdicaoEmpty);
6679        }
6680        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
6681            return Err(ManifestError::EdicaoInvalid {
6682                edicao: s.to_string(),
6683                reason: "must be a 4-digit ASCII decimal year (canonical \"2026\")".to_string(),
6684            });
6685        }
6686        Ok(())
6687    }
6688
6689    /// Compose the supervisor-related flat slots into a single
6690    /// [`SupervisorSpec`] for validation. Returns `None` when the
6691    /// caixa isn't a `:kind Supervisor`.
6692    ///
6693    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
6694    /// simple (one form, no nested `:supervisor (…)` block); this view
6695    /// is the "typed shape" the operator + supervisor reconciler
6696    /// consume.
6697    #[must_use]
6698    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
6699        if !self.kind().is_supervisor() {
6700            return None;
6701        }
6702        // Fold through the shared `supervisor::duration_codec::parse`
6703        // — the same parser the serde-routed `with = "duration_codec"`
6704        // on `SupervisorSpec::restart_window`, the `:politicas
6705        // :timeout` codec, and the `:politicas :circuit-breaker
6706        // :window` codec all consume. The prior inline f64-shaped
6707        // duplicate (`parse_window_inline`) admitted every magnitude
6708        // the integer-magnitude gate (1c55a2a) rejects on the three
6709        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
6710        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
6711        // `None` (i.e. "no reset"), divergent from the shared codec's
6712        // integer-magnitude discipline by construction. The fold
6713        // closes the divergence: every value the typed
6714        // `SupervisorSpec` carries past `supervisor_view` is in the
6715        // shared codec's accepted set. The `.ok()` here preserves the
6716        // existing soft-swallow shape on this view-construction path;
6717        // the new [`Caixa::validate_restart_window`] (sibling of
6718        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
6719        // the offending raw string at build time so authoring tools
6720        // (`feira lint`, the future layout-side wire-up) surface a
6721        // self-locating diagnostic instead of a silently dropped
6722        // window.
6723        let restart_window = self
6724            .restart_window()
6725            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
6726        Some(SupervisorSpec {
6727            // Route the author-omitted `:estrategia` arm through the
6728            // substrate-canonical
6729            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
6730            // `pub const` rather than the transitively-derived
6731            // [`RestartStrategy::default`] route the prior
6732            // `.unwrap_or_default()` fold reached for — one source of
6733            // truth for the Erlang/OTP `one_for_one` half of Learn You
6734            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
6735            // supervisor canonical default that also backs the
6736            // [`crate::supervisor::Default for RestartStrategy`] impl
6737            // and the [`crate::supervisor::Default for SupervisorSpec`]
6738            // impl's struct-literal `estrategia` field, all now routed
6739            // through the same lifted constant. Prior to the lift the
6740            // composition site carried `.unwrap_or_default()` with no
6741            // compile-time link back to the shared OTP-canonical
6742            // default that the peer paired
6743            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
6744            // arm on the sibling `:max-restarts` axis routes through —
6745            // so a future rebrand of the OTP-canonical strategy default
6746            // (a widening to `rest_for_one` once the substrate
6747            // discovers startup-order-coupled child cohorts as the more
6748            // common shape, a per-cluster overlay the operator pins
6749            // through the MESH-COMPOSITION §III.2 supervision-canary
6750            // `:estrategia-overrides` roadmap slot) would have had to
6751            // migrate the paired `MaxIntensity` + `Period` halves
6752            // through the lifted constants and the `one_for_one` half
6753            // through a `RestartStrategy::default()` route in lockstep
6754            // or the three halves of the same OTP-canonical default
6755            // would silently drift out of pairing. Byte-parity against
6756            // the lifted constant closes the split. Pinned by
6757            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
6758            // in the tests module.
6759            estrategia: self
6760                .estrategia()
6761                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
6762            // Route the author-omitted `:max-restarts` arm through the
6763            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
6764            // typed `pub const` rather than the raw `5` literal — one
6765            // source of truth for the Erlang/OTP-canonical
6766            // `{intensity, 5, 60}` `MaxIntensity` default that also
6767            // backs the serde-side wire-format author-omitted arm on
6768            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
6769            // `#[serde(default = "default_max_restarts")]` and the
6770            // [`Default for SupervisorSpec`] impl's struct-literal
6771            // default field. Prior to the lift the composition site
6772            // carried a raw `5` with no compile-time link back to the
6773            // serde-side default, so a future rebrand of the OTP-
6774            // canonical default (a tightening to Elixir's `3`, a
6775            // widening to a per-cluster overlay the operator pins
6776            // through the MESH-COMPOSITION §III.2 supervision-canary
6777            // `:supervisor :max-restarts-overrides` roadmap slot)
6778            // would have had to be threaded through both open-coded
6779            // copies in lockstep or the wire-format author-omitted arm
6780            // and this view-construction author-omitted arm would
6781            // silently disagree on which restart-budget an omitted
6782            // `:max-restarts` resolves to. Pinned by
6783            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
6784            // in the tests module.
6785            max_restarts: self
6786                .max_restarts()
6787                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
6788            restart_window,
6789            children: self.children().to_vec(),
6790        })
6791    }
6792
6793    /// A minimal starter manifest emitted by `feira init`.
6794    #[must_use]
6795    pub fn template(nome: &str) -> String {
6796        format!(
6797            "(defcaixa\n  \
6798               :nome        {nome:?}\n  \
6799               :versao      \"0.1.0\"\n  \
6800               :kind        Biblioteca\n  \
6801               :edicao      \"2026\"\n  \
6802               :descricao   \"FIXME — describe this caixa\"\n  \
6803               :autores     ()\n  \
6804               :etiquetas   ()\n  \
6805               :deps        ()\n  \
6806               :deps-dev    ()\n  \
6807               :bibliotecas (\"lib/{nome}.lisp\"))\n"
6808        )
6809    }
6810
6811    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
6812    /// back after mutation (e.g. `feira add`).
6813    ///
6814    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
6815    /// The derive-macro `compile_from_sexp` path is the inverse, so any
6816    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
6817    #[must_use]
6818    pub fn to_lisp(&self) -> String {
6819        let json = serde_json::to_value(self).expect("Caixa serialize");
6820        let sexp = tatara_lisp::domain::json_to_sexp(&json);
6821        let tatara_lisp::Sexp::List(items) = sexp else {
6822            return format!("(defcaixa {sexp})\n");
6823        };
6824        let mut out = String::from("(defcaixa");
6825        let mut i = 0;
6826        while i + 1 < items.len() {
6827            out.push_str("\n  ");
6828            out.push_str(&items[i].to_string());
6829            out.push(' ');
6830            out.push_str(&items[i + 1].to_string());
6831            i += 2;
6832        }
6833        out.push_str(")\n");
6834        out
6835    }
6836}
6837
6838/// Errors raised by top-level [`Caixa`] validators that don't fit
6839/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
6840/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
6841/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
6842/// through every substrate-side artifact's `metadata.name` /
6843/// version derivation.
6844///
6845/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
6846/// doc-comment anticipates) can hold one of each per-axis error
6847/// family without reshaping individual diagnostics; this enum is
6848/// the first such per-Caixa-identity family.
6849#[derive(Debug, Error, PartialEq, Eq)]
6850pub enum ManifestError {
6851    #[error(
6852        ":nome is empty (every caixa must name itself; the value flows \
6853         into every K8s artifact's `metadata.name` derivation and into \
6854         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
6855    )]
6856    NomeEmpty,
6857    #[error(
6858        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
6859         apiserver enforces this rule on every `metadata.name` the \
6860         caixa's substrate-side renderers derive from `:nome` — the \
6861         `lareira-<nome>` Helm chart name, the programs.yaml entry \
6862         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
6863         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
6864         name; use a lowercase alphanumeric + hyphen identifier like \
6865         `\"checkout\"` or `\"cart-v2\"`)"
6866    )]
6867    NomeInvalid { nome: String, reason: String },
6868    #[error(
6869        ":nome {nome:?} overflows the joint-length budget on the canonical \
6870         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
6871         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
6872         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
6873         `chart:` slot, `caixa-tatara`'s `release_name` + \
6874         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
6875         joint name through the canonical `lareira_chart_name` helper, and \
6876         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
6877         DNS-1123 label cap on every chart-name-derived `metadata.name` \
6878         reject any joint name exceeding 63 bytes; the narrower \
6879         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
6880         arm gates the chart-name budget downstream renderers inherit)"
6881    )]
6882    NomeChartNameBudgetExceeded { nome: String, reason: String },
6883    #[error(
6884        ":versao is empty (every caixa must pin its own version; the value flows \
6885         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
6886         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
6887         `:latest` tags, the lacre closure's `concrete_versao`, and the \
6888         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
6889    )]
6890    VersaoEmpty,
6891    #[error(
6892        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
6893         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
6894         with optional `-prerelease` and `+build` — across every artifact derived \
6895         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
6896         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
6897         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
6898         and the `:upgrade-from :from` peers that match against this exact shape; \
6899         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
6900         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
6901         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
6902    )]
6903    VersaoInvalid { versao: String, reason: String },
6904    #[error(
6905        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
6906         substrate consumes this string through the shared \
6907         `supervisor::duration_codec` — the same parser routed via `with = \
6908         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
6909         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
6910         the canonical authoring form is `<integer><unit>` where the unit is one \
6911         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
6912         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
6913         Without this gate a malformed `:restart-window` silently produced a \
6914         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
6915         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
6916         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
6917         layer with the offending value named verbatim. Omit the slot entirely to \
6918         express \"no reset\"; carry a positive integer duration to express the \
6919         sliding window)"
6920    )]
6921    RestartWindowMalformed {
6922        restart_window: String,
6923        reason: String,
6924    },
6925    #[error(
6926        "{slot} entry is an empty path string — every {slot} entry must name \
6927         a file relative to the caixa root; omit the entry to omit the file \
6928         (the layout checker's `root.join(\"\")` resolves to the caixa root \
6929         itself, so an empty entry silently aliases the project root as a \
6930         declared {slot} file, then fails downstream at parse / existence \
6931         time with a diagnostic that names the root rather than the offending \
6932         entry)"
6933    )]
6934    CodePathEmpty { slot: &'static str },
6935    #[error(
6936        "{slot} entry {} is an absolute path — entries must be relative to \
6937         the caixa root, since `Path::join` replaces the base with an absolute \
6938         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
6939         outside the caixa root sandbox; rewrite the entry as a relative path \
6940         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
6941         `\"servicos/<name>.computeunit.yaml\"`)",
6942        path.display()
6943    )]
6944    CodePathAbsolute { slot: &'static str, path: PathBuf },
6945    #[error(
6946        "{slot} entry {} contains a `..` component — entries must not traverse \
6947         above the caixa root (the layout's `starts_with(<dir>)` fence on \
6948         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
6949         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
6950         has no such fence, so a leading `..` escapes unconditionally if the \
6951         resolved target happens to exist)",
6952        path.display()
6953    )]
6954    CodePathParentEscape { slot: &'static str, path: PathBuf },
6955    #[error(
6956        "{slot} entry {} does not terminate in the `.lisp` extension — every \
6957         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
6958         loop reads through `tatara_lisp::read` at parse time, so any other \
6959         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
6960         structurally a parser error far from the source caixa.lisp, with \
6961         no field naming the offending `:bibliotecas` entry. Pin a relative \
6962         path under the caixa root whose terminating extension is \
6963         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
6964         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
6965         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
6966         (33cc830) axes already carry through the same lifted \
6967         `is_lisp_extension` predicate",
6968        path.display()
6969    )]
6970    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
6971    #[error(
6972        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
6973         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
6974         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
6975         through `serde_yaml::from_str` at chart / FluxCD bundle render \
6976         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
6977         off-by-one-segment `.computeunit-yaml`, the editor-backup \
6978         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
6979         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
6980         source caixa.lisp, with no field naming the offending `:servicos` \
6981         entry. Pin a relative path under the caixa root whose terminating \
6982         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
6983         `\"servicos/<name>.computeunit.yaml\"`, \
6984         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
6985         contract the sibling `:bibliotecas` axis (64772a9) already carries \
6986         on the tatara-lisp-source axis through the peer lifted \
6987         `is_lisp_extension` predicate, here on the compound-suffix axis \
6988         `Path::extension` can't express on its own through the lifted \
6989         `is_computeunit_yaml_extension` predicate",
6990        path.display()
6991    )]
6992    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
6993    #[error(
6994        "{slot} entry {} appears more than once (the code-path list is \
6995         a set, not a multiset; every peer Vec-shaped author-supplied \
6996         list past validate is set-not-multiset — `:membros :caixa`, \
6997         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6998         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
6999         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
7000         code-path lists are the last Vec-shaped author-supplied slots on \
7001         the typed Caixa surface still admitting a duplicate entry. \
7002         `:bibliotecas` duplicates re-parse the same file at \
7003         `feira build` time and silently mask the author's intent to \
7004         declare a *second* biblioteca; `:exe` duplicates collide on the \
7005         flake `packages.<name>` derivation key at the future \
7006         `caixa-flake` materializer; `:servicos` duplicates surface as the \
7007         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
7008         rejection far from the source `caixa.lisp`. Drop the duplicate \
7009         or rename it to the actual second file intended)",
7010        path.display()
7011    )]
7012    CodePathDuplicate { slot: &'static str, path: PathBuf },
7013    #[error(
7014        ":etiquetas entry is empty (every tag must carry a non-empty \
7015         registry-search identifier; the empty entry has no operational \
7016         meaning — it indexes nothing in the future caixa-registry search \
7017         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
7018         with a no-op tag; omit the entry to express \"no tag on this \
7019         position\")"
7020    )]
7021    EtiquetaEmpty,
7022    #[error(
7023        ":etiquetas entry {etiqueta:?} appears more than once (the \
7024         registry-search tag set is a set, not a multiset; duplicate \
7025         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
7026         at chart render — a \"second wins / one silently disappears\" \
7027         shape divergent from every peer typed-graph set gate \
7028         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
7029         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
7030         duplicate or rename it to the actual tag intended)"
7031    )]
7032    EtiquetaDuplicate { etiqueta: String },
7033    #[error(
7034        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
7035         {reason} (the substrate consumes this string through the shared \
7036         `crate::render::is_chart_keyword_shape` predicate — the same \
7037         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
7038         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
7039         continuation. The canonical authoring shapes are short kebab-case \
7040         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
7041         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
7042         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
7043         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
7044         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
7045         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
7046         `\"mesh,http,grpc\"` — the author meant to author three separate \
7047         list entries; path-separator confusion `\"caixa/servico\"`; \
7048         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
7049         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
7050         `\"café\"` — every legitimate search tag is strict ASCII; \
7051         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
7052         passed `from_lisp` + `validate_etiquetas` + \
7053         `StandardLayout::verify` and landed in the rendered \
7054         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
7055         malformed search tag — Artifact Hub's keyword index + the future \
7056         caixa-registry's keyword index would either silently drop the \
7057         tag or fail to index it far from the source caixa.lisp; the gate \
7058         moves the diagnostic to the manifest layer with the offending \
7059         value named verbatim)"
7060    )]
7061    EtiquetaInvalid { etiqueta: String, reason: String },
7062    #[error(
7063        ":autores entry is empty (every maintainer must carry a non-empty \
7064         identifier; the empty entry has no operational meaning — it \
7065         identifies no one in the substrate's authorship index and renders \
7066         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
7067         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
7068         omit the entry to express \"no maintainer on this position\")"
7069    )]
7070    AutorEmpty,
7071    #[error(
7072        ":autores entry {autor:?} appears more than once (the maintainer \
7073         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
7074         `maintainers:` rendering does *no* dedup — duplicate entries \
7075         stack verbatim in `Chart.yaml` as two identical \
7076         `Maintainer {{ name, email: None }}` records, divergent from every \
7077         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
7078         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
7079         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
7080         rename it to the actual author intended)"
7081    )]
7082    AutorDuplicate { autor: String },
7083    #[error(
7084        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
7085         {reason} (the substrate consumes this string through the shared \
7086         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
7087         single-line-UTF-8 floor every realistic chart maintainer name carries: \
7088         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
7089         characters anywhere, Unicode bytes accepted. The canonical authoring \
7090         shapes are short single-line identifiers like `\"pleme-io\"`, \
7091         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
7092         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
7093         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
7094         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
7095         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
7096         records into one entry instead of splitting into one entry per author; \
7097         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
7098         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
7099         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7100         `validate_autores` + `StandardLayout::verify` and landed in the \
7101         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
7102         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
7103         round-trip — every chart-aware UI (`helm list`, `helm search`, \
7104         Artifact Hub maintainer index) would render the maintainer name in a \
7105         single-line column far from the source caixa.lisp; the gate moves the \
7106         diagnostic to the manifest layer with the offending value named \
7107         verbatim)"
7108    )]
7109    AutorInvalid { autor: String, reason: String },
7110    #[error(
7111        ":repositorio is the empty string (every published caixa names its \
7112         git source via a non-empty `:repositorio` locator — the value \
7113         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
7114         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
7115         `GitRepository.spec.url` via `caixa-flux`'s \
7116         `ClusterBundleOpts::for_caixa`; both consumers' \
7117         `Option::unwrap_or_else` fallbacks only fire when the slot is \
7118         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
7119         `url: \"\"` in the rendered artifacts and breaks at `helm \
7120         template` / FluxCD source-controller reconcile time far from the \
7121         source caixa.lisp; omit the slot entirely to defer to the \
7122         renderer's `https://github.com/pleme-io/<nome>` / \
7123         `caixa.nome`-derived fallback, or carry a canonical authoring \
7124         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
7125         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
7126         `\"file:///path\"`)"
7127    )]
7128    RepositorioEmpty,
7129    #[error(
7130        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
7131         (the substrate consumes this string through the shared \
7132         `crate::render::is_git_repo_url` predicate — the same parser the \
7133         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
7134         value through via `DepSource::validate`; the canonical authoring \
7135         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
7136         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
7137         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
7138         scp-style SSH form. Without this gate a malformed `:repositorio` \
7139         (whitespace from a paste-from-doc; control characters / CRLF \
7140         from a paste-from-multiline-doc; a leading `-` from a \
7141         CLI-argument-injection footgun; a missing `:` separator from a \
7142         bare `org/repo` shape git treats as a relative filesystem path) \
7143         silently landed in the rendered `Chart.yaml home:` and the \
7144         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
7145         FluxCD reconcile time far from the source caixa.lisp; the gate \
7146         moves the diagnostic to the manifest layer with the offending \
7147         value named verbatim)"
7148    )]
7149    RepositorioInvalid { repositorio: String, reason: String },
7150    #[error(
7151        ":descricao is the empty string (every published caixa names \
7152         its purpose via a non-empty `:descricao` summary — the value \
7153         flows verbatim into the rendered `lareira-<nome>` Helm \
7154         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
7155         `build_chart_yaml` and into the chart `README.md` header via \
7156         `build_readme`; both consumers' `Option::unwrap_or_else` \
7157         `caixa.nome`-derived fallbacks only fire when the slot is \
7158         `None`, so an empty `Some(\"\")` silently lands as \
7159         `description: \"\"` / a blank `README.md` header in the \
7160         rendered artifacts and breaks at `helm lint` time \
7161         (`WARNING [chart.metadata.description]: description is \
7162         required` on `apiVersion: v2` charts) far from the source \
7163         caixa.lisp; omit the slot entirely to defer to the \
7164         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
7165         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
7166         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
7167         Servico.\"`)"
7168    )]
7169    DescricaoEmpty,
7170    #[error(
7171        ":descricao {descricao:?} is not a valid chart-description shape: \
7172         {reason} (the substrate consumes this string through the shared \
7173         `crate::render::is_chart_description_shape` predicate — the same \
7174         single-line-UTF-8 floor every realistic chart description carries: \
7175         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
7176         characters anywhere, Unicode prose bytes accepted. The canonical \
7177         authoring shapes are short single-line summaries like `\"Canonical \
7178         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
7179         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
7180         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
7181         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
7182         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
7183         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
7184         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
7185         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7186         `validate_descricao` + `StandardLayout::verify` and landed in the \
7187         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
7188         field + `README.md` header paragraph as a YAML-illegal multi-line \
7189         scalar or a silently-trimmed whitespace round-trip — every \
7190         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
7191         render the description in a single-line column far from the source \
7192         caixa.lisp; the gate moves the diagnostic to the manifest layer \
7193         with the offending value named verbatim)"
7194    )]
7195    DescricaoInvalid { descricao: String, reason: String },
7196    #[error(
7197        ":licenca is the empty string (every published caixa names \
7198         its license via a non-empty `:licenca` SPDX expression — the \
7199         value flows verbatim into the rendered `lareira-<nome>` Helm \
7200         chart's `README.md` `## License` section via `caixa-helm`'s \
7201         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
7202         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
7203         only fires when the slot is `None`, so an empty `Some(\"\")` \
7204         silently lands as a bare trailing period in the rendered \
7205         chart `README.md` `License` section far from the source \
7206         caixa.lisp; omit the slot entirely to defer to the \
7207         renderer's `MIT` fallback, or carry a canonical SPDX \
7208         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
7209         `\"Apache-2.0 OR MIT\"`)"
7210    )]
7211    LicencaEmpty,
7212    #[error(
7213        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
7214         (the substrate consumes this string through the shared \
7215         `crate::render::is_spdx_expression_shape` predicate — the same \
7216         alphabet-floor parser every peer per-axis value-shape gate routes \
7217         its value through; the canonical authoring shapes are single \
7218         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
7219         compound expressions like `\"Apache-2.0 OR MIT\"`, \
7220         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
7221         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
7222         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
7223         like `\"LicenseRef-MyLicense\"` / \
7224         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
7225         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
7226         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
7227         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
7228         a smart-quote paste; underscore-instead-of-hyphen typo \
7229         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
7230         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
7231         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
7232         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
7233         `README.md` `## License` section + a future SPDX-aware \
7234         `Chart.yaml license:` emitter would refuse the value at \
7235         `helm lint` time far from the source caixa.lisp; the gate moves \
7236         the diagnostic to the manifest layer with the offending value \
7237         named verbatim)"
7238    )]
7239    LicencaInvalid { licenca: String, reason: String },
7240    #[error(
7241        ":edicao is the empty string (every published caixa names \
7242         its language edition via a non-empty `:edicao` value — the \
7243         edition determines the tatara-lisp macro surface + \
7244         compatibility flags the substrate applies when building \
7245         the caixa; the canonical `Caixa::template` scaffold every \
7246         `feira init` emits carries `:edicao \"2026\"` verbatim and \
7247         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
7248         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
7249         construction, so an empty `Some(\"\")` silently lands as a \
7250         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
7251         a future renderer-side consumer that folds it through \
7252         `Option::unwrap_or_else` will skip the fallback and pass the \
7253         empty edition through to the substrate's build-time edition \
7254         selector far from the source caixa.lisp; omit the slot \
7255         entirely to defer to the substrate's default edition, or \
7256         carry a canonical edition like `\"2026\"`)"
7257    )]
7258    EdicaoEmpty,
7259    #[error(
7260        ":edicao {edicao:?} is not a valid edition: {reason} (every \
7261         documented tatara-lisp edition is a 4-digit ASCII decimal \
7262         year — `\"2026\"` is the only edition currently minted; \
7263         future-introduced siblings will follow the same shape, peer \
7264         with Cargo's `[package] edition` grammar which every value \
7265         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
7266         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
7267         paste-from-doc footguns silently passed: a trailing space \
7268         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
7269         from a paste-from-multiline-doc, a fullwidth-keyboard \
7270         look-alike (`\"2026\"`), a free-form non-year value \
7271         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
7272         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
7273         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
7274         wrong-length numeric value (`\"26\"`, `\"202\"`, \
7275         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
7276         rendered caixa.lisp and broke at the substrate's \
7277         build-time edition selector far from the source caixa.lisp; \
7278         omit the slot entirely to defer to the substrate's default \
7279         edition, or carry a canonical 4-digit ASCII decimal year \
7280         like `\"2026\"`)"
7281    )]
7282    EdicaoInvalid { edicao: String, reason: String },
7283}
7284
7285#[cfg(test)]
7286mod tests {
7287    use super::*;
7288
7289    #[test]
7290    fn template_round_trips() {
7291        let src = Caixa::template("demo");
7292        let c = Caixa::from_lisp(&src).expect("template must parse");
7293        assert_eq!(c.nome, "demo");
7294        assert_eq!(c.versao, "0.1.0");
7295        assert_eq!(c.kind, CaixaKind::Biblioteca);
7296        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
7297        assert!(c.deps.is_empty());
7298        assert!(c.deps_dev.is_empty());
7299    }
7300
7301    #[test]
7302    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
7303        // Fail-before-pass-after pin on [`Caixa::nome`] +
7304        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
7305        // accessor projects the top-level manifest's per-`:nome` /
7306        // per-`:versao` [`String`] storage through the `pub const fn`
7307        // [`String::as_str`] (const-stable since Rust 1.87, well within
7308        // the workspace MSRV) — any future accidental downgrade to
7309        // non-`const` fails the corresponding `<name>_via_const_fn`
7310        // wrapper at caixa-core build time with E0015 (`cannot call
7311        // non-const method`), strictly stronger than a runtime
7312        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
7313        // scalar-accessor family pins on the sibling `const`-eval-
7314        // surface passes ([`crate::CaixaVersion::as_str`] at the
7315        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
7316        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
7317        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
7318        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
7319        // axis, [`crate::supervisor::ChildSpec::nome`] /
7320        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
7321        // M2 supervisor-tree axis,
7322        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
7323        // upgrade axis, [`crate::dep::Dep::nome`] /
7324        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
7325        // axis, and the per-`:contratos`
7326        // [`crate::aplicacao::WitContract::source`] /
7327        // [`crate::aplicacao::WitContract::destination`] /
7328        // [`crate::aplicacao::WitContract::world_ref`] trio the
7329        // sibling pin at 279823b already anchors).
7330        const fn nome_via_const_fn(c: &Caixa) -> &str {
7331            c.nome()
7332        }
7333        const fn versao_via_const_fn(c: &Caixa) -> &str {
7334            c.versao()
7335        }
7336        let src = Caixa::template("demo");
7337        let c = Caixa::from_lisp(&src).expect("template must parse");
7338        assert_eq!(nome_via_const_fn(&c), c.nome());
7339        assert_eq!(versao_via_const_fn(&c), c.versao());
7340        assert_eq!(c.nome(), "demo");
7341        assert_eq!(c.versao(), "0.1.0");
7342    }
7343
7344    #[test]
7345    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
7346        // Fail-before-pass-after pin on the five per-`Caixa`
7347        // `Option<String> → Option<&str>` scalar accessors
7348        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
7349        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
7350        // manifest's optional universal-axis surface, plus
7351        // [`Caixa::restart_window`] on the M2 supervisor-tree
7352        // per-`SupervisorSpec` peer raw-window-string projection axis).
7353        // Each accessor destructures the typed slot's `Option<String>`
7354        // storage through the `match &self.<field> { Some(s) =>
7355        // Some(s.as_str()), None => None }` shape — routing through
7356        // [`String::as_str`] (const-stable since Rust 1.87, well within
7357        // the workspace MSRV) rather than the non-const
7358        // [`Option::as_deref`] the pre-lift bodies carried — and any
7359        // future accidental downgrade to non-`const` fails the
7360        // corresponding `<name>_via_const_fn` wrapper at caixa-core
7361        // build time with E0015 (`cannot call non-const method`),
7362        // strictly stronger than a runtime `assert!` and strictly
7363        // stronger than a module-scope `const _: () = assert!(…)` pin
7364        // (which cannot be formed on a `&Caixa` fixture because the
7365        // type's `String` / `Option<String>` carriers rule out
7366        // `const`-context value construction; the `const fn` wrapper
7367        // is the load-bearing shape that side-steps the destructor-in-
7368        // const restriction on the value axis while still pinning the
7369        // `const`-fn posture on the callee — mirror of the sibling
7370        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7371        // pin's discipline verbatim on the peer non-`Option`
7372        // `String → &str` axis at the same struct).
7373        //
7374        // Peer of the sibling per-M2/M3-slot `Option<String> →
7375        // Option<&str>` accessor family pin
7376        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
7377        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
7378        // [`WitContract::subject`] / [`WitContract::slot`] on the
7379        // per-`:contratos` payload-carrier trio,
7380        // [`Placement::shard_key`] / [`Placement::affinity`] on the
7381        // per-`:placement` optional-scalar pair).
7382        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
7383            c.licenca()
7384        }
7385        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
7386            c.repositorio()
7387        }
7388        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
7389            c.descricao()
7390        }
7391        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
7392            c.edicao()
7393        }
7394        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
7395            c.restart_window()
7396        }
7397        // Sweep both the `Some`-carrying arm (author-declared slot,
7398        // the byte-string projection payload) and the `None`-carrying
7399        // arm (author-omitted slot, the default-path projection) on
7400        // every accessor so the `const fn` wrapper family pins each
7401        // axis's canonical two-arm partition through the same const
7402        // dispatch as the runtime path.
7403        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7404        c1.licenca = Some("MIT".to_string());
7405        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
7406        c1.descricao = Some("demo caixa".to_string());
7407        c1.edicao = Some("2024".to_string());
7408        c1.restart_window = Some("60s".to_string());
7409        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
7410        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
7411        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
7412        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
7413        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
7414        assert_eq!(c1.licenca(), Some("MIT"));
7415        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
7416        assert_eq!(c1.descricao(), Some("demo caixa"));
7417        assert_eq!(c1.edicao(), Some("2024"));
7418        assert_eq!(c1.restart_window(), Some("60s"));
7419        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7420        c2.licenca = None;
7421        c2.repositorio = None;
7422        c2.descricao = None;
7423        c2.edicao = None;
7424        c2.restart_window = None;
7425        assert_eq!(licenca_via_const_fn(&c2), None);
7426        assert_eq!(repositorio_via_const_fn(&c2), None);
7427        assert_eq!(descricao_via_const_fn(&c2), None);
7428        assert_eq!(edicao_via_const_fn(&c2), None);
7429        assert_eq!(restart_window_via_const_fn(&c2), None);
7430    }
7431
7432    #[test]
7433    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
7434        // Fail-before-pass-after pin on the two outer-[`Caixa`]
7435        // `Copy`-return accessors — [`Caixa::kind`] on the required
7436        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
7437        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
7438        // axis. Both accessors project a `Copy`-carrier field
7439        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
7440        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
7441        // `Option<RestartStrategy>: Copy`) by value through a bare
7442        // `self.<field>` field-access — no dispatch, no destructor, no
7443        // heap. Any future accidental downgrade to non-`const` fails
7444        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
7445        // build time with E0015 (`cannot call non-const method`),
7446        // strictly stronger than a runtime `assert!` and strictly
7447        // stronger than a module-scope `const _: () = assert!(…)` pin
7448        // (which cannot be formed on a `&Caixa` fixture because the
7449        // type's `String` / `Vec` / `Option<Composite>` carriers rule
7450        // out `const`-context value construction; the `const fn`
7451        // wrapper is the load-bearing shape that side-steps the
7452        // destructor-in-const restriction on the value axis while still
7453        // pinning the `const`-fn posture on the callee — mirror of the
7454        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7455        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
7456        // pins' discipline verbatim on the peer outer-`Caixa`
7457        // `String → &str` + `Option<String> → Option<&str>` axes at the
7458        // same struct).
7459        //
7460        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
7461        // family on the inner-altitude nested-spec typed-slot
7462        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
7463        // + [`crate::supervisor::ChildSpec::restart`] on the M2
7464        // supervisor-tree axis (pinned at 152c868), and
7465        // [`crate::aplicacao::Placement::estrategia`] +
7466        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
7467        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
7468        // unlifted altitude for the `Copy`-return-accessor family.
7469        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
7470            c.kind()
7471        }
7472        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
7473            c.estrategia()
7474        }
7475        // Sweep every arm of both discriminant partitions the accessors
7476        // fan on — every [`CaixaKind`] variant the six-arm required
7477        // discriminant carries (Biblioteca / Binario / Servico /
7478        // Supervisor / Aplicacao / Acao) and both arms of the
7479        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
7480        // (`Some(<strategy>)` on an author-declared supervisor and
7481        // `None` on the author-omitted default arm every non-Supervisor
7482        // caixa carries by `#[serde(default)]`) — so the `const fn`
7483        // wrapper family pins the closed-set partition through the
7484        // same const dispatch as the runtime path.
7485        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7486        c1.kind = CaixaKind::Servico;
7487        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
7488        assert_eq!(kind_via_const_fn(&c1), c1.kind());
7489        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
7490        assert_eq!(c1.kind(), CaixaKind::Servico);
7491        assert_eq!(
7492            c1.estrategia(),
7493            Some(crate::supervisor::RestartStrategy::OneForAll)
7494        );
7495        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7496        c2.kind = CaixaKind::Aplicacao;
7497        c2.estrategia = None;
7498        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
7499        assert_eq!(estrategia_via_const_fn(&c2), None);
7500        // Anchor the remaining discriminant arms so any future
7501        // reordering of [`CaixaKind`]'s six-variant enum surfaces
7502        // through the wrapper dispatch, not just through the direct
7503        // method call.
7504        for kind in [
7505            CaixaKind::Biblioteca,
7506            CaixaKind::Binario,
7507            CaixaKind::Servico,
7508            CaixaKind::Supervisor,
7509            CaixaKind::Aplicacao,
7510            CaixaKind::Acao,
7511        ] {
7512            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7513            c.kind = kind;
7514            assert_eq!(kind_via_const_fn(&c), kind);
7515        }
7516    }
7517
7518    #[test]
7519    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
7520        // Fail-before-pass-after pin on the five outer-[`Caixa`]
7521        // `Vec<String> → &[String]` slice-return accessors on the
7522        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
7523        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
7524        // Each body is a bare `self.<field>.as_slice()` dispatch through
7525        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7526        // the workspace MSRV). Any future accidental downgrade to
7527        // non-`const` fails the corresponding `<name>_via_const_fn`
7528        // wrapper at caixa-core build time with E0015 (`cannot call
7529        // non-const method`) — mirror of the sibling
7530        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
7531        // discipline on the peer outer-`Caixa` `Copy`-return accessor
7532        // axis, and peer of the sibling composite-carrier slice-return
7533        // pin below on the peer outer-`Caixa` composite-slice axis.
7534        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
7535            c.autores()
7536        }
7537        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
7538            c.etiquetas()
7539        }
7540        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
7541            c.bibliotecas()
7542        }
7543        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
7544            c.exe()
7545        }
7546        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
7547            c.servicos()
7548        }
7549        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
7550        // `servicos` — the template's `Vec::new()` default) and the
7551        // populated arm (mutated below) on every accessor so the
7552        // `const fn` wrapper family pins each axis's two-arm partition
7553        // through the same const dispatch as the runtime path.
7554        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
7555        // so that arm's "empty" fixture is the populated arm the
7556        // mutation sweep covers.
7557        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7558        assert!(autores_via_const_fn(&c_empty).is_empty());
7559        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
7560        assert!(exe_via_const_fn(&c_empty).is_empty());
7561        assert!(servicos_via_const_fn(&c_empty).is_empty());
7562        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7563        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
7564        c_full.etiquetas = vec!["compounding".to_string()];
7565        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
7566        c_full.exe = vec!["exe/cli.lisp".to_string()];
7567        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
7568        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
7569        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
7570        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
7571        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
7572        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
7573        assert_eq!(
7574            bibliotecas_via_const_fn(&c_full),
7575            &["lib/one.lisp", "lib/two.lisp"]
7576        );
7577        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
7578        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
7579        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
7580        assert_eq!(
7581            servicos_via_const_fn(&c_full),
7582            &["servicos/one.computeunit.yaml"]
7583        );
7584    }
7585
7586    #[test]
7587    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
7588        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
7589        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
7590        // / [`Caixa::deps_dev`] on the dep-graph axis,
7591        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
7592        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
7593        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
7594        // bare `self.<field>.as_slice()` dispatch through
7595        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7596        // the workspace MSRV) — peer of the sibling `String`-payload
7597        // slice-return pin above on the peer outer-`Caixa` universal-
7598        // axis surface, and peer of the sibling inner-composite-
7599        // altitude reference-return pin family
7600        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
7601        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
7602        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
7603        // (all pinned at 0b23e0f).
7604        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
7605            c.deps()
7606        }
7607        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
7608            c.deps_dev()
7609        }
7610        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
7611            c.upgrade_from()
7612        }
7613        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
7614            c.children()
7615        }
7616        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
7617            c.membros()
7618        }
7619        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
7620            c.contratos()
7621        }
7622        // Empty-arm sweep on all six composite-carrier axes — every
7623        // `Caixa::template` starts with `Vec::new()` on each.
7624        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7625        assert!(deps_via_const_fn(&c_empty).is_empty());
7626        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
7627        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
7628        assert!(children_via_const_fn(&c_empty).is_empty());
7629        assert!(membros_via_const_fn(&c_empty).is_empty());
7630        assert!(contratos_via_const_fn(&c_empty).is_empty());
7631        // Populate `:membros` / `:contratos` directly via struct literals
7632        // — the parser-side validation path fans on `:kind`-gated cross-
7633        // slot invariants irrelevant to the accessor dispatch under test.
7634        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7635        c_full.membros = vec![
7636            crate::aplicacao::Membro {
7637                caixa: "demo-a".to_string(),
7638                versao: "^0.1.0".to_string(),
7639            },
7640            crate::aplicacao::Membro {
7641                caixa: "demo-b".to_string(),
7642                versao: "^0.2.0".to_string(),
7643            },
7644        ];
7645        c_full.contratos = vec![crate::aplicacao::WitContract {
7646            de: "demo-a".to_string(),
7647            para: "demo-b".to_string(),
7648            wit: "wasi:http/proxy".to_string(),
7649            endpoint: Some("/edge".to_string()),
7650            subject: None,
7651            slot: None,
7652        }];
7653        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
7654        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
7655        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
7656        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
7657        // Alias-borrow check on the four remaining composite-carrier
7658        // slice-return arms — the wrapper's return borrow must alias the
7659        // caller's borrow so any future accessor re-routing that skips
7660        // the storage field surfaces through the assertion.
7661        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
7662        assert!(std::ptr::eq(
7663            deps_dev_via_const_fn(&c_full),
7664            c_full.deps_dev()
7665        ));
7666        assert!(std::ptr::eq(
7667            upgrade_from_via_const_fn(&c_full),
7668            c_full.upgrade_from()
7669        ));
7670        assert!(std::ptr::eq(
7671            children_via_const_fn(&c_full),
7672            c_full.children()
7673        ));
7674    }
7675
7676    #[test]
7677    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
7678        // Fail-before-pass-after pin on the six outer-[`Caixa`]
7679        // `Option<Composite> → Option<&Composite>` reference-return
7680        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
7681        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
7682        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
7683        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
7684        // Each body is a bare `self.<field>.as_ref()` dispatch through
7685        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
7686        // the workspace MSRV of 1.89). Any future accidental downgrade
7687        // to non-`const` fails the corresponding `<name>_via_const_fn`
7688        // wrapper at caixa-core build time with E0015 (`cannot call
7689        // non-const method`), strictly stronger than a runtime `assert!`
7690        // and strictly stronger than a module-scope `const _: () =
7691        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
7692        // because the type's `String` / `Vec` / `Option<Composite>`
7693        // carriers rule out `const`-context value construction; the
7694        // `const fn` wrapper is the load-bearing shape that side-steps
7695        // the destructor-in-const restriction on the value axis while
7696        // still pinning the `const`-fn posture on the callee — mirror
7697        // of the sibling
7698        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
7699        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
7700        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
7701        // pins' discipline verbatim on the peer outer-`Caixa` axes at
7702        // the same struct).
7703        //
7704        // Closes the outer-`Caixa` `Option<&Composite>` composite-
7705        // reference-return sub-family — the last unlifted altitude on
7706        // the outer-`Caixa` accessor-family const-eval surface after
7707        // the sibling `Copy`-return / universal-axis-`&str` /
7708        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
7709        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
7710        // 231a968 (the last of these pins the `Vec<T> → &[T]`
7711        // composite-slice arm the six accessors here close as their
7712        // `Option<Composite> → Option<&Composite>` peer). Peer of the
7713        // sibling inner-altitude nested-spec composite-reference-return
7714        // pin family — [`crate::AplicacaoSpec::politicas`] /
7715        // [`crate::AplicacaoSpec::placement`] /
7716        // [`crate::AplicacaoSpec::entrada`] on the inner
7717        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
7718        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
7719        // the same shape so both altitudes of the reference-return
7720        // discipline (per-`Caixa` outer-slot presence + per-
7721        // `AplicacaoSpec` inner-slot presence) route through one typed
7722        // const dispatch on the substrate primitive.
7723        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
7724            c.limits()
7725        }
7726        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
7727            c.behavior()
7728        }
7729        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
7730            c.politicas()
7731        }
7732        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
7733            c.placement()
7734        }
7735        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
7736            c.entrada()
7737        }
7738        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
7739            c.ci()
7740        }
7741        // Both-arm sweep on every accessor: the `None` author-omitted
7742        // arm (template default — no M2/M3/CI slot declared) and the
7743        // `Some(<composite>)` authored arm (mutated below via struct-
7744        // literal seeds, side-stepping the parser-side `:kind`-gated
7745        // cross-slot invariants irrelevant to the accessor dispatch
7746        // under test). Both arms route through the `const fn` wrapper
7747        // family so the two-arm `Option` partition is pinned through
7748        // the same const dispatch as the runtime path.
7749        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7750        assert!(limits_via_const_fn(&c_empty).is_none());
7751        assert!(behavior_via_const_fn(&c_empty).is_none());
7752        assert!(politicas_via_const_fn(&c_empty).is_none());
7753        assert!(placement_via_const_fn(&c_empty).is_none());
7754        assert!(entrada_via_const_fn(&c_empty).is_none());
7755        assert!(ci_via_const_fn(&c_empty).is_none());
7756        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7757        c_full.limits = Some(LimitsSpec::default());
7758        c_full.behavior = Some(crate::BehaviorSpec::default());
7759        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
7760        c_full.placement = Some(crate::aplicacao::Placement::default());
7761        c_full.entrada = Some(crate::aplicacao::Entrada {
7762            host: "demo.quero.cloud".to_string(),
7763            para: "demo".to_string(),
7764            paths: Vec::new(),
7765            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
7766        });
7767        c_full.ci = Some(canteiro_types::CiRun {
7768            workspace: "pleme-io".into(),
7769            repo: "caixa".into(),
7770            nodes: vec![],
7771        });
7772        assert!(limits_via_const_fn(&c_full).is_some());
7773        assert!(behavior_via_const_fn(&c_full).is_some());
7774        assert!(politicas_via_const_fn(&c_full).is_some());
7775        assert!(placement_via_const_fn(&c_full).is_some());
7776        assert!(entrada_via_const_fn(&c_full).is_some());
7777        assert!(ci_via_const_fn(&c_full).is_some());
7778        // Alias-borrow check on every arm: the wrapper's inner-`Option`
7779        // reference must alias the caller's borrow so any future accessor
7780        // re-routing that skips the storage field surfaces through the
7781        // assertion.
7782        assert!(std::ptr::eq(
7783            limits_via_const_fn(&c_full).unwrap(),
7784            c_full.limits().unwrap()
7785        ));
7786        assert!(std::ptr::eq(
7787            behavior_via_const_fn(&c_full).unwrap(),
7788            c_full.behavior().unwrap()
7789        ));
7790        assert!(std::ptr::eq(
7791            politicas_via_const_fn(&c_full).unwrap(),
7792            c_full.politicas().unwrap()
7793        ));
7794        assert!(std::ptr::eq(
7795            placement_via_const_fn(&c_full).unwrap(),
7796            c_full.placement().unwrap()
7797        ));
7798        assert!(std::ptr::eq(
7799            entrada_via_const_fn(&c_full).unwrap(),
7800            c_full.entrada().unwrap()
7801        ));
7802        assert!(std::ptr::eq(
7803            ci_via_const_fn(&c_full).unwrap(),
7804            c_full.ci().unwrap()
7805        ));
7806    }
7807
7808    #[test]
7809    fn register_populates_registry() {
7810        Caixa::register().expect("first register call in this test process must succeed");
7811        let kws = tatara_lisp::domain::registered_keywords();
7812        assert!(kws.contains(&"defcaixa"));
7813    }
7814
7815    #[test]
7816    fn to_lisp_round_trips() {
7817        let src = Caixa::template("demo");
7818        let c1 = Caixa::from_lisp(&src).unwrap();
7819        let emitted = c1.to_lisp();
7820        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
7821        assert_eq!(c1, c2);
7822    }
7823
7824    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
7825    //
7826    // The compounding pin: the variant stores only the typed
7827    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
7828    // (canonical keyword, description, consumer) routes through the enum's
7829    // own accessors at Display time. Prior to that closure the variant
7830    // carried each accessor's return value as a stored `&'static str`
7831    // snapshot alongside `dialeto`; a caller could construct the variant
7832    // with a snapshot that drifted from what `dialeto`'s accessors would
7833    // return, and every downstream user-facing projection would silently
7834    // disagree with the classification. Storing only the axis makes the
7835    // drift structurally impossible.
7836
7837    #[test]
7838    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
7839        // Single-field construction is the whole compounding shape — a
7840        // future re-introduction of a snapshot field (a `palavra_canonica:
7841        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
7842        // would re-open the drift surface and this construction would fail
7843        // to compile with "missing field" until every snapshot was seeded
7844        // at the call site again. The compile-time guarantee is the
7845        // invariant; the assertion below only witnesses that the
7846        // construction is well-formed after the closure.
7847        let err = LeituraError::DialetoEstrangeiro {
7848            dialeto: crate::dialeto::CaixaDialeto::Molde,
7849        };
7850        assert!(matches!(
7851            err,
7852            LeituraError::DialetoEstrangeiro {
7853                dialeto: crate::dialeto::CaixaDialeto::Molde,
7854            }
7855        ));
7856    }
7857
7858    #[test]
7859    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
7860        // For every foreign-dialect classification the variant surfaces —
7861        // [`crate::dialeto::CaixaDialeto::Molde`] and
7862        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
7863        // variants [`Caixa::from_lisp`] raises this error for — the
7864        // rendered [`std::fmt::Display`] byte-string must interpolate each
7865        // typed accessor's return verbatim. A future re-introduction of a
7866        // stored `&'static str` snapshot alongside `dialeto` that Display
7867        // read instead of the accessor would fail this pin as soon as the
7868        // two disagreed; a future accessor rebrand (a per-dialect
7869        // consumer rename, a canonical-keyword shift once the substrate
7870        // migration named in [`crate::dialeto`] completes) reaches every
7871        // consumer through one typed dispatch and this pin verifies the
7872        // display path is one of them.
7873        for d in [
7874            crate::dialeto::CaixaDialeto::Molde,
7875            crate::dialeto::CaixaDialeto::MoldePosicional,
7876        ] {
7877            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
7878            assert!(
7879                rendered.contains(d.palavra_canonica()),
7880                "Display must interpolate `dialeto.palavra_canonica()` \
7881                 verbatim — a stored snapshot would silently drift from \
7882                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
7883            );
7884            assert!(
7885                rendered.contains(d.descricao()),
7886                "Display must interpolate `dialeto.descricao()` verbatim. \
7887                 dialect: {d}, rendered: {rendered:?}"
7888            );
7889            assert!(
7890                rendered.contains(d.consumidor()),
7891                "Display must interpolate `dialeto.consumidor()` verbatim. \
7892                 dialect: {d}, rendered: {rendered:?}"
7893            );
7894        }
7895    }
7896
7897    #[test]
7898    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
7899        // The end-to-end pin the compounding closure defends: a
7900        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
7901        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
7902        // rendered Display byte-string names the Molde accessors'
7903        // returns verbatim. Any future path that constructed the variant
7904        // with a mismatched snapshot (a stored `palavra_canonica:
7905        // "defcaixa"` on a `Molde` classification) would land Display
7906        // pointing at `defcaixa` while the typed axis said `Molde` — the
7907        // exact drift the closure removes.
7908        let src = r#"
7909          (defcaixa
7910            :name "x"
7911            :kind :Biblioteca
7912            :ecosystem :rust-single-crate
7913            :package {:name "x" :version "0.1.0"})
7914        "#;
7915        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
7916        match err {
7917            LeituraError::DialetoEstrangeiro { dialeto } => {
7918                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
7919                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
7920                assert!(rendered.contains(dialeto.palavra_canonica()));
7921                assert!(rendered.contains(dialeto.consumidor()));
7922                assert!(rendered.contains(dialeto.descricao()));
7923            }
7924            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
7925        }
7926    }
7927
7928    #[test]
7929    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
7930        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
7931        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
7932        // positional-arity `defmolde` form written under a `(defcaixa …)`
7933        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
7934        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
7935        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
7936        // so no test exercised the positional-arity path through
7937        // `Caixa::from_lisp` specifically; the sibling
7938        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
7939        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
7940        // two arms route through the lifted
7941        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
7942        // typed predicate — the same predicate the pre-lift `foreign =>`
7943        // wildcard resolved to today — and this pin makes the
7944        // positional-arity arm's byte-shape at the gate explicit rather
7945        // than implied by wildcard-absorption. A future regression that
7946        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
7947        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
7948        // from the two-arity closure) would fail this pin at caixa-core
7949        // test time rather than surfacing far from the change as a
7950        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
7951        // …)` silently parsing past the derive.
7952        let src = r#"
7953          (defcaixa todoku-go
7954            :kind :Biblioteca
7955            :ecosystem :go
7956            :package {:name "todoku-go" :version "0.3.0"})
7957        "#;
7958        let err =
7959            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
7960        match err {
7961            LeituraError::DialetoEstrangeiro { dialeto } => {
7962                assert_eq!(
7963                    dialeto,
7964                    crate::dialeto::CaixaDialeto::MoldePosicional,
7965                    "DialetoEstrangeiro must carry the MoldePosicional \
7966                     variant verbatim — the positional-arity `defmolde` \
7967                     form under a `(defcaixa …)` head is the \
7968                     `MoldePosicional` arm's canonical byte-shape"
7969                );
7970                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
7971                assert!(
7972                    rendered.contains(dialeto.palavra_canonica()),
7973                    "Display must interpolate `dialeto.palavra_canonica()` \
7974                     verbatim on the MoldePosicional arm; rendered: \
7975                     {rendered:?}"
7976                );
7977                assert!(
7978                    rendered.contains(dialeto.consumidor()),
7979                    "Display must interpolate `dialeto.consumidor()` \
7980                     verbatim on the MoldePosicional arm; rendered: \
7981                     {rendered:?}"
7982                );
7983                assert!(
7984                    rendered.contains(dialeto.descricao()),
7985                    "Display must interpolate `dialeto.descricao()` \
7986                     verbatim on the MoldePosicional arm; rendered: \
7987                     {rendered:?}"
7988                );
7989            }
7990            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
7991        }
7992    }
7993
7994    #[test]
7995    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
7996        // Load-bearing byte-parity pin: for every arm in
7997        // [`crate::dialeto::CaixaDialeto::ALL`], the
7998        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
7999        // partition must agree with the lifted
8000        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
8001        // typed predicate — i.e. from_lisp raises
8002        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
8003        // `d.is_molde_family()` returns `true`, and does NOT raise
8004        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
8005        // predicate returns `false` (the arm's source falls through to
8006        // the derive — parses cleanly on
8007        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
8008        // [`LeituraError::Leitura`] on
8009        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
8010        //
8011        // Pre-lift the gate hand-rolled a three-arm match
8012        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
8013        // whose `foreign =>` wildcard expressed no compile-time link
8014        // back to the substrate primitive's arm-family; a future fifth
8015        // dialect the [`crate::dialeto`] module doc's "third dialect"
8016        // hazard actualises would fall silently onto the wildcard
8017        // regardless of whether it belonged to the `defmolde` family or
8018        // to a distinct `defcaixa`-family. Post-lift the partition
8019        // resolves through
8020        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
8021        // typed dispatch, and this pin refuses any future regression
8022        // that silently split the from_lisp partition from the typed
8023        // predicate — the two paths now migrate as one on any future
8024        // arm addition.
8025        //
8026        // Sibling in shape to the peer
8027        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
8028        // (e9d2315) that pins the same byte-parity between
8029        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
8030        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
8031        // `== "defmolde"` classifier — extends the discipline from the
8032        // two paths within the [`crate::dialeto`] primitive onto the
8033        // third external consumer of the `defmolde`-family partition
8034        // (the [`Caixa::from_lisp`] gate that raises
8035        // [`LeituraError::DialetoEstrangeiro`]).
8036        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
8037            (
8038                crate::dialeto::CaixaDialeto::Pacote,
8039                r#"
8040                  (defcaixa
8041                    :nome   "checkout"
8042                    :versao "0.1.0"
8043                    :kind   Biblioteca
8044                    :edicao "2026"
8045                    :descricao "canonical Pacote source"
8046                    :autores ()
8047                    :etiquetas ()
8048                    :deps ()
8049                    :deps-dev ()
8050                    :bibliotecas ("lib/checkout.lisp"))
8051                "#,
8052            ),
8053            (
8054                crate::dialeto::CaixaDialeto::Molde,
8055                r#"
8056                  (defcaixa
8057                    :name "base64"
8058                    :kind :Biblioteca
8059                    :ecosystem :rust-single-crate
8060                    :package {:name "base64" :version "0.22.1"}
8061                    :workflows [:auto-release])
8062                "#,
8063            ),
8064            (
8065                crate::dialeto::CaixaDialeto::MoldePosicional,
8066                r#"
8067                  (defcaixa todoku-go
8068                    :kind :Biblioteca
8069                    :ecosystem :go
8070                    :package {:name "todoku-go" :version "0.3.0"})
8071                "#,
8072            ),
8073            (
8074                crate::dialeto::CaixaDialeto::Desconhecido,
8075                r#"(defcaixa :licenca "MIT")"#,
8076            ),
8077        ];
8078
8079        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
8080        // must appear in the fixture table so the pin's arm-set stays
8081        // synchronised with the enum's arm-set. Fails at test time if a
8082        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
8083        // (with a corresponding `is_molde_family` return) forgot to
8084        // extend this fixture table with a canonical source for the new
8085        // arm — the pin cannot cover an arm it has no source for.
8086        for &expected in crate::dialeto::CaixaDialeto::ALL {
8087            assert!(
8088                fixtures.iter().any(|(d, _)| *d == expected),
8089                "fixture table must carry a canonical source for every \
8090                 CaixaDialeto arm; missing: {expected:?}"
8091            );
8092        }
8093
8094        for &(expected_dialect, src) in fixtures {
8095            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
8096                panic!(
8097                    "fixture source for {expected_dialect:?} must classify \
8098                     cleanly, got err: {err:?}"
8099                )
8100            });
8101            assert_eq!(
8102                classified, expected_dialect,
8103                "fixture source for {expected_dialect:?} must classify as \
8104                 {expected_dialect:?} (drift here defeats the byte-parity \
8105                 pin below — a source labelled for one arm but classifying \
8106                 as another would silently satisfy or violate the pin for \
8107                 the wrong reason)"
8108            );
8109
8110            let outcome = Caixa::from_lisp(src);
8111            match (expected_dialect.is_molde_family(), &outcome) {
8112                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
8113                    assert_eq!(
8114                        *dialeto, expected_dialect,
8115                        "DialetoEstrangeiro must carry the same typed arm \
8116                         the classifier returned — a drift here would let \
8117                         from_lisp raise the error while pointing at the \
8118                         wrong dialect (e.g. rejecting a \
8119                         MoldePosicional source as Molde). arm: \
8120                         {expected_dialect:?}"
8121                    );
8122                }
8123                (true, other) => panic!(
8124                    "arm {expected_dialect:?} has is_molde_family() = true \
8125                     so from_lisp must raise DialetoEstrangeiro carrying \
8126                     {expected_dialect:?}; got: {other:?}"
8127                ),
8128                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
8129                    "arm {expected_dialect:?} has is_molde_family() = false \
8130                     so from_lisp must NOT raise DialetoEstrangeiro; got \
8131                     one carrying: {dialeto:?}. This means the typed \
8132                     predicate and the from_lisp partition disagree on \
8133                     this arm — exactly the drift this pin refuses."
8134                ),
8135                (false, _) => {
8136                    // A non-molde arm's source falls through to the
8137                    // derive: Pacote sources parse to Ok(_); Desconhecido
8138                    // sources surface as LeituraError::Leitura from the
8139                    // derive's own unknown-keyword rejection. Either
8140                    // shape is acceptable here — the pin's promise is
8141                    // narrower: "no DialetoEstrangeiro on
8142                    // is_molde_family() == false".
8143                }
8144            }
8145        }
8146    }
8147
8148    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
8149
8150    #[test]
8151    fn limits_round_trip_via_json() {
8152        use crate::LimitsSpec;
8153        use std::time::Duration;
8154        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8155        c.limits = Some(LimitsSpec {
8156            memory: Some(64 * 1024 * 1024),
8157            fuel: Some(1_000_000),
8158            wall_clock: Some(Duration::from_secs(30)),
8159            cpu: Some(500),
8160        });
8161        let json = serde_json::to_string(&c).unwrap();
8162        assert!(json.contains("\"limits\""));
8163        assert!(json.contains("\"64MiB\""));
8164        assert!(json.contains("\"30s\""));
8165        assert!(json.contains("\"500m\""));
8166        let back: Caixa = serde_json::from_str(&json).unwrap();
8167        assert_eq!(c.limits, back.limits);
8168    }
8169
8170    #[test]
8171    fn behavior_round_trip_via_json() {
8172        use crate::BehaviorSpec;
8173        use std::path::PathBuf;
8174        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8175        c.behavior = Some(BehaviorSpec {
8176            on_init: Some(PathBuf::from("lib/init.lisp")),
8177            on_call: Some(PathBuf::from("lib/handlers.lisp")),
8178            ..Default::default()
8179        });
8180        let json = serde_json::to_string(&c).unwrap();
8181        let back: Caixa = serde_json::from_str(&json).unwrap();
8182        assert_eq!(c.behavior, back.behavior);
8183    }
8184
8185    #[test]
8186    fn upgrade_from_round_trip_via_json() {
8187        use crate::{UpgradeFromEntry, UpgradeInstruction};
8188        use std::path::PathBuf;
8189        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8190        c.upgrade_from = vec![UpgradeFromEntry {
8191            from: "0.1.0".into(),
8192            instructions: vec![
8193                UpgradeInstruction::LoadModule {
8194                    module: "demo".into(),
8195                },
8196                UpgradeInstruction::StateChange {
8197                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
8198                },
8199                UpgradeInstruction::SoftPurge {
8200                    module: "demo-old".into(),
8201                },
8202            ],
8203        }];
8204        let json = serde_json::to_string(&c).unwrap();
8205        let back: Caixa = serde_json::from_str(&json).unwrap();
8206        assert_eq!(c.upgrade_from, back.upgrade_from);
8207    }
8208
8209    #[test]
8210    fn supervisor_view_returns_typed_shape() {
8211        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8212        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8213        c.kind = CaixaKind::Supervisor;
8214        c.bibliotecas.clear();
8215        c.estrategia = Some(RestartStrategy::OneForOne);
8216        c.max_restarts = Some(5);
8217        c.restart_window = Some("60s".into());
8218        c.children = vec![ChildSpec {
8219            caixa: "worker".into(),
8220            versao: "^0.1".into(),
8221            restart: RestartPolicy::Permanent,
8222        }];
8223        let view = c.supervisor_view().expect("Supervisor kind has a view");
8224        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
8225        assert_eq!(view.max_restarts, 5);
8226        assert_eq!(
8227            view.restart_window,
8228            Some(std::time::Duration::from_secs(60))
8229        );
8230        assert_eq!(view.children.len(), 1);
8231        view.validate().unwrap();
8232    }
8233
8234    #[test]
8235    fn supervisor_view_none_for_non_supervisor_kinds() {
8236        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8237        assert!(c.supervisor_view().is_none());
8238    }
8239
8240    #[test]
8241    fn declared_mesh_slots_empty_for_bare_caixa() {
8242        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8243        assert!(c.declared_mesh_slots().is_empty());
8244    }
8245
8246    #[test]
8247    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
8248        use crate::{Entrada, Membro};
8249        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8250        // Set a non-adjacent pair (:membros + :entrada) to pin that the
8251        // canonical declaration order is preserved regardless of which
8252        // subset is populated.
8253        c.membros = vec![Membro {
8254            caixa: "a".into(),
8255            versao: "^0.1".into(),
8256        }];
8257        c.entrada = Some(Entrada {
8258            host: "x.example.com".into(),
8259            para: "a".into(),
8260            paths: vec![],
8261            port: 8080,
8262        });
8263        assert_eq!(
8264            c.declared_mesh_slots(),
8265            vec![
8266                crate::render::M3_AUTHOR_KEY_MEMBROS,
8267                crate::render::M3_AUTHOR_KEY_ENTRADA,
8268            ]
8269        );
8270    }
8271
8272    #[test]
8273    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8274        // Scalar-value pin: the five author-facing kebab-case labels the
8275        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
8276        // mesh slot axis, one arm per typed slot. Mirrors the peer
8277        // scalar-value pin the sibling
8278        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8279        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8280        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
8281        // carry (f49c8b0), so both altitudes of the typed-slot algebra
8282        // (per-Servico M2 + per-Aplicacao M3) share the same
8283        // "one canonical byte-string per arm" discipline. A future
8284        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
8285        // `:politicas` → `:policies`, `:placement` → `:distribution`,
8286        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
8287        // and every consumer that reaches for the label picks it up at
8288        // build time rather than at runtime as a downstream mismatch.
8289        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
8290        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
8291        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
8292        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
8293        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
8294    }
8295
8296    #[test]
8297    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
8298        // Production-through-const pin: the five per-arm labels the
8299        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
8300        // `Vec` route through the lifted
8301        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
8302        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
8303        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
8304        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
8305        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
8306        // declaration order. A future re-order or drift at the tagger
8307        // (a rename that reaches the tagger but not the const, or vice
8308        // versa) surfaces here at build time rather than at runtime as
8309        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
8310        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8311        // commit. Mirror of the peer
8312        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8313        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
8314        // axis.
8315        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
8316        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8317        c.membros = vec![Membro {
8318            caixa: "a".into(),
8319            versao: "^0.1".into(),
8320        }];
8321        c.contratos = vec![WitContract {
8322            de: "a".into(),
8323            para: "a".into(),
8324            wit: "wasi:http/proxy".into(),
8325            endpoint: Some("/x".into()),
8326            subject: None,
8327            slot: None,
8328        }];
8329        c.politicas = Some(MeshPolicy::default());
8330        c.placement = Some(Placement {
8331            estrategia: PlacementStrategy::Replicated,
8332            clusters: vec!["rio".into()],
8333            affinity: None,
8334            shard_key: None,
8335        });
8336        c.entrada = Some(Entrada {
8337            host: "x.example.com".into(),
8338            para: "a".into(),
8339            paths: vec![],
8340            port: 8080,
8341        });
8342        assert_eq!(
8343            c.declared_mesh_slots(),
8344            vec![
8345                crate::render::M3_AUTHOR_KEY_MEMBROS,
8346                crate::render::M3_AUTHOR_KEY_CONTRATOS,
8347                crate::render::M3_AUTHOR_KEY_POLITICAS,
8348                crate::render::M3_AUTHOR_KEY_PLACEMENT,
8349                crate::render::M3_AUTHOR_KEY_ENTRADA,
8350            ]
8351        );
8352    }
8353
8354    #[test]
8355    fn declared_supervisor_slots_empty_for_bare_caixa() {
8356        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8357        assert!(c.declared_supervisor_slots().is_empty());
8358    }
8359
8360    #[test]
8361    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
8362        use crate::RestartStrategy;
8363        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8364        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
8365        // that the canonical declaration order is preserved regardless
8366        // of which subset is populated.
8367        c.estrategia = Some(RestartStrategy::OneForOne);
8368        c.restart_window = Some("60s".into());
8369        assert_eq!(
8370            c.declared_supervisor_slots(),
8371            vec![
8372                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8373                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8374            ]
8375        );
8376    }
8377
8378    #[test]
8379    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8380        // Scalar-value pin: the four author-facing kebab-case labels the
8381        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
8382        // supervision-tree slot axis, one arm per typed slot. Mirrors the
8383        // peer scalar-value pins the sibling
8384        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
8385        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
8386        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
8387        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
8388        // top-level M3 slot consts carry, so all three kind-scoped
8389        // typed-slot-family author-facing-label axes route through one
8390        // canonical per-arm declaration. A future rebrand
8391        // (`:estrategia` → `:strategy` for English uniformity,
8392        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
8393        // `MaxIntensity` name, `:restart-window` → `:period` matching
8394        // OTP's `Period` name, `:children` → `:workers` matching Elixir
8395        // idiom) lands as an edit to exactly one const, and every
8396        // consumer that reaches for the label picks it up at build time
8397        // rather than at runtime as a downstream mismatch.
8398        assert_eq!(
8399            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8400            ":estrategia"
8401        );
8402        assert_eq!(
8403            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8404            ":max-restarts"
8405        );
8406        assert_eq!(
8407            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8408            ":restart-window"
8409        );
8410        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
8411    }
8412
8413    #[test]
8414    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
8415        // Production-through-const pin: the four per-arm labels the
8416        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
8417        // return `Vec` route through the lifted
8418        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
8419        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
8420        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
8421        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
8422        // canonical declaration order. A future re-order or drift at the
8423        // tagger (a rename that reaches the tagger but not the const, or
8424        // vice versa) surfaces here at build time rather than at runtime
8425        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
8426        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8427        // commit. Mirror of the peer
8428        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8429        // (f49c8b0) and
8430        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
8431        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
8432        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8433        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8434        c.estrategia = Some(RestartStrategy::OneForOne);
8435        c.max_restarts = Some(5);
8436        c.restart_window = Some("60s".into());
8437        c.children = vec![ChildSpec {
8438            caixa: "worker".into(),
8439            versao: "^0.1".into(),
8440            restart: RestartPolicy::Permanent,
8441        }];
8442        assert_eq!(
8443            c.declared_supervisor_slots(),
8444            vec![
8445                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8446                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8447                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8448                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
8449            ]
8450        );
8451    }
8452
8453    #[test]
8454    fn declared_servico_slots_empty_for_bare_caixa() {
8455        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8456        assert!(c.declared_servico_slots().is_empty());
8457    }
8458
8459    #[test]
8460    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
8461        use crate::{UpgradeFromEntry, UpgradeInstruction};
8462        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8463        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
8464        // the canonical declaration order is preserved regardless of
8465        // which subset is populated.
8466        c.limits = Some(crate::LimitsSpec {
8467            fuel: Some(1_000_000),
8468            ..Default::default()
8469        });
8470        c.upgrade_from = vec![UpgradeFromEntry {
8471            from: "0.1.0".into(),
8472            instructions: vec![UpgradeInstruction::Restart],
8473        }];
8474        assert_eq!(
8475            c.declared_servico_slots(),
8476            vec![
8477                crate::render::M2_AUTHOR_KEY_LIMITS,
8478                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8479            ]
8480        );
8481    }
8482
8483    #[test]
8484    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8485        // Scalar-value pin: the three author-facing kebab-case labels
8486        // the `(defcaixa … :<slot> (…))` surface admits on the M2
8487        // top-level slot axis, one arm per typed slot. Mirrors the peer
8488        // scalar-value pin the sibling renderer-side
8489        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
8490        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
8491        // consts carry, so both halves of the M2 top-level slot dual
8492        // axis (author-facing kebab-case label + renderer-side
8493        // camelCase overlay-container wire key) route through one
8494        // canonical per-arm declaration. A future rebrand
8495        // (`:limits` → `:sandbox` matching Lunatic per-process
8496        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
8497        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
8498        // matching Erlang's verbatim appup name) lands as an edit to
8499        // exactly one const, and every consumer that reaches for the
8500        // label picks it up at build time rather than at runtime as a
8501        // downstream mismatch.
8502        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
8503        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
8504        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
8505    }
8506
8507    #[test]
8508    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
8509        // Production-through-const pin: the three per-arm labels the
8510        // [`Caixa::declared_servico_slots`] tagger pushes onto its
8511        // return `Vec` route through the lifted
8512        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8513        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8514        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
8515        // declaration order. A future re-order or drift at the tagger
8516        // (a rename that reaches the tagger but not the const, or vice
8517        // versa) surfaces here at build time rather than at runtime as
8518        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
8519        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8520        // commit. Mirror of the peer
8521        // [`crate::behavior::BehaviorSpec::declared_slots`] production
8522        // tagger pin (889dc18) on the sibling per-callback axis.
8523        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
8524        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8525        c.limits = Some(crate::LimitsSpec {
8526            fuel: Some(1_000_000),
8527            ..Default::default()
8528        });
8529        c.behavior = Some(BehaviorSpec {
8530            on_init: Some(PathBuf::from("lib/init.lisp")),
8531            ..Default::default()
8532        });
8533        c.upgrade_from = vec![UpgradeFromEntry {
8534            from: "0.1.0".into(),
8535            instructions: vec![UpgradeInstruction::Restart],
8536        }];
8537        assert_eq!(
8538            c.declared_servico_slots(),
8539            vec![
8540                crate::render::M2_AUTHOR_KEY_LIMITS,
8541                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
8542                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8543            ]
8544        );
8545    }
8546
8547    #[test]
8548    fn existing_manifests_unaffected_by_new_optional_slots() {
8549        // Regression test: a caixa.lisp authored before M2 typed slots
8550        // should still parse + serialize cleanly. The bare `defcaixa`
8551        // emitted by `Caixa::template` has none of the new fields.
8552        let src = Caixa::template("legacy");
8553        let c = Caixa::from_lisp(&src).unwrap();
8554        assert!(c.limits.is_none());
8555        assert!(c.behavior.is_none());
8556        assert!(c.upgrade_from.is_empty());
8557        assert!(c.estrategia.is_none());
8558        assert!(c.children.is_empty());
8559
8560        // And to_lisp emits a manifest with the new slots in the
8561        // empty/default state — round-trippable.
8562        let emitted = c.to_lisp();
8563        let back = Caixa::from_lisp(&emitted).unwrap();
8564        assert_eq!(c, back);
8565    }
8566
8567    #[test]
8568    fn validate_deps_accepts_canonical_caixa() {
8569        // Positive control: the bare template — zero deps, zero
8570        // deps_dev — passes the gate trivially. A future axis added to
8571        // `Dep::validate` mustn't regress an empty-deps caixa to a
8572        // build error.
8573        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8574        c.validate_deps().unwrap();
8575    }
8576
8577    #[test]
8578    fn validate_deps_rejects_invalid_versao_in_deps() {
8579        // Fail-before-pass-after pin: a malformed `:deps :versao`
8580        // surfaces at validate_deps() time, not at lacre-resolve time.
8581        // Mirrors `rejects_invalid_membro_versao_requirement` and
8582        // `validate_rejects_invalid_child_versao_requirement` on the
8583        // other two `:versao` axes.
8584        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8585        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
8586        let err = c.validate_deps().unwrap_err();
8587        assert!(
8588            matches!(
8589                err,
8590                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8591                    if nome == "caixa-teia" && versao == "^bad-version"
8592            ),
8593            "got {err:?}"
8594        );
8595    }
8596
8597    #[test]
8598    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
8599        // Parity pin: `:deps-dev` must run through the same per-entry
8600        // validator as `:deps` — a typo in either axis surfaces the
8601        // same diagnostic. Without this leg, `:deps-dev` would be a
8602        // second-class citizen of the typed surface and an author
8603        // could land a build that passes validate_deps but fails at
8604        // `feira lock`-time when the dev-dep is resolved for a test
8605        // build.
8606        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8607        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
8608        let err = c.validate_deps().unwrap_err();
8609        assert!(
8610            matches!(
8611                err,
8612                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8613                    if nome == "tatara-check" && versao == "^^0.1"
8614            ),
8615            "got {err:?}"
8616        );
8617    }
8618
8619    #[test]
8620    fn validate_deps_runs_deps_before_deps_dev() {
8621        // Order pin: when both lists carry typos, the `:deps`
8622        // diagnostic surfaces first. The author's mental model is
8623        // "runtime deps are load-bearing; dev deps are scaffolding";
8624        // surfacing the runtime axis first matches that hierarchy.
8625        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8626        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
8627        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
8628        let err = c.validate_deps().unwrap_err();
8629        assert!(
8630            matches!(
8631                err,
8632                crate::dep::DepError::VersaoInvalid { ref nome, .. }
8633                    if nome == "runtime-dep"
8634            ),
8635            "expected `:deps` typo to surface first, got {err:?}"
8636        );
8637    }
8638
8639    #[test]
8640    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
8641        // Positive control sweep across both lists. Pin every
8642        // canonical Cargo-shaped form so a future tightening of the
8643        // accepted set surfaces here as a test failure (parity with
8644        // `accepts_canonical_membro_versao_forms` and
8645        // `validate_accepts_canonical_child_versao_forms`).
8646        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8647        c.deps = vec![
8648            Dep::simple("caret", "^0.1"),
8649            Dep::simple("tilde", "~0.1.2"),
8650            Dep::simple("exact", "0.1.0"),
8651            Dep::simple("wildcard", "*"),
8652            Dep::simple("multi-range", ">=0.1, <2"),
8653        ];
8654        c.deps_dev = vec![
8655            Dep::simple("dev-caret", "^0.1"),
8656            Dep::simple("dev-wildcard", "*"),
8657        ];
8658        c.validate_deps().unwrap();
8659    }
8660
8661    #[test]
8662    fn validate_deps_diagnostic_carries_offending_dep() {
8663        // Diagnostic-shape pin: the error names the offending entry's
8664        // `:nome` + `:versao` verbatim and carries a non-empty
8665        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
8666        // run can render the diagnostic without re-parsing.
8667        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8668        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
8669        let err = c.validate_deps().unwrap_err();
8670        let crate::dep::DepError::VersaoInvalid {
8671            nome,
8672            versao,
8673            reason,
8674        } = err
8675        else {
8676            panic!("expected VersaoInvalid, got other variant");
8677        };
8678        assert_eq!(nome, "caixa-teia");
8679        assert_eq!(versao, "not-a-req");
8680        assert!(
8681            !reason.is_empty(),
8682            "VersaoInvalid `reason` must carry the parser's wording verbatim"
8683        );
8684    }
8685
8686    #[test]
8687    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
8688        // Cross-axis pin: `validate_deps` walks both :deps and
8689        // :deps-dev through `Dep::validate`, and the new fonte gate
8690        // (`:tag` + `:branch` both set — the canonical "pin drift"
8691        // footgun) must surface from the :deps-dev arm with the
8692        // offending entry's :nome named. Pin the :deps-dev arm
8693        // explicitly so a future shortcut that only walks :deps
8694        // surfaces here as a regression.
8695        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8696        c.deps_dev = vec![Dep {
8697            nome: "dev-only".into(),
8698            versao: "^0.1".into(),
8699            fonte: Some(crate::DepSource::Git {
8700                repo: "github:p/x".into(),
8701                tag: Some("v1".into()),
8702                rev: None,
8703                branch: Some("main".into()),
8704            }),
8705            opcional: false,
8706            caracteristicas: vec![],
8707        }];
8708        let err = c.validate_deps().unwrap_err();
8709        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
8710            panic!("expected FontePinAmbiguous from :deps-dev walk");
8711        };
8712        assert_eq!(nome, "dev-only");
8713        assert!(pins.contains(":tag") && pins.contains(":branch"));
8714    }
8715
8716    #[test]
8717    fn validate_deps_rejects_empty_repo_in_deps() {
8718        // Parity pin on the :deps arm: an empty :repo on the runtime
8719        // deps list surfaces the same FonteRepoEmpty diagnostic the
8720        // dep.rs per-entry tests pin, naming the offending entry.
8721        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8722        c.deps = vec![Dep {
8723            nome: "runtime".into(),
8724            versao: "^0.1".into(),
8725            fonte: Some(crate::DepSource::Git {
8726                repo: String::new(),
8727                tag: Some("v1".into()),
8728                rev: None,
8729                branch: None,
8730            }),
8731            opcional: false,
8732            caracteristicas: vec![],
8733        }];
8734        let err = c.validate_deps().unwrap_err();
8735        assert!(
8736            matches!(
8737                err,
8738                crate::dep::DepError::FonteRepoEmpty { ref nome }
8739                    if nome == "runtime"
8740            ),
8741            "got {err:?}"
8742        );
8743    }
8744
8745    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
8746
8747    #[test]
8748    fn validate_deps_rejects_duplicate_nome_in_deps() {
8749        // Fail-before-pass-after pin: two `:deps` entries naming the same
8750        // caixa carry two `:versao` / `:fonte` / feature triples that the
8751        // caixa-resolver's lacre pipeline collapses (the second silently
8752        // overwrites the first at `concrete_versao`-resolve time). The
8753        // gate surfaces the duplicate at validate-time, naming the
8754        // offending caixa + the list, before the resolver-side silent
8755        // drop. Mirrors the peer typed-graph duplicate gates
8756        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
8757        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8758        c.deps = vec![
8759            Dep::simple("caixa-teia", "^0.1"),
8760            Dep::simple("caixa-teia", "^0.2"),
8761        ];
8762        let err = c.validate_deps().unwrap_err();
8763        assert!(
8764            matches!(
8765                err,
8766                crate::dep::DepError::DuplicateNome { ref nome, list }
8767                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8768            ),
8769            "got {err:?}"
8770        );
8771    }
8772
8773    #[test]
8774    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
8775        // Parity pin: `:deps-dev` runs through the same per-list
8776        // duplicate check as `:deps` — neither axis is a second-class
8777        // citizen of the set-not-multiset discipline.
8778        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8779        c.deps_dev = vec![
8780            Dep::simple("tatara-check", "*"),
8781            Dep::simple("tatara-check", "^0.1"),
8782        ];
8783        let err = c.validate_deps().unwrap_err();
8784        assert!(
8785            matches!(
8786                err,
8787                crate::dep::DepError::DuplicateNome { ref nome, list }
8788                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
8789            ),
8790            "got {err:?}"
8791        );
8792    }
8793
8794    #[test]
8795    fn validate_deps_accepts_cross_list_same_nome() {
8796        // The Cargo `[dependencies]` + `[dev-dependencies]` override
8797        // convention is preserved: a name appearing in *both* lists is
8798        // valid (the dev-pin overrides at test/dev time). Only
8799        // within-list duplicates are structurally incoherent — pin the
8800        // permissive cross-list semantics so a future shortcut that
8801        // collapses the two seen-sets into one surfaces here as a test
8802        // failure.
8803        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8804        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
8805        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
8806        c.validate_deps().unwrap();
8807    }
8808
8809    #[test]
8810    fn validate_deps_accepts_distinct_nome_in_both_lists() {
8811        // Positive control: distinct names within each list pass — the
8812        // gate's identity element on the canonical authoring shape.
8813        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8814        c.deps = vec![
8815            Dep::simple("caixa-teia", "^0.1"),
8816            Dep::simple("pleme-mesh", "*"),
8817        ];
8818        c.deps_dev = vec![
8819            Dep::simple("tatara-check", "*"),
8820            Dep::simple("dev-shim", "^0.1"),
8821        ];
8822        c.validate_deps().unwrap();
8823    }
8824
8825    #[test]
8826    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
8827        // Diagnostic-precedence pin: a malformed `:versao` on the
8828        // duplicating entry surfaces its narrower `VersaoInvalid`
8829        // diagnostic first, before the cross-entry duplicate gate fires
8830        // — the canonical "per-entry shape before cross-entry uniqueness"
8831        // precedence every peer set-not-multiset gate establishes
8832        // (`*_invalid_fires_before_duplicate_check` pins on
8833        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
8834        // `validate_upgrade_from`).
8835        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8836        c.deps = vec![
8837            Dep::simple("caixa-teia", "^0.1"),
8838            Dep::simple("caixa-teia", "^bad-version"),
8839        ];
8840        let err = c.validate_deps().unwrap_err();
8841        assert!(
8842            matches!(
8843                err,
8844                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8845                    if nome == "caixa-teia" && versao == "^bad-version"
8846            ),
8847            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
8848        );
8849    }
8850
8851    #[test]
8852    fn validate_deps_duplicate_diagnostic_names_first_collision() {
8853        // First-collision determinism pin: with three entries naming the
8854        // same caixa, the first colliding pair surfaces — not the last.
8855        // Mirrors the peer first-collision posture on every
8856        // duplicate-target gate
8857        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
8858        // — the second entry is the first collision; this gate uses the
8859        // same shape: the second entry's `:nome` lands in the diagnostic
8860        // because `seen.insert(first.nome)` already populated the set).
8861        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8862        c.deps = vec![
8863            Dep::simple("caixa-teia", "^0.1"),
8864            Dep::simple("caixa-teia", "^0.2"),
8865            Dep::simple("caixa-teia", "^0.3"),
8866        ];
8867        let err = c.validate_deps().unwrap_err();
8868        // The diagnostic carries the offending caixa name; the
8869        // implementation surfaces on the *second* entry (the first
8870        // collision), so the test pins the `:nome` value.
8871        assert!(
8872            matches!(
8873                err,
8874                crate::dep::DepError::DuplicateNome { ref nome, list }
8875                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8876            ),
8877            "got {err:?}"
8878        );
8879    }
8880
8881    #[test]
8882    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
8883        // Cross-list precedence pin: when both lists carry duplicates,
8884        // the `:deps` diagnostic surfaces first — same author-mental-
8885        // model ordering the `validate_deps_runs_deps_before_deps_dev`
8886        // pin establishes for malformed `:versao` (runtime axis before
8887        // dev axis).
8888        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8889        c.deps = vec![
8890            Dep::simple("runtime-dep", "^0.1"),
8891            Dep::simple("runtime-dep", "^0.2"),
8892        ];
8893        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
8894        let err = c.validate_deps().unwrap_err();
8895        assert!(
8896            matches!(
8897                err,
8898                crate::dep::DepError::DuplicateNome { ref nome, list }
8899                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
8900            ),
8901            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
8902        );
8903    }
8904
8905    #[test]
8906    fn validate_deps_empty_lists_pass_duplicate_gate() {
8907        // Empty-set identity pin: the bare template (zero deps, zero
8908        // deps_dev) passes the duplicate gate as the gate's identity
8909        // element. A future tighten that conflates "empty" with
8910        // "missing" would regress this baseline.
8911        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8912        c.validate_deps().unwrap();
8913    }
8914
8915    #[test]
8916    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
8917        // Diagnostic-shape pin: the `list:` field tags which list the
8918        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
8919        // `feira lint` run can route the author to the right block in
8920        // their caixa.lisp without re-deriving the list from context.
8921        // Same self-locating shape every peer per-axis diagnostic
8922        // already exposes.
8923        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8924        c.deps_dev = vec![
8925            Dep::simple("dev-thing", "*"),
8926            Dep::simple("dev-thing", "^0.1"),
8927        ];
8928        let err = c.validate_deps().unwrap_err();
8929        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
8930            panic!("expected DuplicateNome from :deps-dev walk");
8931        };
8932        assert_eq!(nome, "dev-thing");
8933        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
8934    }
8935
8936    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
8937
8938    #[test]
8939    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
8940        // Thread-through pin on `:deps`: the per-entry
8941        // `Dep::validate_caracteristicas` gate fires inside
8942        // `Caixa::validate_deps`'s linear walk, so a malformed feature
8943        // list on any `:deps` entry surfaces as a `DepError` from
8944        // `validate_deps` — the same reachability shape every per-entry
8945        // `Dep::validate` arm threads through. Without this pin a future
8946        // shortcut that skips the per-entry `Dep::validate` call on the
8947        // cross-entry-uniqueness path would mask the within-entry
8948        // `:caracteristicas` gates.
8949        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8950        c.deps = vec![Dep {
8951            nome: "caixa-teia".into(),
8952            versao: "^0.1".into(),
8953            fonte: None,
8954            opcional: false,
8955            caracteristicas: vec!["http".into(), "http".into()],
8956        }];
8957        let err = c.validate_deps().unwrap_err();
8958        let crate::dep::DepError::CaracteristicaDuplicate {
8959            nome,
8960            caracteristica,
8961        } = err
8962        else {
8963            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
8964        };
8965        assert_eq!(nome, "caixa-teia");
8966        assert_eq!(caracteristica, "http");
8967    }
8968
8969    #[test]
8970    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
8971        // Peer thread-through pin on `:deps-dev`: same reachability as
8972        // the `:deps` arm above, on the dev-only authoring axis. Pins
8973        // that the `validate_deps` walk visits both lists' per-entry
8974        // gates uniformly. The empty-feature arm carries here so both
8975        // new `:caracteristicas` arms are surfaced via at least one
8976        // `validate_deps` thread-through.
8977        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8978        c.deps_dev = vec![Dep {
8979            nome: "caixa-teia".into(),
8980            versao: "^0.1".into(),
8981            fonte: None,
8982            opcional: false,
8983            caracteristicas: vec![String::new()],
8984        }];
8985        let err = c.validate_deps().unwrap_err();
8986        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
8987            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
8988        };
8989        assert_eq!(nome, "caixa-teia");
8990    }
8991
8992    #[test]
8993    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
8994        // Thread-through pin on `:deps`: the per-entry
8995        // `Dep::validate_caracteristicas` value-shape gate (lifted via
8996        // `crate::render::is_cargo_feature_name`) fires inside
8997        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
8998        // a structurally invalid feature name on any `:deps` entry
8999        // surfaces as `DepError::CaracteristicaInvalid` from
9000        // `validate_deps` — the same reachability shape every per-entry
9001        // `Dep::validate` arm threads through. Without this pin a
9002        // future shortcut that skips the per-entry `Dep::validate` call
9003        // on the cross-entry-uniqueness path would mask the within-
9004        // entry `:caracteristicas` value-shape gate.
9005        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9006        c.deps = vec![Dep {
9007            nome: "caixa-teia".into(),
9008            versao: "^0.1".into(),
9009            fonte: None,
9010            opcional: false,
9011            caracteristicas: vec!["+http".into()],
9012        }];
9013        let err = c.validate_deps().unwrap_err();
9014        let crate::dep::DepError::CaracteristicaInvalid {
9015            nome,
9016            caracteristica,
9017            ..
9018        } = err
9019        else {
9020            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
9021        };
9022        assert_eq!(nome, "caixa-teia");
9023        assert_eq!(caracteristica, "+http");
9024    }
9025
9026    #[test]
9027    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
9028        // Peer thread-through pin on `:deps-dev`: same reachability as
9029        // the `:deps` arm above, on the dev-only authoring axis. The
9030        // `http/json` shape carries here so the segment-separator
9031        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
9032        // confusion footgun) is surfaced via the cross-entry walk too —
9033        // pinning that the `:deps-dev` list visits the same per-entry
9034        // value-shape gate as the `:deps` list.
9035        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9036        c.deps_dev = vec![Dep {
9037            nome: "caixa-teia".into(),
9038            versao: "^0.1".into(),
9039            fonte: None,
9040            opcional: false,
9041            caracteristicas: vec!["http/json".into()],
9042        }];
9043        let err = c.validate_deps().unwrap_err();
9044        let crate::dep::DepError::CaracteristicaInvalid {
9045            nome,
9046            caracteristica,
9047            ..
9048        } = err
9049        else {
9050            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
9051        };
9052        assert_eq!(nome, "caixa-teia");
9053        assert_eq!(caracteristica, "http/json");
9054    }
9055
9056    #[test]
9057    fn to_lisp_preserves_deps() {
9058        let src = r#"
9059(defcaixa
9060  :nome "x"
9061  :versao "0.1.0"
9062  :kind Biblioteca
9063  :deps ((:nome "a" :versao "^0.1")
9064         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
9065"#;
9066        let c1 = Caixa::from_lisp(src).unwrap();
9067        let emitted = c1.to_lisp();
9068        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
9069        assert_eq!(c1.deps, c2.deps);
9070    }
9071
9072    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
9073
9074    fn caixa_with_nome(nome: &str) -> Caixa {
9075        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
9076        c.nome = nome.to_string();
9077        c
9078    }
9079
9080    #[test]
9081    fn validate_nome_accepts_canonical_template() {
9082        // Positive control: the bare `feira init`-style template's
9083        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
9084        // not regress this baseline shape. A future tightening of the
9085        // accepted set surfaces here as a test failure first.
9086        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9087        c.validate_nome().unwrap();
9088    }
9089
9090    #[test]
9091    fn validate_nome_accepts_canonical_forms() {
9092        // Positive-set sweep: each realistic caixa-name shape the K8s
9093        // apiserver accepts as a `metadata.name` label must pass —
9094        // single-word, hyphen-joined, version-suffixed, single-char,
9095        // two-char, digit-start (DNS-1123 allows this; the stricter
9096        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
9097        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
9098        // the peer member-name axis.
9099        for nome in [
9100            "checkout",
9101            "cart-v2",
9102            "a",
9103            "db",
9104            "3rd-party-shim",
9105            "payment-retry",
9106            "0",
9107        ] {
9108            caixa_with_nome(nome)
9109                .validate_nome()
9110                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
9111        }
9112    }
9113
9114    #[test]
9115    fn validate_nome_rejects_empty() {
9116        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9117        // an empty `:nome` (the derive macro stores the raw String);
9118        // the gate's empty arm names the offending axis with a narrower
9119        // diagnostic than the `NomeInvalid` parse arm would emit.
9120        let c = caixa_with_nome("");
9121        let err = c.validate_nome().unwrap_err();
9122        assert_eq!(err, ManifestError::NomeEmpty);
9123    }
9124
9125    #[test]
9126    fn validate_nome_rejects_uppercase() {
9127        // The canonical "I copied the TitleCase display name verbatim"
9128        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
9129        // admission on every derived artifact (Helm chart, ComputeUnit,
9130        // CNP, HTTPRoute, label values); the gate moves the diagnostic
9131        // to the source `caixa.lisp` and the reason suggests the
9132        // lowercased fix verbatim.
9133        let c = caixa_with_nome("MyApp");
9134        let err = c.validate_nome().unwrap_err();
9135        let ManifestError::NomeInvalid { nome, reason } = err else {
9136            panic!("expected NomeInvalid for uppercase :nome");
9137        };
9138        assert_eq!(nome, "MyApp");
9139        assert!(
9140            reason.contains("uppercase") && reason.contains("myapp"),
9141            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
9142        );
9143    }
9144
9145    #[test]
9146    fn validate_nome_rejects_underscore() {
9147        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
9148        // `_`; the apiserver rejects on admission across every derived
9149        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
9150        // and `:children :caixa` (31bfa43).
9151        let c = caixa_with_nome("my_app");
9152        let err = c.validate_nome().unwrap_err();
9153        assert!(
9154            matches!(
9155                err,
9156                ManifestError::NomeInvalid { ref nome, ref reason }
9157                    if nome == "my_app" && reason.contains('_')
9158            ),
9159            "got {err:?}"
9160        );
9161    }
9162
9163    #[test]
9164    fn validate_nome_rejects_dot() {
9165        // A `:nome` is a single DNS-1123 label, not a subdomain. The
9166        // "I want to namespace with `.`" footgun the gate redirects to
9167        // `-` via the shared predicate's reason wording.
9168        let c = caixa_with_nome("team.app");
9169        let err = c.validate_nome().unwrap_err();
9170        assert!(
9171            matches!(
9172                err,
9173                ManifestError::NomeInvalid { ref nome, ref reason }
9174                    if nome == "team.app" && reason.contains('.')
9175            ),
9176            "got {err:?}"
9177        );
9178    }
9179
9180    #[test]
9181    fn validate_nome_rejects_leading_hyphen() {
9182        // DNS-1123 boundary rule: the label must start with an ASCII
9183        // alphanumeric. Pin the leading-`-` arm explicitly.
9184        let c = caixa_with_nome("-app");
9185        let err = c.validate_nome().unwrap_err();
9186        assert!(
9187            matches!(
9188                err,
9189                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
9190            ),
9191            "got {err:?}"
9192        );
9193    }
9194
9195    #[test]
9196    fn validate_nome_rejects_trailing_hyphen() {
9197        // Symmetric arm of the boundary rule, pinned separately so a
9198        // future relaxation that only checks the leading position
9199        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
9200        // and `_with_trailing_hyphen` on the supervisor / aplicacao
9201        // axes.
9202        let c = caixa_with_nome("app-");
9203        let err = c.validate_nome().unwrap_err();
9204        assert!(
9205            matches!(
9206                err,
9207                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
9208            ),
9209            "got {err:?}"
9210        );
9211    }
9212
9213    #[test]
9214    fn validate_nome_rejects_unicode() {
9215        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
9216        // bytes are rejected by the K8s apiserver on every name axis.
9217        let c = caixa_with_nome("café");
9218        let err = c.validate_nome().unwrap_err();
9219        assert!(
9220            matches!(
9221                err,
9222                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
9223            ),
9224            "got {err:?}"
9225        );
9226    }
9227
9228    #[test]
9229    fn validate_nome_rejects_whitespace() {
9230        // The paste-from-sketch / paste-from-spec footgun. Internal
9231        // whitespace is rejected by every K8s name axis.
9232        let c = caixa_with_nome("my app");
9233        let err = c.validate_nome().unwrap_err();
9234        assert!(
9235            matches!(
9236                err,
9237                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
9238            ),
9239            "got {err:?}"
9240        );
9241    }
9242
9243    #[test]
9244    fn validate_nome_rejects_too_long() {
9245        // 64-byte boundary pin: the K8s apiserver rejects any
9246        // `metadata.name` over 63 bytes at admission; the diagnostic
9247        // names both the 63-byte cap and the actual length so the
9248        // author can shorten in one edit. Mirrors `_too_long` on the
9249        // peer member-/cluster-/child-name axes.
9250        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
9251        let c = caixa_with_nome(&over);
9252        let err = c.validate_nome().unwrap_err();
9253        let ManifestError::NomeInvalid { nome, reason } = err else {
9254            panic!("expected NomeInvalid for over-cap :nome");
9255        };
9256        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
9257        assert!(
9258            reason.contains("63") && reason.contains("64"),
9259            "diagnostic must name the cap + actual length, got {reason:?}"
9260        );
9261    }
9262
9263    #[test]
9264    fn nome_max_length_validates() {
9265        // The 63-byte cap exactly — the boundary-accepting case pinned
9266        // alongside `validate_nome_rejects_too_long` so a future cap
9267        // shift surfaces both arms simultaneously. Mirrors
9268        // `membro_caixa_max_length_validates`,
9269        // `placement_cluster_max_length_validates`,
9270        // `child_caixa_max_length_validates`.
9271        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9272        caixa_with_nome(&at_cap).validate_nome().unwrap();
9273    }
9274
9275    #[test]
9276    fn nome_empty_takes_precedence_over_invalid() {
9277        // Order pin: the empty arm fires before the predicate is
9278        // consulted. Empty < invalid in self-locating-ness — the
9279        // narrower `NomeEmpty` diagnostic doesn't carry a useless
9280        // `nome: ""` reference into the parser-shaped reason. Mirrors
9281        // `membro_caixa_empty_takes_precedence_over_invalid` on the
9282        // peer axis (3f9d7a0).
9283        let c = caixa_with_nome("");
9284        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
9285    }
9286
9287    #[test]
9288    fn nome_invalid_diagnostic_carries_offending_nome() {
9289        // Diagnostic-shape pin: the error names the offending `:nome`
9290        // verbatim with a non-empty parser-shaped reason, so a `feira
9291        // lint` run can render the diagnostic without re-parsing.
9292        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
9293        let c = caixa_with_nome("MyApp");
9294        let err = c.validate_nome().unwrap_err();
9295        let ManifestError::NomeInvalid { nome, reason } = err else {
9296            panic!("expected NomeInvalid variant");
9297        };
9298        assert_eq!(nome, "MyApp");
9299        assert!(
9300            !reason.is_empty(),
9301            "NomeInvalid `reason` must carry the predicate's wording verbatim"
9302        );
9303    }
9304
9305    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
9306    //
9307    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
9308    // via DNS-1123; this second-axis gate caps the joint
9309    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
9310    // canonical [`crate::lareira_chart_name`] helper's doc comment
9311    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
9312    // "the M4 admission webhook will pin the joint-length invariant
9313    // when it lands". These tests pin it at the manifest-validate
9314    // layer instead, fail-before-pass-after on the 56-byte boundary.
9315
9316    #[test]
9317    fn validate_nome_chart_name_budget_accepts_canonical_template() {
9318        // Positive control: the bare `feira init`-style template's
9319        // `:nome` ("demo") sits far below the cap; the gate must not
9320        // regress this baseline. Same shape every peer
9321        // value-shape-gate baseline pin uses.
9322        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9323        c.validate_nome_chart_name_budget().unwrap();
9324    }
9325
9326    #[test]
9327    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
9328        // Positive-set sweep across the canonical author surface every
9329        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
9330        // `worker`, the `checkout-aplicacao` example members, the
9331        // `example-attest` caixa-tatara fixture). Every value sits
9332        // far below the 55-byte per-`:nome` budget. Same shape every
9333        // peer per-axis baseline pin uses.
9334        for nome in [
9335            "hello-rio",
9336            "cart",
9337            "checkout",
9338            "worker",
9339            "example-attest",
9340            "demo",
9341            "a",
9342        ] {
9343            caixa_with_nome(nome)
9344                .validate_nome_chart_name_budget()
9345                .unwrap_or_else(|e| {
9346                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
9347                });
9348        }
9349    }
9350
9351    #[test]
9352    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
9353        // Boundary-accepting case at the 55-byte per-`:nome` budget —
9354        // the joint chart name is exactly 63 bytes, the DNS-1123 label
9355        // cap. Pinned alongside the rejecting-arm test so a future cap
9356        // shift surfaces both arms simultaneously. Mirrors
9357        // `nome_max_length_validates` on the peer bare-`:nome` axis.
9358        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
9359        caixa_with_nome(&at_cap)
9360            .validate_nome_chart_name_budget()
9361            .unwrap();
9362    }
9363
9364    #[test]
9365    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
9366        // Fail-before-pass-after pin on the 56-byte boundary: the
9367        // smallest `:nome` length that overflows the joint chart-name
9368        // cap. The inner [`is_dns_1123_label`] gate
9369        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
9370        // this gate it silently passed the manifest-validate cascade
9371        // and surfaced as a `helm lint` / apiserver rejection on the
9372        // rendered chart name far from the source `caixa.lisp`, with
9373        // no field naming the overflow. With this gate the diagnostic
9374        // names the offending `:nome` verbatim alongside the rendered
9375        // chart name and the budget, so the author can shorten in one
9376        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
9377        // bare-`:nome` axis.
9378        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9379        let c = caixa_with_nome(&over);
9380        let err = c.validate_nome_chart_name_budget().unwrap_err();
9381        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9382            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
9383        };
9384        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9385        assert_eq!(nome, over);
9386        assert!(
9387            reason.contains("63") && reason.contains("64") && reason.contains("55"),
9388            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
9389             and the per-`:nome` budget (55), got {reason:?}"
9390        );
9391    }
9392
9393    #[test]
9394    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
9395        // The 63-byte `:nome` boundary — passes the bare-`:nome`
9396        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
9397        // joint chart name that overflows the DNS-1123 label cap
9398        // structurally. The most stringent fail-before-pass-after
9399        // surface: every `:nome` in the 56..=63-byte range passed the
9400        // prior cascade and broke at admission.
9401        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9402        let c = caixa_with_nome(&bare_max);
9403        // The bare-`:nome` gate accepts the 63-byte length.
9404        c.validate_nome().unwrap();
9405        // The new joint-length gate rejects it.
9406        let err = c.validate_nome_chart_name_budget().unwrap_err();
9407        assert!(
9408            matches!(
9409                err,
9410                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
9411                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
9412            ),
9413            "got {err:?}"
9414        );
9415    }
9416
9417    #[test]
9418    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
9419        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
9420        // name appears verbatim in the diagnostic so the author sees
9421        // exactly the string the apiserver / `helm lint` would have
9422        // rejected — no re-derivation required to grep the source.
9423        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
9424        // on the bare-`:nome` axis.
9425        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
9426        let c = caixa_with_nome(&over);
9427        let err = c.validate_nome_chart_name_budget().unwrap_err();
9428        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9429            panic!("expected NomeChartNameBudgetExceeded variant");
9430        };
9431        assert_eq!(nome, over);
9432        let expected_chart = crate::lareira_chart_name(&over);
9433        assert!(
9434            reason.contains(&expected_chart),
9435            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
9436             got {reason:?}"
9437        );
9438        assert!(
9439            reason.contains("lareira-"),
9440            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
9441        );
9442    }
9443
9444    #[test]
9445    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
9446        // Order pin on the layout cascade: the narrower
9447        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
9448        // joint-length budget. A structurally-malformed `:nome` (here:
9449        // uppercase) surfaces its specific shape error rather than
9450        // the chart-name-budget error, even when the joint length
9451        // would also overflow — the narrower diagnostic is more
9452        // self-locating. Mirrors the cascade-precedence pins peer
9453        // gates already use (e.g. `EntradaParaEmpty` before
9454        // `EntradaParaInvalid`).
9455        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9456        let c = caixa_with_nome(&over);
9457        // The bare-shape gate fires first.
9458        let err = c.validate_nome().unwrap_err();
9459        assert!(
9460            matches!(err, ManifestError::NomeInvalid { .. }),
9461            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
9462        );
9463        // And the layout verify cascade surfaces that diagnostic, not
9464        // the budget arm. Inject a path-exists oracle so the cascade
9465        // gets past the manifest-presence check and into the
9466        // value-shape gates.
9467        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9468        let err = crate::LayoutInvariants::verify(
9469            &layout,
9470            &c,
9471            std::path::Path::new("/tmp/caixa-test-fake-root"),
9472        )
9473        .unwrap_err();
9474        let issue = err.to_string();
9475        assert!(
9476            issue.contains("DNS-1123") || issue.contains("uppercase"),
9477            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
9478             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
9479        );
9480    }
9481
9482    #[test]
9483    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
9484        // Cross-axis envelope pin: the layout cascade wraps both
9485        // bare-`:nome` and joint-length-`:nome` failures through the
9486        // same [`LayoutError::NomeViolation`] envelope, since both
9487        // arms are on the `:nome` axis. The user's diagnostic stays
9488        // self-locating ("which axis"), and a future consumer that
9489        // dispatches on the layout-error variant (e.g. a `feira lint`
9490        // exit-code mapping) sees a single per-axis envelope. The
9491        // wrapped `issue:` carries the full inner diagnostic.
9492        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9493        let c = caixa_with_nome(&over);
9494        // The bare-shape gate accepts.
9495        c.validate_nome().unwrap();
9496        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9497        let err = crate::LayoutInvariants::verify(
9498            &layout,
9499            &c,
9500            std::path::Path::new("/tmp/caixa-test-fake-root"),
9501        )
9502        .unwrap_err();
9503        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
9504            panic!("expected LayoutError::NomeViolation, got {err:?}");
9505        };
9506        assert_eq!(caixa, over);
9507        assert!(
9508            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
9509            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
9510        );
9511    }
9512
9513    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
9514
9515    fn caixa_with_versao(versao: &str) -> Caixa {
9516        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9517        c.versao = versao.to_string();
9518        c
9519    }
9520
9521    #[test]
9522    fn validate_versao_accepts_canonical_template() {
9523        // Positive control: the bare `feira init`-style template's
9524        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
9525        // must not regress this baseline shape. A future tightening of
9526        // the accepted set surfaces here as a test failure first.
9527        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9528        c.validate_versao().unwrap();
9529    }
9530
9531    #[test]
9532    fn validate_versao_accepts_canonical_forms() {
9533        // Positive-set sweep: each realistic SemVer-2 shape the
9534        // substrate's downstream consumers accept must pass — bare
9535        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
9536        // build metadata (`+build.42`), the combined form, and the
9537        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
9538        // the peer `:nome` axis (6c992f8).
9539        for versao in [
9540            "0.1.0",
9541            "0.0.0",
9542            "1.0.0",
9543            "0.2.0-rc.1",
9544            "1.0.0-alpha.0",
9545            "1.0.0+build.42",
9546            "1.0.0-rc.1+build.42",
9547            "10.20.30",
9548        ] {
9549            caixa_with_versao(versao)
9550                .validate_versao()
9551                .unwrap_or_else(|e| {
9552                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
9553                });
9554        }
9555    }
9556
9557    #[test]
9558    fn validate_versao_rejects_empty() {
9559        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9560        // an empty `:versao` (the derive macro stores the raw String);
9561        // the gate's empty arm names the offending axis with a narrower
9562        // diagnostic than the `VersaoInvalid` parse arm would emit.
9563        // Mirrors `validate_nome_rejects_empty` (6c992f8).
9564        let c = caixa_with_versao("");
9565        let err = c.validate_versao().unwrap_err();
9566        assert_eq!(err, ManifestError::VersaoEmpty);
9567    }
9568
9569    #[test]
9570    fn validate_versao_rejects_git_tag_shape() {
9571        // The canonical "I copied the git tag verbatim" footgun —
9572        // `feira publish` *emits* `v<versao>` git tags, so a leaked
9573        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
9574        // shift every downstream consumer's version axis. `semver`
9575        // rejects the leading `v` at parse time; the gate moves the
9576        // diagnostic to the source `caixa.lisp`.
9577        let c = caixa_with_versao("v0.1.0");
9578        let err = c.validate_versao().unwrap_err();
9579        let ManifestError::VersaoInvalid { versao, reason } = err else {
9580            panic!("expected VersaoInvalid for git-tag-shape :versao");
9581        };
9582        assert_eq!(versao, "v0.1.0");
9583        assert!(
9584            !reason.is_empty(),
9585            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
9586        );
9587    }
9588
9589    #[test]
9590    fn validate_versao_rejects_missing_patch() {
9591        // The canonical "I shortened it" footgun — SemVer-2 requires
9592        // three parts. Cargo's `version =` field accepts the shortened
9593        // form as a requirement, conflating the two leaks across the
9594        // typed `:deps :versao` vs top-level `:versao` axes; the gate
9595        // pins the top-level axis to the strict three-part shape.
9596        let c = caixa_with_versao("0.1");
9597        let err = c.validate_versao().unwrap_err();
9598        assert!(
9599            matches!(
9600                err,
9601                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
9602            ),
9603            "got {err:?}"
9604        );
9605    }
9606
9607    #[test]
9608    fn validate_versao_rejects_requirement_shape() {
9609        // The canonical "I leaked a requirement into a version" footgun —
9610        // the typed `:deps :versao` / `:membros :versao` axes accept
9611        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
9612        // concrete `Version`. Without this gate the two typed surfaces
9613        // would silently overlap, and a top-level `^0.1` would surface
9614        // at `helm install` time as a Chart.yaml version rejection far
9615        // from the source `caixa.lisp`.
9616        let c = caixa_with_versao("^0.1");
9617        let err = c.validate_versao().unwrap_err();
9618        assert!(
9619            matches!(
9620                err,
9621                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
9622            ),
9623            "got {err:?}"
9624        );
9625    }
9626
9627    #[test]
9628    fn validate_versao_rejects_docker_tag_shape() {
9629        // The "I confused it with a docker tag" footgun — `latest`,
9630        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
9631        // SemVer rejects at parse time; the gate moves the diagnostic
9632        // to the source `caixa.lisp`.
9633        for bad in ["latest", "main", "stable"] {
9634            let c = caixa_with_versao(bad);
9635            let err = c.validate_versao().unwrap_err();
9636            assert!(
9637                matches!(
9638                    err,
9639                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
9640                ),
9641                "got {err:?} for {bad:?}"
9642            );
9643        }
9644    }
9645
9646    #[test]
9647    fn validate_versao_rejects_four_part_form() {
9648        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
9649        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
9650        // semver crate rejects the extra `.0` at parse time.
9651        let c = caixa_with_versao("0.1.0.0");
9652        let err = c.validate_versao().unwrap_err();
9653        assert!(
9654            matches!(
9655                err,
9656                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
9657            ),
9658            "got {err:?}"
9659        );
9660    }
9661
9662    #[test]
9663    fn versao_empty_takes_precedence_over_invalid() {
9664        // Order pin: the empty arm fires before the parser is consulted.
9665        // Empty < invalid in self-locating-ness — the narrower
9666        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
9667        // reference into the parser-shaped reason. Mirrors
9668        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
9669        // peer axis.
9670        let c = caixa_with_versao("");
9671        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
9672    }
9673
9674    #[test]
9675    fn versao_invalid_diagnostic_carries_offending_versao() {
9676        // Diagnostic-shape pin: the error names the offending `:versao`
9677        // verbatim with a non-empty parser-shaped reason, so a `feira
9678        // lint` run can render the diagnostic without re-parsing.
9679        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
9680        let c = caixa_with_versao("v0.1.0");
9681        let err = c.validate_versao().unwrap_err();
9682        let ManifestError::VersaoInvalid { versao, reason } = err else {
9683            panic!("expected VersaoInvalid variant");
9684        };
9685        assert_eq!(versao, "v0.1.0");
9686        assert!(
9687            !reason.is_empty(),
9688            "VersaoInvalid `reason` must carry the parser's wording verbatim"
9689        );
9690    }
9691
9692    #[test]
9693    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
9694        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
9695        // for `:upgrade-from :from` must also pass `validate_versao` —
9696        // the two `:versao`-typed surfaces (top-level `:versao`,
9697        // `:upgrade-from :from`) consume the *same* `semver::Version`
9698        // parser, so they must agree on the accepted set. Without this
9699        // pin, a future tightening of one axis could silently diverge
9700        // from the other. Mirrors the `:versao` requirement-axis
9701        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
9702        // commits established.
9703        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
9704            // From the canonical UpgradeFromEntry round-trip fixture
9705            // (`upgrade::tests::round_trip_load_module` peers).
9706            let entry = crate::UpgradeFromEntry {
9707                from: versao.to_string(),
9708                instructions: Vec::new(),
9709            };
9710            entry
9711                .validate()
9712                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
9713            caixa_with_versao(versao)
9714                .validate_versao()
9715                .unwrap_or_else(|e| {
9716                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
9717                });
9718        }
9719    }
9720
9721    // ── Caixa::validate_restart_window — supervisor restart-window
9722    //    folds through the shared `supervisor::duration_codec` ────────
9723
9724    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
9725        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
9726        c.kind = CaixaKind::Supervisor;
9727        c.restart_window = window.map(str::to_string);
9728        c
9729    }
9730
9731    #[test]
9732    fn validate_restart_window_accepts_none() {
9733        // The canonical "omit the slot to express no reset" shape — a
9734        // `None` raw string is the absence of the typed
9735        // `:restart-window` slot, which is exactly the SupervisorSpec
9736        // "never reset" semantics. The gate must be a no-op here; a
9737        // future tightening that rejected `None` would force every
9738        // supervisor caixa to authoring-time pin a window even when
9739        // the OTP semantics call for none.
9740        caixa_with_restart_window(None)
9741            .validate_restart_window()
9742            .unwrap();
9743    }
9744
9745    #[test]
9746    fn validate_restart_window_accepts_canonical_forms() {
9747        // Positive-set sweep across the canonical authoring units the
9748        // shared `supervisor::duration_codec::parse` accepts —
9749        // matches the codec-side `parse_accepts_integer_canonical_units`
9750        // pin in supervisor::tests so a future codec-side tightening
9751        // surfaces simultaneously on both axes.
9752        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
9753            caixa_with_restart_window(Some(window))
9754                .validate_restart_window()
9755                .unwrap_or_else(|e| {
9756                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
9757                });
9758        }
9759    }
9760
9761    #[test]
9762    fn validate_restart_window_rejects_fractional_seconds() {
9763        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
9764        // as f64 to 1.5 → renders back as `"1500ms"` on first
9765        // serialize). Prior to the fold + this gate, the inline
9766        // `parse_window_inline` accepted f64 magnitudes and silently
9767        // produced a `Duration::from_secs_f64(1.5)`, divergent from
9768        // the shared codec's integer-magnitude discipline on the
9769        // serde-routed siblings. The gate now surfaces a self-locating
9770        // diagnostic at the manifest layer.
9771        let err = caixa_with_restart_window(Some("1.5s"))
9772            .validate_restart_window()
9773            .unwrap_err();
9774        let ManifestError::RestartWindowMalformed {
9775            restart_window,
9776            reason,
9777        } = err
9778        else {
9779            panic!("expected RestartWindowMalformed for fractional seconds");
9780        };
9781        assert_eq!(restart_window, "1.5s");
9782        assert!(
9783            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
9784            "diagnostic must carry shared-codec wording, got {reason:?}"
9785        );
9786    }
9787
9788    #[test]
9789    fn validate_restart_window_rejects_decimal_shaped_integer() {
9790        // The `"1.0s"` class — numerically `1s` exactly, but the
9791        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
9792        // gets the same canonical-form diagnostic.
9793        let err = caixa_with_restart_window(Some("1.0s"))
9794            .validate_restart_window()
9795            .unwrap_err();
9796        assert!(
9797            matches!(
9798                err,
9799                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9800                    if restart_window == "1.0s"
9801            ),
9802            "got {err:?}"
9803        );
9804    }
9805
9806    #[test]
9807    fn validate_restart_window_rejects_half_unit_minute() {
9808        // `"0.5m"` is the unit-fraction footgun — author writes a
9809        // human-readable half-minute, the prior inline parser silently
9810        // produced `Duration::from_secs_f64(30.0)` and serde
9811        // re-emitted as `"30s"`, rewriting author intent. The gate
9812        // closes the loop at the manifest layer.
9813        let err = caixa_with_restart_window(Some("0.5m"))
9814            .validate_restart_window()
9815            .unwrap_err();
9816        let ManifestError::RestartWindowMalformed {
9817            restart_window,
9818            reason,
9819        } = err
9820        else {
9821            panic!("expected RestartWindowMalformed");
9822        };
9823        assert_eq!(restart_window, "0.5m");
9824        assert!(
9825            reason.contains("\"30s\""),
9826            "diagnostic must point at the canonical-form remediation, got {reason:?}"
9827        );
9828    }
9829
9830    #[test]
9831    fn validate_restart_window_rejects_leading_sign() {
9832        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
9833        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
9834        // and was caught by the `num < 0.0` arm which silently
9835        // returned `None`, dropping the author-supplied window). The
9836        // shared codec's digit-only gate rejects both with a unified
9837        // canonical-form diagnostic; the manifest-layer wrapper names
9838        // the offending value.
9839        for bad in ["+30s", "-30s"] {
9840            let err = caixa_with_restart_window(Some(bad))
9841                .validate_restart_window()
9842                .unwrap_err();
9843            assert!(
9844                matches!(
9845                    err,
9846                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
9847                        if restart_window == bad
9848                ),
9849                "got {err:?} for {bad:?}"
9850            );
9851        }
9852    }
9853
9854    #[test]
9855    fn validate_restart_window_rejects_unknown_unit() {
9856        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
9857        // unit dispatch surfaces an `unknown duration unit` reason;
9858        // the manifest-layer wrapper names the offending value.
9859        let err = caixa_with_restart_window(Some("30x"))
9860            .validate_restart_window()
9861            .unwrap_err();
9862        let ManifestError::RestartWindowMalformed {
9863            restart_window,
9864            reason,
9865        } = err
9866        else {
9867            panic!("expected RestartWindowMalformed for unknown unit");
9868        };
9869        assert_eq!(restart_window, "30x");
9870        assert!(
9871            reason.contains("unknown duration unit"),
9872            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
9873        );
9874    }
9875
9876    #[test]
9877    fn validate_restart_window_rejects_garbage() {
9878        // Pure non-numeric magnitude (`"abc"`) falls through to the
9879        // shared codec's narrower `"bad duration magnitude"` arm. Same
9880        // diagnostic shape as the codec-side
9881        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
9882        let err = caixa_with_restart_window(Some("abc"))
9883            .validate_restart_window()
9884            .unwrap_err();
9885        let ManifestError::RestartWindowMalformed {
9886            restart_window,
9887            reason,
9888        } = err
9889        else {
9890            panic!("expected RestartWindowMalformed for garbage");
9891        };
9892        assert_eq!(restart_window, "abc");
9893        assert!(
9894            reason.contains("bad duration magnitude"),
9895            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
9896        );
9897    }
9898
9899    #[test]
9900    fn validate_restart_window_rejects_empty_string() {
9901        // The empty-after-trim edge case — distinct from the `None`
9902        // canonical "omit the slot" shape. The shared codec's
9903        // digit-only gate refuses an empty magnitude; the manifest
9904        // layer names the offending `""` so the author can grep for
9905        // the literal empty value in their `caixa.lisp` and either
9906        // remove the slot (the canonical "no reset" shape) or pin a
9907        // positive duration.
9908        let err = caixa_with_restart_window(Some(""))
9909            .validate_restart_window()
9910            .unwrap_err();
9911        assert!(
9912            matches!(
9913                err,
9914                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9915                    if restart_window.is_empty()
9916            ),
9917            "got {err:?}"
9918        );
9919    }
9920
9921    #[test]
9922    fn validate_restart_window_diagnostic_carries_offending_value() {
9923        // Diagnostic-shape pin (peer with
9924        // `nome_invalid_diagnostic_carries_offending_nome` /
9925        // `versao_invalid_diagnostic_carries_offending_versao`): the
9926        // error names the offending raw `:restart-window` verbatim
9927        // with a non-empty shared-codec-shaped reason, so a `feira
9928        // lint` run can render the diagnostic without re-parsing.
9929        let err = caixa_with_restart_window(Some("1.5s"))
9930            .validate_restart_window()
9931            .unwrap_err();
9932        let ManifestError::RestartWindowMalformed {
9933            restart_window,
9934            reason,
9935        } = err
9936        else {
9937            panic!("expected RestartWindowMalformed variant");
9938        };
9939        assert_eq!(restart_window, "1.5s");
9940        assert!(
9941            !reason.is_empty(),
9942            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
9943        );
9944    }
9945
9946    #[test]
9947    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
9948        // Behavioral parity pin after the fold (`parse_window_inline`
9949        // deletion): the canonical `"60s"` still produces
9950        // `Duration::from_secs(60)` on the typed view — the fold is
9951        // semantically equivalent to the prior inline parser on the
9952        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
9953        // pin, narrowed to the parser-side contract.
9954        let c = caixa_with_restart_window(Some("60s"));
9955        let view = c.supervisor_view().expect("Supervisor kind has a view");
9956        assert_eq!(
9957            view.restart_window,
9958            Some(std::time::Duration::from_secs(60))
9959        );
9960    }
9961
9962    #[test]
9963    fn supervisor_view_soft_swallows_what_validate_rejects() {
9964        // Parity pin between the view-construction path and the
9965        // manifest-level validator: the same `"1.5s"` that surfaces
9966        // `RestartWindowMalformed` at `validate_restart_window` time
9967        // becomes `restart_window: None` on the typed view (the fold
9968        // preserves the existing best-effort shape of `supervisor_view`).
9969        // The contract is: a layout-verifier / `feira lint` flow that
9970        // cares about the malformed-window axis MUST consult
9971        // `validate_restart_window` — relying solely on the view's
9972        // `None` swallows the diagnostic silently. This pin makes the
9973        // expectation a typed invariant.
9974        let c = caixa_with_restart_window(Some("1.5s"));
9975        let view = c.supervisor_view().expect("Supervisor kind has a view");
9976        assert_eq!(
9977            view.restart_window, None,
9978            "view-construction path soft-swallows the parse error to None"
9979        );
9980        // And the manifest-level validator does NOT soft-swallow:
9981        assert!(
9982            matches!(
9983                c.validate_restart_window().unwrap_err(),
9984                ManifestError::RestartWindowMalformed { ref restart_window, .. }
9985                    if restart_window == "1.5s"
9986            ),
9987            "validator must surface the offending value",
9988        );
9989    }
9990
9991    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
9992
9993    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
9994        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9995        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
9996        c.exe = exe.into_iter().map(String::from).collect();
9997        c.servicos = servicos.into_iter().map(String::from).collect();
9998        c
9999    }
10000
10001    #[test]
10002    fn validate_code_paths_accepts_canonical_template() {
10003        // The bare `Caixa::template` shape is the gate's identity element
10004        // on the canonical authoring shape — `:bibliotecas
10005        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
10006        // that the gate is non-disruptive against every existing caixa.
10007        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10008        c.validate_code_paths().unwrap();
10009    }
10010
10011    #[test]
10012    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
10013        // Positive control sweep: a canonical-shaped path on every slot
10014        // passes. Mirrors the peer
10015        // `behavior::validate_every_slot_relative_is_ok` pin.
10016        let c = caixa_with_code_paths(
10017            vec!["lib/demo.lisp", "lib/helpers.lisp"],
10018            vec!["exe/demo", "exe/tool"],
10019            vec!["servicos/demo.computeunit.yaml"],
10020        );
10021        c.validate_code_paths().unwrap();
10022    }
10023
10024    #[test]
10025    fn validate_code_paths_accepts_all_empty_lists() {
10026        // The empty-list identity element: every Caixa with no declared
10027        // code paths trivially passes (Supervisor / Aplicacao kinds rely
10028        // on this — the OwnCode gate already rejected them before the
10029        // path-shape gate runs in the layout, but the validator itself
10030        // must accept the empty shape).
10031        let c = caixa_with_code_paths(vec![], vec![], vec![]);
10032        c.validate_code_paths().unwrap();
10033    }
10034
10035    #[test]
10036    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
10037        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10038        let err = c.validate_code_paths().unwrap_err();
10039        assert!(
10040            matches!(
10041                err,
10042                ManifestError::CodePathEmpty {
10043                    slot: ":bibliotecas"
10044                }
10045            ),
10046            "got {err:?}",
10047        );
10048    }
10049
10050    #[test]
10051    fn validate_code_paths_rejects_empty_exe_entry() {
10052        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
10053        let err = c.validate_code_paths().unwrap_err();
10054        assert!(
10055            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
10056            "got {err:?}",
10057        );
10058    }
10059
10060    #[test]
10061    fn validate_code_paths_rejects_empty_servicos_entry() {
10062        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
10063        let err = c.validate_code_paths().unwrap_err();
10064        assert!(
10065            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
10066            "got {err:?}",
10067        );
10068    }
10069
10070    #[test]
10071    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
10072        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
10073        // so an absolute path that resolves on disk silently passes the
10074        // layout's existence check — the canonical sandbox-escape on
10075        // the biblioteca axis.
10076        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10077        let err = c.validate_code_paths().unwrap_err();
10078        let ManifestError::CodePathAbsolute { slot, path } = err else {
10079            panic!("expected CodePathAbsolute, got {err:?}");
10080        };
10081        assert_eq!(slot, ":bibliotecas");
10082        assert_eq!(path, PathBuf::from("/etc/passwd"));
10083    }
10084
10085    #[test]
10086    fn validate_code_paths_rejects_absolute_exe_entry() {
10087        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
10088        let err = c.validate_code_paths().unwrap_err();
10089        let ManifestError::CodePathAbsolute { slot, path } = err else {
10090            panic!("expected CodePathAbsolute, got {err:?}");
10091        };
10092        assert_eq!(slot, ":exe");
10093        assert_eq!(path, PathBuf::from("/usr/bin/env"));
10094    }
10095
10096    #[test]
10097    fn validate_code_paths_rejects_absolute_servicos_entry() {
10098        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
10099        let err = c.validate_code_paths().unwrap_err();
10100        let ManifestError::CodePathAbsolute { slot, path } = err else {
10101            panic!("expected CodePathAbsolute, got {err:?}");
10102        };
10103        assert_eq!(slot, ":servicos");
10104        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
10105    }
10106
10107    #[test]
10108    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
10109        // Canonical "I want a lib from a sibling caixa" footgun on the
10110        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
10111        // downstream, so a leading `..` traverses to the parent of the
10112        // caixa root with no diagnostic at layout time if the resolved
10113        // target exists.
10114        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
10115        let err = c.validate_code_paths().unwrap_err();
10116        let ManifestError::CodePathParentEscape { slot, path } = err else {
10117            panic!("expected CodePathParentEscape, got {err:?}");
10118        };
10119        assert_eq!(slot, ":bibliotecas");
10120        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
10121    }
10122
10123    #[test]
10124    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
10125        // Mid-path `..` defeats the layout's component-aware
10126        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
10127        // `starts_with(<root>/exe)` is true, but the canonical resolution
10128        // lives outside the caixa root. Caught regardless of where the
10129        // `..` sits — mirrors the peer
10130        // `behavior::validate_rejects_parent_escape_mid_path` pin.
10131        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
10132        let err = c.validate_code_paths().unwrap_err();
10133        let ManifestError::CodePathParentEscape { slot, path } = err else {
10134            panic!("expected CodePathParentEscape, got {err:?}");
10135        };
10136        assert_eq!(slot, ":exe");
10137        assert_eq!(path, PathBuf::from("exe/../../escape"));
10138    }
10139
10140    #[test]
10141    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
10142        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
10143        let err = c.validate_code_paths().unwrap_err();
10144        let ManifestError::CodePathParentEscape { slot, path } = err else {
10145            panic!("expected CodePathParentEscape, got {err:?}");
10146        };
10147        assert_eq!(slot, ":servicos");
10148        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
10149    }
10150
10151    #[test]
10152    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
10153        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
10154        // `:servicos`. A manifest with malformed entries on all three
10155        // surfaces surfaces the `:bibliotecas` defect first, mirroring
10156        // the canonical declaration order
10157        // `Caixa::declared_foreign_code_slots` already establishes for
10158        // the foreign-code-slot diagnostic.
10159        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
10160        let err = c.validate_code_paths().unwrap_err();
10161        assert!(
10162            matches!(
10163                err,
10164                ManifestError::CodePathEmpty {
10165                    slot: ":bibliotecas"
10166                }
10167            ),
10168            "got {err:?}",
10169        );
10170    }
10171
10172    #[test]
10173    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
10174        // Within-slot precedence pin: empty → absolute → parent-escape,
10175        // matching the [`PathShapeViolation`] arm-ordering every peer
10176        // `is_sandboxed_relative_path` caller follows (b0c8389
10177        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
10178        // `:bibliotecas` list whose first entry is empty *and* whose
10179        // later entries are absolute/parent-escape surfaces the empty
10180        // arm first, on the lexicographically-earliest offending entry.
10181        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
10182        let err = c.validate_code_paths().unwrap_err();
10183        assert!(
10184            matches!(
10185                err,
10186                ManifestError::CodePathEmpty {
10187                    slot: ":bibliotecas"
10188                }
10189            ),
10190            "got {err:?}",
10191        );
10192    }
10193
10194    #[test]
10195    fn validate_code_paths_first_offender_per_slot_wins() {
10196        // Within a single slot, the first declaration-order offender
10197        // surfaces — pins that the gate is left-to-right deterministic
10198        // (peer of every `*_first_collision_*` pin on duplicate gates).
10199        let c = caixa_with_code_paths(
10200            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
10201            vec![],
10202            vec![],
10203        );
10204        let err = c.validate_code_paths().unwrap_err();
10205        let ManifestError::CodePathAbsolute { slot, path } = err else {
10206            panic!("expected CodePathAbsolute, got {err:?}");
10207        };
10208        assert_eq!(slot, ":bibliotecas");
10209        assert_eq!(path, PathBuf::from("/etc/escape"));
10210    }
10211
10212    #[test]
10213    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
10214        // Diagnostic-shape pin (peer with
10215        // `nome_invalid_diagnostic_carries_offending_nome` /
10216        // `versao_invalid_diagnostic_carries_offending_versao`): the
10217        // error's Display surfaces both the offending `:slot` tag and
10218        // the offending path verbatim, so a `feira lint` run can render
10219        // the diagnostic without re-parsing.
10220        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10221        let rendered = c.validate_code_paths().unwrap_err().to_string();
10222        assert!(
10223            rendered.contains(":bibliotecas"),
10224            "diagnostic must name the offending slot: {rendered}",
10225        );
10226        assert!(
10227            rendered.contains("/etc/passwd"),
10228            "diagnostic must quote the offending path: {rendered}",
10229        );
10230    }
10231
10232    #[test]
10233    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
10234        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
10235        // axis. Without the gate `feira build` re-parses the same lib
10236        // twice, wasting work and silently masking the author's intent
10237        // to declare a *second* biblioteca.
10238        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
10239        let err = c.validate_code_paths().unwrap_err();
10240        let ManifestError::CodePathDuplicate { slot, path } = err else {
10241            panic!("expected CodePathDuplicate, got {err:?}");
10242        };
10243        assert_eq!(slot, ":bibliotecas");
10244        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
10245    }
10246
10247    #[test]
10248    fn validate_code_paths_rejects_duplicate_exe_entry() {
10249        // Same footgun on the Binario surface. The future `caixa-flake`
10250        // emitter that materializes each `:exe` entry as a flake
10251        // `packages.<name>` derivation would collide on the duplicate
10252        // package key — surfaced here at the typed-validate layer with a
10253        // self-locating diagnostic instead.
10254        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
10255        let err = c.validate_code_paths().unwrap_err();
10256        let ManifestError::CodePathDuplicate { slot, path } = err else {
10257            panic!("expected CodePathDuplicate, got {err:?}");
10258        };
10259        assert_eq!(slot, ":exe");
10260        assert_eq!(path, PathBuf::from("exe/cli"));
10261    }
10262
10263    #[test]
10264    fn validate_code_paths_rejects_duplicate_servicos_entry() {
10265        // Same footgun on the Servico surface. The peer caixa-helm /
10266        // caixa-flux renderers refuse `:servicos.len() != 1` with the
10267        // narrower `UnsupportedServicoCount` diagnostic, but that
10268        // diagnostic surfaces "too many servicos" without naming
10269        // "duplicate entry" — the typed self-locating framing only lands
10270        // at this gate.
10271        let c = caixa_with_code_paths(
10272            vec![],
10273            vec![],
10274            vec![
10275                "servicos/demo.computeunit.yaml",
10276                "servicos/demo.computeunit.yaml",
10277            ],
10278        );
10279        let err = c.validate_code_paths().unwrap_err();
10280        let ManifestError::CodePathDuplicate { slot, path } = err else {
10281            panic!("expected CodePathDuplicate, got {err:?}");
10282        };
10283        assert_eq!(slot, ":servicos");
10284        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
10285    }
10286
10287    #[test]
10288    fn validate_code_paths_accepts_same_path_across_slots() {
10289        // Per-list scope pin: a `:bibliotecas` entry that happens to
10290        // collide with an `:exe` or `:servicos` entry as a *string* is
10291        // not a duplicate by this gate (each list gets its own HashSet),
10292        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
10293        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
10294        // shape on the dep axis). The structural `starts_with(<exe |
10295        // servicos>_dir)` fence at layout time prevents the realistic
10296        // cross-slot collision case from existing on disk, but the gate's
10297        // per-list scope is correct independent of that downstream fence.
10298        let c = caixa_with_code_paths(
10299            vec!["lib/x.lisp"],
10300            vec!["exe/x"],
10301            vec!["servicos/x.computeunit.yaml"],
10302        );
10303        c.validate_code_paths().unwrap();
10304    }
10305
10306    #[test]
10307    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
10308        // Within-slot ordering pin: structural defects (empty / absolute
10309        // / parent-escape) fire before the duplicate gate on the same
10310        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
10311        // surfaces the narrower `CodePathEmpty` for the empty entry
10312        // first, not the duplicate on the later pair — same arm-ordering
10313        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
10314        // `:autores` 86c769b, `:deps` 359fba5).
10315        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
10316        let err = c.validate_code_paths().unwrap_err();
10317        assert!(
10318            matches!(
10319                err,
10320                ManifestError::CodePathEmpty {
10321                    slot: ":bibliotecas"
10322                }
10323            ),
10324            "got {err:?}",
10325        );
10326    }
10327
10328    #[test]
10329    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
10330        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
10331        // duplicates surface before `:exe` duplicates, matching the
10332        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
10333        // order every peer per-slot diagnostic on this surface follows.
10334        let c = caixa_with_code_paths(
10335            vec!["lib/x.lisp", "lib/x.lisp"],
10336            vec!["exe/y", "exe/y"],
10337            vec![],
10338        );
10339        let err = c.validate_code_paths().unwrap_err();
10340        let ManifestError::CodePathDuplicate { slot, path } = err else {
10341            panic!("expected CodePathDuplicate, got {err:?}");
10342        };
10343        assert_eq!(slot, ":bibliotecas");
10344        assert_eq!(path, PathBuf::from("lib/x.lisp"));
10345    }
10346
10347    #[test]
10348    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
10349        // Diagnostic-shape pin (peer with
10350        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10351        // on the structural arm): the duplicate-arm Display surfaces both
10352        // the offending `:slot` tag and the offending path verbatim, so a
10353        // `feira lint` run can render the diagnostic without re-parsing.
10354        let c = caixa_with_code_paths(
10355            vec![],
10356            vec![],
10357            vec![
10358                "servicos/demo.computeunit.yaml",
10359                "servicos/demo.computeunit.yaml",
10360            ],
10361        );
10362        let rendered = c.validate_code_paths().unwrap_err().to_string();
10363        assert!(
10364            rendered.contains(":servicos"),
10365            "diagnostic must name the offending slot: {rendered}",
10366        );
10367        assert!(
10368            rendered.contains("servicos/demo.computeunit.yaml"),
10369            "diagnostic must quote the offending path: {rendered}",
10370        );
10371    }
10372
10373    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
10374    //
10375    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
10376    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
10377    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
10378    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
10379    // at parse time — the same downstream consumer the peer `:behavior
10380    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
10381    // `:upgrade-from :state-change :script` (33cc830,
10382    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
10383    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
10384    // nix-built executable surface (`"exe/<name>"` shape per the canonical
10385    // [`crate::LayoutError::ExeOutsideDir`] error message and every
10386    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
10387    // is the `.computeunit.yaml` ComputeUnit-CR axis.
10388
10389    #[test]
10390    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
10391        // Canonical "I dragged the wrong file from the workspace tree"
10392        // footgun on the biblioteca axis. Without the gate `feira build`
10393        // hands the extensionless path to `tatara_lisp::read` and fails
10394        // with a parser-shaped diagnostic far from the source caixa.lisp,
10395        // with no field naming the offending `:bibliotecas` entry.
10396        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
10397            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10398            let err = c.validate_code_paths().unwrap_err();
10399            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10400                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10401            };
10402            assert_eq!(slot, ":bibliotecas");
10403            assert_eq!(path, PathBuf::from(relpath));
10404        }
10405    }
10406
10407    #[test]
10408    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
10409        // Wrong-extension sweep across common authoring footguns. Same
10410        // sweep posture as the peer
10411        // `behavior::validate_rejects_wrong_extension` (c97815a) and
10412        // `upgrade::tests::state_change_rejects_wrong_extension_script`
10413        // (33cc830) cases.
10414        for relpath in [
10415            "lib/demo.rs",
10416            "lib/demo.txt",
10417            "lib/demo.md",
10418            "lib/demo.json",
10419            "lib/demo.yaml",
10420            "lib/demo.toml",
10421            "lib/demo.lisp.bak",
10422            "lib/demo.lispx",
10423            "lib/demo.lis",
10424        ] {
10425            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10426            let err = c.validate_code_paths().unwrap_err();
10427            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10428                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10429            };
10430            assert_eq!(slot, ":bibliotecas");
10431            assert_eq!(path, PathBuf::from(relpath));
10432        }
10433    }
10434
10435    #[test]
10436    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
10437        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
10438        // contract. An uppercase `.LISP` shape that the layout's existence
10439        // check would (case-insensitively, on case-insensitive volumes)
10440        // match the on-disk file still mismatches the canonical form the
10441        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
10442        // contract. Mirrors the peer
10443        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
10444        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
10445        // (33cc830) sweeps.
10446        for relpath in [
10447            "lib/demo.LISP",
10448            "lib/demo.Lisp",
10449            "lib/demo.LiSp",
10450            "lib/demo.lISP",
10451        ] {
10452            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10453            let err = c.validate_code_paths().unwrap_err();
10454            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10455                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10456            };
10457            assert_eq!(slot, ":bibliotecas");
10458            assert_eq!(path, PathBuf::from(relpath));
10459        }
10460    }
10461
10462    #[test]
10463    fn validate_code_paths_accepts_canonical_lisp_shapes() {
10464        // Positive-control sweep through every canonical authoring shape
10465        // every in-tree fixture and the `Caixa::template` scaffold use.
10466        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
10467        // (c97815a) and the lifted predicate's own
10468        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
10469        // (33cc830).
10470        for relpath in [
10471            "lib/demo.lisp",
10472            "lib/handlers.lisp",
10473            "lib/migrations/v01-to-v02.lisp",
10474            "demo.lisp",
10475            "a.lisp",
10476            "./lib/demo.lisp",
10477            "lib/./handlers.lisp",
10478            "lib/migrations/v.0.1.lisp",
10479        ] {
10480            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10481            c.validate_code_paths()
10482                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10483        }
10484    }
10485
10486    #[test]
10487    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
10488        // The file-type gate is per-slot — only `:bibliotecas` carries the
10489        // tatara-lisp-source contract. An extensionless `:exe` entry
10490        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
10491        // canonical shapes every in-tree fixture uses, and must continue
10492        // to pass validate. Pins that a future tightening that broadens
10493        // the `.lisp` gate to either axis surfaces as a test failure
10494        // rather than as a silent breaking change to existing valid
10495        // manifests.
10496        let c = caixa_with_code_paths(
10497            vec![],
10498            vec!["exe/demo", "exe/tool"],
10499            vec!["servicos/demo.computeunit.yaml"],
10500        );
10501        c.validate_code_paths().unwrap();
10502    }
10503
10504    #[test]
10505    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
10506        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
10507        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
10508        // sandbox-shape diagnostic first (the `.lisp` remediation would
10509        // be misleading when the offending path can never resolve under
10510        // the caixa root anyway). Mirrors the peer
10511        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
10512        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
10513        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
10514        // on `:upgrade-from :state-change :script` (33cc830).
10515        //
10516        // Empty wins (the strictly-smaller-scope structural arm).
10517        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10518        assert!(
10519            matches!(
10520                c.validate_code_paths().unwrap_err(),
10521                ManifestError::CodePathEmpty {
10522                    slot: ":bibliotecas"
10523                }
10524            ),
10525            "empty must win over non-lisp-extension",
10526        );
10527        // Absolute wins (the path can't resolve under the caixa root).
10528        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10529        let err = c.validate_code_paths().unwrap_err();
10530        let ManifestError::CodePathAbsolute { slot, .. } = err else {
10531            panic!("absolute must win over non-lisp-extension, got {err:?}");
10532        };
10533        assert_eq!(slot, ":bibliotecas");
10534        // ParentEscape wins (the path escapes the caixa root).
10535        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
10536        let err = c.validate_code_paths().unwrap_err();
10537        let ManifestError::CodePathParentEscape { slot, .. } = err else {
10538            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
10539        };
10540        assert_eq!(slot, ":bibliotecas");
10541    }
10542
10543    #[test]
10544    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
10545        // Within-slot precedence pin: the per-entry file-type shape gate
10546        // fires before the cross-entry duplicate gate, so the narrower
10547        // structural defect dominates the uniqueness diagnostic. A
10548        // `("lib/x.txt" "lib/x.txt")` shape surfaces
10549        // `CodePathNonLispExtension` on the first entry rather than
10550        // `CodePathDuplicate` on the pair — same posture every per-entry
10551        // shape-gate-precedes-duplicate cascade follows on this surface
10552        // (the empty / absolute / parent-escape arms already precede the
10553        // duplicate arm; the lifted file-type arm joins that set).
10554        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
10555        let err = c.validate_code_paths().unwrap_err();
10556        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10557            panic!("expected CodePathNonLispExtension, got {err:?}");
10558        };
10559        assert_eq!(slot, ":bibliotecas");
10560        assert_eq!(path, PathBuf::from("lib/x.txt"));
10561    }
10562
10563    #[test]
10564    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
10565        // Diagnostic-shape pin (peer with
10566        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10567        // on the sandbox-shape arms and
10568        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
10569        // on the duplicate arm): the file-type-arm Display surfaces both
10570        // the offending `:slot` tag, the offending path verbatim, and the
10571        // expected `.lisp` extension named in the remediation text, so a
10572        // `feira lint` run can render the diagnostic without re-parsing.
10573        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
10574        let rendered = c.validate_code_paths().unwrap_err().to_string();
10575        assert!(
10576            rendered.contains(":bibliotecas"),
10577            "diagnostic must name the offending slot: {rendered}",
10578        );
10579        assert!(
10580            rendered.contains("lib/demo.rs"),
10581            "diagnostic must quote the offending path: {rendered}",
10582        );
10583        assert!(
10584            rendered.contains(".lisp"),
10585            "diagnostic must name the expected extension: {rendered}",
10586        );
10587    }
10588
10589    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
10590    //
10591    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
10592    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
10593    // contract. The peer caixa-helm / caixa-flux renderers consume each
10594    // `:servicos` entry through `serde_yaml::from_str` as a typed
10595    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
10596    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
10597    // axis `Path::extension` can't express on its own.
10598
10599    #[test]
10600    fn validate_code_paths_rejects_no_extension_servicos_entry() {
10601        // Canonical "I dragged the wrong file from the workspace tree"
10602        // footgun on the Servico axis. Without the gate the peer
10603        // caixa-helm / caixa-flux renderers hand the extensionless path
10604        // to `serde_yaml::from_str` and fail with a parser-shaped
10605        // diagnostic far from the source caixa.lisp, with no field
10606        // naming the offending `:servicos` entry.
10607        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
10608            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10609            let err = c.validate_code_paths().unwrap_err();
10610            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10611                panic!(
10612                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10613                     got {err:?}"
10614                );
10615            };
10616            assert_eq!(slot, ":servicos");
10617            assert_eq!(path, PathBuf::from(relpath));
10618        }
10619    }
10620
10621    #[test]
10622    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
10623        // Wrong-extension sweep across common authoring footguns on the
10624        // Servico axis. Bare `.yaml` is the canonical "I forgot the
10625        // `.computeunit` segment" typo; the off-by-one-segment shapes
10626        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
10627        // bare `Path::extension` view but mismatch the typed compound
10628        // suffix the renderers' `serde_yaml::from_str` consumer demands.
10629        // Same sweep-posture as the peer
10630        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
10631        // (64772a9) on the sibling tatara-lisp-source axis.
10632        for relpath in [
10633            "servicos/demo.yaml",
10634            "servicos/demo.yml",
10635            "servicos/demo.json",
10636            "servicos/demo.toml",
10637            "servicos/demo.txt",
10638            "servicos/demo.computeunit.yaml.bak",
10639            "servicos/demo.computeunit.yam",
10640            "servicos/demo.computeunit",
10641            "servicos/demo-computeunit.yaml",
10642            "servicos/demo_computeunit.yaml",
10643        ] {
10644            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10645            let err = c.validate_code_paths().unwrap_err();
10646            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10647                panic!(
10648                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10649                     got {err:?}"
10650                );
10651            };
10652            assert_eq!(slot, ":servicos");
10653            assert_eq!(path, PathBuf::from(relpath));
10654        }
10655    }
10656
10657    #[test]
10658    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
10659        // Case-sensitivity sweep — pins the strict lowercase
10660        // `.computeunit.yaml` contract. A case-folded shape that the
10661        // layout's existence check would (case-insensitively, on
10662        // case-insensitive volumes) match the on-disk file still
10663        // mismatches the canonical form the codec emits, breaking the
10664        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
10665        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
10666        // (64772a9) sweep on the sibling tatara-lisp-source axis.
10667        for relpath in [
10668            "servicos/demo.ComputeUnit.yaml",
10669            "servicos/demo.COMPUTEUNIT.yaml",
10670            "servicos/demo.computeunit.YAML",
10671            "servicos/demo.computeunit.Yaml",
10672            "servicos/demo.COMPUTEUNIT.YAML",
10673        ] {
10674            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10675            let err = c.validate_code_paths().unwrap_err();
10676            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10677                panic!(
10678                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10679                     got {err:?}"
10680                );
10681            };
10682            assert_eq!(slot, ":servicos");
10683            assert_eq!(path, PathBuf::from(relpath));
10684        }
10685    }
10686
10687    #[test]
10688    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
10689        // Degenerate hidden-file shape: a file name exactly equal to the
10690        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
10691        // the structural "Servico declared with no identity" footgun.
10692        // The substrate identifies each ComputeUnit by the file-stem
10693        // segment that precedes `.computeunit.yaml` (the rendered
10694        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
10695        // the M3 `:contratos` membership lookup), so an empty stem
10696        // leaves the Servico unidentifiable. Pinned at the typed-axis
10697        // level so a future regression that drops the `name.len() >
10698        // SUFFIX.len()` bound at the predicate surfaces here, not
10699        // piecemeal as a `lareira-` chart-name collision at render time.
10700        for relpath in ["servicos/.computeunit.yaml"] {
10701            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10702            let err = c.validate_code_paths().unwrap_err();
10703            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10704                panic!(
10705                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10706                     got {err:?}"
10707                );
10708            };
10709            assert_eq!(slot, ":servicos");
10710            assert_eq!(path, PathBuf::from(relpath));
10711        }
10712    }
10713
10714    #[test]
10715    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
10716        // Positive-control sweep through every canonical authoring shape
10717        // every in-tree fixture and the `Caixa::template` scaffold use.
10718        // Mirrors the peer
10719        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
10720        // and the lifted predicate's own
10721        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
10722        // render.rs.
10723        for relpath in [
10724            "servicos/demo.computeunit.yaml",
10725            "servicos/hello-rio.computeunit.yaml",
10726            "servicos/my-service.computeunit.yaml",
10727            "servicos/a.computeunit.yaml",
10728            "./servicos/demo.computeunit.yaml",
10729            "servicos/./demo.computeunit.yaml",
10730            "servicos/sub/nested.computeunit.yaml",
10731            "servicos/v0.1.computeunit.yaml",
10732        ] {
10733            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10734            c.validate_code_paths()
10735                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10736        }
10737    }
10738
10739    #[test]
10740    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
10741        // The file-type gate is per-slot — only `:servicos` carries the
10742        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
10743        // entry and an extensionless `:exe` entry are the canonical
10744        // shapes every in-tree fixture uses, and must continue to pass
10745        // validate. Peer of
10746        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
10747        // (64772a9) — together pin that the typed
10748        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
10749        // cross-axis leakage in either direction.
10750        let c = caixa_with_code_paths(
10751            vec!["lib/demo.lisp"],
10752            vec!["exe/demo", "exe/tool"],
10753            vec!["servicos/demo.computeunit.yaml"],
10754        );
10755        c.validate_code_paths().unwrap();
10756    }
10757
10758    #[test]
10759    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
10760        // Cross-arm precedence pin: a `:servicos` entry that is *both*
10761        // sandbox-escaping and wrong-extension surfaces the more
10762        // fundamental sandbox-shape diagnostic first (the
10763        // `.computeunit.yaml` remediation would be misleading when the
10764        // offending path can never resolve under the caixa root
10765        // anyway). Mirrors the peer
10766        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
10767        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
10768        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
10769        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
10770        // table establishes.
10771        //
10772        // Empty wins (the strictly-smaller-scope structural arm).
10773        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
10774        assert!(
10775            matches!(
10776                c.validate_code_paths().unwrap_err(),
10777                ManifestError::CodePathEmpty { slot: ":servicos" }
10778            ),
10779            "empty must win over non-computeunit-yaml-extension",
10780        );
10781        // Absolute wins (the path can't resolve under the caixa root).
10782        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
10783        let err = c.validate_code_paths().unwrap_err();
10784        let ManifestError::CodePathAbsolute { slot, .. } = err else {
10785            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
10786        };
10787        assert_eq!(slot, ":servicos");
10788        // ParentEscape wins (the path escapes the caixa root).
10789        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
10790        let err = c.validate_code_paths().unwrap_err();
10791        let ManifestError::CodePathParentEscape { slot, .. } = err else {
10792            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
10793        };
10794        assert_eq!(slot, ":servicos");
10795    }
10796
10797    #[test]
10798    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
10799        // Within-slot precedence pin: the per-entry file-type shape gate
10800        // fires before the cross-entry duplicate gate, so the narrower
10801        // structural defect dominates the uniqueness diagnostic. A
10802        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
10803        // `CodePathNonComputeUnitYamlExtension` on the first entry
10804        // rather than `CodePathDuplicate` on the pair — same posture
10805        // every per-entry shape-gate-precedes-duplicate cascade follows
10806        // on this surface, peer of the 64772a9 `:bibliotecas`
10807        // `("lib/x.txt" "lib/x.txt")` ordering.
10808        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
10809        let err = c.validate_code_paths().unwrap_err();
10810        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10811            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
10812        };
10813        assert_eq!(slot, ":servicos");
10814        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
10815    }
10816
10817    #[test]
10818    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
10819     {
10820        // Diagnostic-shape pin (peer with
10821        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
10822        // on the sibling tatara-lisp-source axis): the file-type-arm
10823        // Display surfaces both the offending `:slot` tag, the
10824        // offending path verbatim, and the expected
10825        // `.computeunit.yaml` compound suffix named in the remediation
10826        // text, so a `feira lint` run can render the diagnostic without
10827        // re-parsing.
10828        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
10829        let rendered = c.validate_code_paths().unwrap_err().to_string();
10830        assert!(
10831            rendered.contains(":servicos"),
10832            "diagnostic must name the offending slot: {rendered}",
10833        );
10834        assert!(
10835            rendered.contains("servicos/demo.yaml"),
10836            "diagnostic must quote the offending path: {rendered}",
10837        );
10838        assert!(
10839            rendered.contains(".computeunit.yaml"),
10840            "diagnostic must name the expected compound suffix: {rendered}",
10841        );
10842    }
10843
10844    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
10845
10846    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
10847        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10848        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
10849        c
10850    }
10851
10852    #[test]
10853    fn validate_etiquetas_accepts_empty_list() {
10854        // The empty-list identity: every caixa with no declared tags
10855        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
10856        // so the gate is non-disruptive against every existing manifest.
10857        let c = caixa_with_etiquetas(vec![]);
10858        c.validate_etiquetas().unwrap();
10859    }
10860
10861    #[test]
10862    fn validate_etiquetas_accepts_canonical_forms() {
10863        // Positive control sweep: a canonical-shaped non-empty distinct
10864        // tag list passes, mirroring the example checkout-aplicacao
10865        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
10866        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
10867        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
10868        c.validate_etiquetas().unwrap();
10869    }
10870
10871    #[test]
10872    fn validate_etiquetas_rejects_empty_entry() {
10873        // Canonical paste-from-blank-doc footgun. Without the gate the
10874        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
10875        // no-op tag indexing nothing in the future caixa-registry.
10876        let c = caixa_with_etiquetas(vec![""]);
10877        let err = c.validate_etiquetas().unwrap_err();
10878        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10879    }
10880
10881    #[test]
10882    fn validate_etiquetas_rejects_duplicate_entry() {
10883        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
10884        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
10885        // collect at chart render — a "second wins / one silently
10886        // disappears" shape divergent from every peer typed-graph set
10887        // gate. The duplicate-arm names the offending tag verbatim.
10888        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
10889        let err = c.validate_etiquetas().unwrap_err();
10890        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
10891            panic!("expected EtiquetaDuplicate, got {err:?}");
10892        };
10893        assert_eq!(etiqueta, "demo");
10894    }
10895
10896    #[test]
10897    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
10898        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
10899        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
10900        // structural "this entry has no value" defect dominates the
10901        // cross-entry uniqueness diagnostic. Mirrors the peer
10902        // empty-before-duplicate cascades on `:caracteristicas`
10903        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
10904        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
10905        // `MembroDuplicate`).
10906        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
10907        let err = c.validate_etiquetas().unwrap_err();
10908        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
10909    }
10910
10911    #[test]
10912    fn validate_etiquetas_duplicate_reports_first_collision() {
10913        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
10914        // duplicate (the lexicographically-earliest offending position
10915        // — the second `"a"` at index 2 collides with the first `"a"`
10916        // at index 0), not the later `"b"` collision at index 3,
10917        // peer with every other first-collision diagnostic posture on
10918        // this surface (`validate_load_singularity_reports_first_collision`,
10919        // `validate_cleanup_singularity_reports_first_collision`).
10920        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
10921        let err = c.validate_etiquetas().unwrap_err();
10922        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
10923            panic!("expected EtiquetaDuplicate, got {err:?}");
10924        };
10925        assert_eq!(etiqueta, "a");
10926    }
10927
10928    #[test]
10929    fn validate_etiquetas_case_sensitive() {
10930        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
10931        // mirroring the peer `:membros :caixa` / `:children :caixa`
10932        // exact-string-match discipline. The shape gate this routine
10933        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
10934        // grammar) accepts mixed case — crates.io's keyword rule is
10935        // "case-insensitive" at the index layer but admits mixed case
10936        // at the entry layer (the canonical Helm chart `keywords:`
10937        // shape is lowercase by convention, but the grammar admits
10938        // uppercase). Case-sensitivity at the duplicate-set layer
10939        // remains structural — two distinct strings are two distinct
10940        // entries.
10941        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
10942        c.validate_etiquetas().unwrap();
10943    }
10944
10945    #[test]
10946    fn validate_etiquetas_diagnostic_carries_offending_tag() {
10947        // Diagnostic-shape pin (peer with
10948        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
10949        // the error's Display surfaces the offending tag verbatim, so a
10950        // `feira lint` run can render the diagnostic without re-parsing
10951        // and the author can grep their caixa.lisp for the offending
10952        // value.
10953        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
10954        let rendered = c.validate_etiquetas().unwrap_err().to_string();
10955        assert!(
10956            rendered.contains(":etiquetas"),
10957            "diagnostic must name the offending slot: {rendered}",
10958        );
10959        assert!(
10960            rendered.contains("demo"),
10961            "diagnostic must quote the offending tag: {rendered}",
10962        );
10963    }
10964
10965    #[test]
10966    fn validate_etiquetas_rejects_leading_whitespace_entry() {
10967        // Canonical paste-from-aligned-doc footgun. Without the shape
10968        // gate `" mesh"` silently passed validate and landed as a
10969        // YAML plain-style scalar with leading whitespace in the
10970        // rendered Chart.yaml `keywords:` array — every YAML 1.2
10971        // dumper trims leading whitespace from plain-style scalars,
10972        // so the authored space round-tripped inconsistently back
10973        // through `caixa.lisp`. Mirrors the peer
10974        // `validate_autores_rejects_leading_whitespace_entry`.
10975        let c = caixa_with_etiquetas(vec![" mesh"]);
10976        let err = c.validate_etiquetas().unwrap_err();
10977        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10978            panic!("expected EtiquetaInvalid, got {err:?}");
10979        };
10980        assert_eq!(etiqueta, " mesh");
10981        assert!(reason.contains("whitespace"), "got: {reason}");
10982    }
10983
10984    #[test]
10985    fn validate_etiquetas_rejects_embedded_newline_entry() {
10986        // Canonical paste-from-multiline-doc footgun — the author
10987        // pasted a multi-tag block into one `:etiquetas` entry
10988        // instead of splitting into one entry per tag. Without the
10989        // shape gate `"mesh\nhttp"` silently passed validate and
10990        // landed as a YAML-illegal multi-line scalar in the rendered
10991        // Chart.yaml `keywords:` array.
10992        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
10993        let err = c.validate_etiquetas().unwrap_err();
10994        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
10995            panic!("expected EtiquetaInvalid, got {err:?}");
10996        };
10997        assert_eq!(etiqueta, "mesh\nhttp");
10998        assert!(reason.contains("newline"), "got: {reason}");
10999    }
11000
11001    #[test]
11002    fn validate_etiquetas_rejects_embedded_comma_entry() {
11003        // Canonical CSV-list-separator-confusion footgun: the author
11004        // confused the CSV-style separator convention with the
11005        // `:etiquetas` list grammar. Without the shape gate
11006        // `"mesh,http,grpc"` silently passed validate and landed as a
11007        // single malformed search tag in the rendered Chart.yaml
11008        // `keywords:` array — Artifact Hub's keyword index would
11009        // either silently drop the tag or index it as
11010        // `mesh,http,grpc` instead of three separate tags.
11011        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
11012        let err = c.validate_etiquetas().unwrap_err();
11013        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11014            panic!("expected EtiquetaInvalid, got {err:?}");
11015        };
11016        assert_eq!(etiqueta, "mesh,http,grpc");
11017        assert!(reason.contains('`'), "got: {reason}");
11018        assert!(reason.contains(','), "got: {reason}");
11019    }
11020
11021    #[test]
11022    fn validate_etiquetas_rejects_embedded_slash_entry() {
11023        // Canonical path-separator-confusion footgun: the author
11024        // confused namespace-path notation with the keyword grammar.
11025        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
11026        let err = c.validate_etiquetas().unwrap_err();
11027        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11028            panic!("expected EtiquetaInvalid, got {err:?}");
11029        };
11030        assert_eq!(etiqueta, "caixa/servico");
11031        assert!(reason.contains('/'), "got: {reason}");
11032    }
11033
11034    #[test]
11035    fn validate_etiquetas_rejects_leading_digit_entry() {
11036        // Canonical paste-from-numbered-list footgun: the author
11037        // copied `1. mesh` from a numbered doc and the `1` leaked
11038        // into the tag.
11039        let c = caixa_with_etiquetas(vec!["1mesh"]);
11040        let err = c.validate_etiquetas().unwrap_err();
11041        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11042            panic!("expected EtiquetaInvalid, got {err:?}");
11043        };
11044        assert_eq!(etiqueta, "1mesh");
11045        assert!(reason.contains("digit"), "got: {reason}");
11046    }
11047
11048    #[test]
11049    fn validate_etiquetas_rejects_leading_hyphen_entry() {
11050        // Canonical kebab-leak footgun.
11051        let c = caixa_with_etiquetas(vec!["-foo"]);
11052        let err = c.validate_etiquetas().unwrap_err();
11053        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11054            panic!("expected EtiquetaInvalid, got {err:?}");
11055        };
11056        assert_eq!(etiqueta, "-foo");
11057        assert!(reason.contains('-'), "got: {reason}");
11058    }
11059
11060    #[test]
11061    fn validate_etiquetas_rejects_non_ascii_entry() {
11062        // Canonical paste-from-Unicode-doc footgun. Every legitimate
11063        // search tag is strict ASCII; raw non-ASCII silently
11064        // round-trips inconsistently across NFC/NFD normalization on
11065        // APFS / case-folding filesystems and breaks the Artifact Hub
11066        // keyword search index lookup.
11067        let c = caixa_with_etiquetas(vec!["café"]);
11068        let err = c.validate_etiquetas().unwrap_err();
11069        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11070            panic!("expected EtiquetaInvalid, got {err:?}");
11071        };
11072        assert_eq!(etiqueta, "café");
11073        assert!(reason.contains("non-ASCII"), "got: {reason}");
11074    }
11075
11076    #[test]
11077    fn validate_etiquetas_rejects_period_entry() {
11078        // Canonical namespace-confusion / version-suffix footgun
11079        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
11080        // excludes `.` from the continuation set even though the
11081        // sibling `:caracteristicas` axis (Cargo's feature-name
11082        // grammar) admits it. Tighter than the sibling axis, peer
11083        // with Cargo's own crates.io keyword shape.
11084        let c = caixa_with_etiquetas(vec!["http.1"]);
11085        let err = c.validate_etiquetas().unwrap_err();
11086        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11087            panic!("expected EtiquetaInvalid, got {err:?}");
11088        };
11089        assert_eq!(etiqueta, "http.1");
11090        assert!(reason.contains('.'), "got: {reason}");
11091    }
11092
11093    #[test]
11094    fn validate_etiquetas_empty_takes_precedence_over_shape() {
11095        // Per-entry empty-first cascade pin: an entry that is both
11096        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
11097        // narrower "this entry has no value" structural defect
11098        // dominates the broader shape-predicate diagnostic). The
11099        // empty arm fires before the shape predicate is consulted,
11100        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
11101        // cascade established on the sibling universal-axis Vec<String>
11102        // surface.
11103        let c = caixa_with_etiquetas(vec![""]);
11104        let err = c.validate_etiquetas().unwrap_err();
11105        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11106    }
11107
11108    #[test]
11109    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
11110        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11111        // entry that is malformed surfaces `EtiquetaInvalid` even when
11112        // a later entry would have collided on duplicate. The
11113        // per-entry shape arm fires inside the same loop iteration as
11114        // the empty arm, before the seen-set insert at end-of-iteration
11115        // — structural per-entry defects dominate the cross-entry
11116        // uniqueness diagnostic. Mirrors the peer
11117        // `validate_autores_shape_takes_precedence_over_duplicate`.
11118        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
11119        let err = c.validate_etiquetas().unwrap_err();
11120        assert!(
11121            matches!(err, ManifestError::EtiquetaInvalid { .. }),
11122            "got {err:?}",
11123        );
11124    }
11125
11126    #[test]
11127    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
11128        // Diagnostic-shape pin on the new shape arm (peer with
11129        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
11130        // the rendered Display surfaces both the offending slot name
11131        // and the offending value verbatim, so a `feira lint` run
11132        // points the author at the exact `:etiquetas` entry to fix.
11133        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11134        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11135        assert!(
11136            rendered.contains(":etiquetas"),
11137            "diagnostic must name the offending slot: {rendered}",
11138        );
11139        assert!(
11140            rendered.contains("mesh\\nhttp"),
11141            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11142        );
11143    }
11144
11145    #[test]
11146    fn validate_etiquetas_rejects_at_21_byte_boundary() {
11147        // The 20-byte cap pin — boundary-exceeding case rejected,
11148        // boundary-accepting case passes. Mirrors the peer
11149        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
11150        // side pin, surfaced at the per-axis caller so the cap
11151        // propagates through validate end-to-end. Constructed as a
11152        // single all-`a` token so only the cap arm fires.
11153        let max_ok = "a".repeat(20);
11154        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
11155        c.validate_etiquetas().unwrap();
11156        let too_long = "a".repeat(21);
11157        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
11158        let err = c.validate_etiquetas().unwrap_err();
11159        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
11160            panic!("expected EtiquetaInvalid, got {err:?}");
11161        };
11162        assert!(reason.contains("20"), "got: {reason}");
11163        assert!(reason.contains("21"), "got: {reason}");
11164    }
11165
11166    #[test]
11167    fn validate_etiquetas_accepts_canonical_shaped_forms() {
11168        // Positive control sweep: every canonical-shaped tag from the
11169        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
11170        // example fixtures plus the substrate-fixed tags caixa-helm
11171        // unions in at chart render. Drift between this list and the
11172        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
11173        // sweep surfaces here — one source of truth for the rule.
11174        let c = caixa_with_etiquetas(vec![
11175            "example",
11176            "aplicacao",
11177            "mesh",
11178            "ecommerce",
11179            "demo",
11180            "infrastructure",
11181            "aws",
11182            "akeyless",
11183            "pangea-native",
11184            "hello-world",
11185            "wasm",
11186            "rust",
11187            "tatara-lisp",
11188            "caixa-servico",
11189            "lareira",
11190        ]);
11191        c.validate_etiquetas().unwrap();
11192    }
11193
11194    // ── validate_autores — universal-axis maintainer shape ────────────
11195
11196    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
11197        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11198        c.autores = autores.into_iter().map(String::from).collect();
11199        c
11200    }
11201
11202    #[test]
11203    fn validate_autores_accepts_empty_list() {
11204        // The empty-list identity: `Caixa::template` emits `:autores ()`,
11205        // so the gate is non-disruptive against every existing manifest.
11206        let c = caixa_with_autores(vec![]);
11207        c.validate_autores().unwrap();
11208    }
11209
11210    #[test]
11211    fn validate_autores_accepts_canonical_forms() {
11212        // Positive control sweep: every canonical-shaped non-empty
11213        // distinct maintainer list passes — the hello-rio / checkout-
11214        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
11215        // multi-author shape downstream packaging surfaces emit.
11216        let c = caixa_with_autores(vec!["pleme-io"]);
11217        c.validate_autores().unwrap();
11218        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
11219        c.validate_autores().unwrap();
11220    }
11221
11222    #[test]
11223    fn validate_autores_rejects_empty_entry() {
11224        // Canonical paste-from-blank-doc footgun. Without the gate the
11225        // empty entry rendered as `maintainers: [{name: "", email: null}]`
11226        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
11227        // to.
11228        let c = caixa_with_autores(vec![""]);
11229        let err = c.validate_autores().unwrap_err();
11230        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11231    }
11232
11233    #[test]
11234    fn validate_autores_rejects_duplicate_entry() {
11235        // Canonical copy-paste-the-wrong-author footgun. Unlike the
11236        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
11237        // dedups the rendered `keywords:` array), the `maintainers:`
11238        // rendering has *no* dedup — duplicates stack verbatim. The
11239        // duplicate-arm names the offending author verbatim.
11240        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11241        let err = c.validate_autores().unwrap_err();
11242        let ManifestError::AutorDuplicate { autor } = err else {
11243            panic!("expected AutorDuplicate, got {err:?}");
11244        };
11245        assert_eq!(autor, "pleme-io");
11246    }
11247
11248    #[test]
11249    fn validate_autores_empty_takes_precedence_over_duplicate() {
11250        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
11251        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
11252        // "this entry has no value" defect dominates the cross-entry
11253        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
11254        // cascades on `:etiquetas` (`EtiquetaEmpty` before
11255        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
11256        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11257        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
11258        // `MembroDuplicate`).
11259        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
11260        let err = c.validate_autores().unwrap_err();
11261        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11262    }
11263
11264    #[test]
11265    fn validate_autores_duplicate_reports_first_collision() {
11266        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11267        // duplicate (the lexicographically-earliest offending position
11268        // — the second `"a"` at index 2 collides with the first `"a"`
11269        // at index 0), not the later `"b"` collision at index 3,
11270        // peer with every other first-collision diagnostic posture on
11271        // this surface.
11272        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
11273        let err = c.validate_autores().unwrap_err();
11274        let ManifestError::AutorDuplicate { autor } = err else {
11275            panic!("expected AutorDuplicate, got {err:?}");
11276        };
11277        assert_eq!(autor, "a");
11278    }
11279
11280    #[test]
11281    fn validate_autores_case_sensitive() {
11282        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
11283        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
11284        // / `:children :caixa` exact-string-match discipline.
11285        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
11286        c.validate_autores().unwrap();
11287    }
11288
11289    #[test]
11290    fn validate_autores_diagnostic_carries_offending_author() {
11291        // Diagnostic-shape pin (peer with
11292        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
11293        // error's Display surfaces the offending author verbatim, so a
11294        // `feira lint` run can render the diagnostic without re-parsing
11295        // and the author can grep their caixa.lisp for the offending
11296        // value.
11297        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11298        let rendered = c.validate_autores().unwrap_err().to_string();
11299        assert!(
11300            rendered.contains(":autores"),
11301            "diagnostic must name the offending slot: {rendered}",
11302        );
11303        assert!(
11304            rendered.contains("pleme-io"),
11305            "diagnostic must quote the offending author: {rendered}",
11306        );
11307    }
11308
11309    #[test]
11310    fn validate_autores_rejects_leading_whitespace_entry() {
11311        // Canonical paste-from-aligned-doc footgun. Without the shape
11312        // gate `" pleme-io"` silently passed validate and landed as a
11313        // YAML plain-style scalar with leading whitespace in the
11314        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
11315        // dumper trims leading whitespace from plain-style scalars, so
11316        // the authored space round-tripped inconsistently back through
11317        // `caixa.lisp`. Mirrors the peer
11318        // `validate_descricao_rejects_leading_whitespace`.
11319        let c = caixa_with_autores(vec![" pleme-io"]);
11320        let err = c.validate_autores().unwrap_err();
11321        let ManifestError::AutorInvalid { autor, reason } = err else {
11322            panic!("expected AutorInvalid, got {err:?}");
11323        };
11324        assert_eq!(autor, " pleme-io");
11325        assert!(reason.contains("whitespace"), "got: {reason}");
11326    }
11327
11328    #[test]
11329    fn validate_autores_rejects_trailing_whitespace_entry() {
11330        // Canonical paste-from-doc footgun.
11331        let c = caixa_with_autores(vec!["pleme-io "]);
11332        let err = c.validate_autores().unwrap_err();
11333        let ManifestError::AutorInvalid { autor, reason } = err else {
11334            panic!("expected AutorInvalid, got {err:?}");
11335        };
11336        assert_eq!(autor, "pleme-io ");
11337        assert!(reason.contains("whitespace"), "got: {reason}");
11338    }
11339
11340    #[test]
11341    fn validate_autores_rejects_embedded_newline_entry() {
11342        // Canonical paste-from-multiline-doc footgun — the author
11343        // pasted a multi-line block of author records into one
11344        // `:autores` entry instead of splitting into one entry per
11345        // author. Without the shape gate `"alice\nbob"` silently
11346        // passed validate and landed as a YAML-illegal multi-line
11347        // scalar in the rendered Chart.yaml `maintainers:` array.
11348        let c = caixa_with_autores(vec!["alice\nbob"]);
11349        let err = c.validate_autores().unwrap_err();
11350        let ManifestError::AutorInvalid { autor, reason } = err else {
11351            panic!("expected AutorInvalid, got {err:?}");
11352        };
11353        assert_eq!(autor, "alice\nbob");
11354        assert!(reason.contains("newline"), "got: {reason}");
11355    }
11356
11357    #[test]
11358    fn validate_autores_rejects_embedded_carriage_return_entry() {
11359        // Canonical paste-from-Windows-CRLF-doc footgun.
11360        let c = caixa_with_autores(vec!["alice\rbob"]);
11361        let err = c.validate_autores().unwrap_err();
11362        let ManifestError::AutorInvalid { autor, reason } = err else {
11363            panic!("expected AutorInvalid, got {err:?}");
11364        };
11365        assert_eq!(autor, "alice\rbob");
11366        assert!(reason.contains("carriage return"), "got: {reason}");
11367    }
11368
11369    #[test]
11370    fn validate_autores_rejects_embedded_tab_entry() {
11371        // Canonical tab-from-aligned-doc footgun.
11372        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
11373        let err = c.validate_autores().unwrap_err();
11374        let ManifestError::AutorInvalid { autor, reason } = err else {
11375            panic!("expected AutorInvalid, got {err:?}");
11376        };
11377        assert_eq!(autor, "Pleme\tContributors");
11378        assert!(reason.contains("tab"), "got: {reason}");
11379    }
11380
11381    #[test]
11382    fn validate_autores_rejects_embedded_control_bytes_entry() {
11383        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
11384        // surface the same control-byte arm.
11385        for entry in [
11386            "alice\x00bob",
11387            "alice\x07bob",
11388            "alice\x1bbob",
11389            "alice\x7fbob",
11390        ] {
11391            let c = caixa_with_autores(vec![entry]);
11392            let err = c.validate_autores().unwrap_err();
11393            let ManifestError::AutorInvalid { autor, reason } = err else {
11394                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
11395            };
11396            assert_eq!(autor, entry);
11397            assert!(
11398                reason.contains("control character"),
11399                "{entry:?} reason: {reason}",
11400            );
11401        }
11402    }
11403
11404    #[test]
11405    fn validate_autores_accepts_unicode_entry() {
11406        // Unicode positive control: realistic maintainer names carry
11407        // Unicode (`François`, `日本語`, `naïve`). The predicate must
11408        // round-trip Unicode losslessly, peer with the
11409        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
11410        // sweep.
11411        let c = caixa_with_autores(vec![
11412            "François Dupont",
11413            "日本語の名前",
11414            "naïve <naive@example.com>",
11415        ]);
11416        c.validate_autores().unwrap();
11417    }
11418
11419    #[test]
11420    fn validate_autores_empty_takes_precedence_over_shape() {
11421        // Per-entry empty-first cascade pin: an entry that is both
11422        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
11423        // "this entry has no value" structural defect dominates the
11424        // broader shape-predicate diagnostic). The empty arm fires
11425        // before the shape predicate is consulted, mirroring the peer
11426        // `validate_repositorio_empty_takes_precedence_over_shape`
11427        // cascade on the universal `Option<String>` siblings — and now
11428        // established on the Vec<String> per-entry surface.
11429        let c = caixa_with_autores(vec![""]);
11430        let err = c.validate_autores().unwrap_err();
11431        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11432    }
11433
11434    #[test]
11435    fn validate_autores_shape_takes_precedence_over_duplicate() {
11436        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11437        // entry that is malformed surfaces `AutorInvalid` even when a
11438        // later entry would have collided on duplicate. The per-entry
11439        // shape arm fires inside the same loop iteration as the empty
11440        // arm, before the seen-set insert at end-of-iteration —
11441        // structural per-entry defects dominate the cross-entry
11442        // uniqueness diagnostic.
11443        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
11444        let err = c.validate_autores().unwrap_err();
11445        assert!(
11446            matches!(err, ManifestError::AutorInvalid { .. }),
11447            "got {err:?}",
11448        );
11449    }
11450
11451    #[test]
11452    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
11453        // Diagnostic-shape pin on the new shape arm (peer with
11454        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
11455        // the rendered Display surfaces both the offending slot name
11456        // and the offending value verbatim, so a `feira lint` run
11457        // points the author at the exact `:autores` entry to fix.
11458        let c = caixa_with_autores(vec!["alice\nbob"]);
11459        let rendered = c.validate_autores().unwrap_err().to_string();
11460        assert!(
11461            rendered.contains(":autores"),
11462            "diagnostic must name the offending slot: {rendered}",
11463        );
11464        assert!(
11465            rendered.contains("alice\\nbob"),
11466            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11467        );
11468    }
11469
11470    #[test]
11471    fn validate_autores_rejects_at_129_byte_boundary() {
11472        // The 128-byte cap pin — boundary-exceeding case rejected,
11473        // boundary-accepting case passes. Mirrors the peer
11474        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
11475        // substrate-side pin, surfaced at the per-axis caller so the
11476        // cap propagates through validate end-to-end. Constructed as
11477        // a single all-`a` token so only the cap arm fires.
11478        let max_ok = "a".repeat(128);
11479        let c = caixa_with_autores(vec![max_ok.as_str()]);
11480        c.validate_autores().unwrap();
11481        let too_long = "a".repeat(129);
11482        let c = caixa_with_autores(vec![too_long.as_str()]);
11483        let err = c.validate_autores().unwrap_err();
11484        let ManifestError::AutorInvalid { reason, .. } = err else {
11485            panic!("expected AutorInvalid, got {err:?}");
11486        };
11487        assert!(reason.contains("128"), "got: {reason}");
11488        assert!(reason.contains("129"), "got: {reason}");
11489    }
11490
11491    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
11492
11493    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
11494        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11495        c.repositorio = repositorio.map(String::from);
11496        c
11497    }
11498
11499    #[test]
11500    fn validate_repositorio_accepts_none() {
11501        // The omit-the-slot identity: `:repositorio` is optional. The
11502        // gate is a no-op when the author didn't declare a value —
11503        // every caixa without a `:repositorio` line trivially passes,
11504        // and the substrate-side renderers fall back to their
11505        // documented placeholder (`caixa-helm`'s `home: None`,
11506        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
11507        // URL). Mirrors the peer `validate_restart_window_accepts_none`
11508        // posture on the other `Option<String>` Caixa slot.
11509        let c = caixa_with_repositorio(None);
11510        c.validate_repositorio().unwrap();
11511    }
11512
11513    #[test]
11514    fn validate_repositorio_accepts_canonical_forms() {
11515        // Positive control sweep across every documented `:repositorio`
11516        // authoring shape — the same union the shared
11517        // `crate::render::is_git_repo_url` predicate accepts and the
11518        // peer `:deps :fonte :repo` axis already routes through.
11519        // Covers the `github:` shorthand (the canonical pleme-io
11520        // convention used in the `:repositorio` field of every
11521        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
11522        // `examples/`), the `https://…` URL the README quickstart uses,
11523        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
11524        // `file://` URL schemes the shared predicate documents.
11525        for repo in [
11526            "github:pleme-io/hello-rio",
11527            "github:pleme-io/checkout",
11528            "https://github.com/pleme-io/hello-rio",
11529            "ssh://git@github.com/pleme-io/hello-rio.git",
11530            "git://github.com/pleme-io/hello-rio.git",
11531            "git@github.com:pleme-io/hello-rio.git",
11532            "file:///srv/pleme/hello-rio",
11533        ] {
11534            let c = caixa_with_repositorio(Some(repo));
11535            c.validate_repositorio()
11536                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
11537        }
11538    }
11539
11540    #[test]
11541    fn validate_repositorio_rejects_empty_some() {
11542        // Canonical paste-from-blank-doc footgun. The narrower
11543        // [`ManifestError::RepositorioEmpty`] arm fires before the
11544        // shape predicate is consulted, mirroring the empty-first
11545        // cascade every peer per-axis identity gate uses
11546        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
11547        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
11548        // the empty `Some("")` silently passed the renderer's
11549        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
11550        // on `None`) and landed as `home: ""` in `Chart.yaml` /
11551        // `url: ""` in the FluxCD `GitRepository`.
11552        let c = caixa_with_repositorio(Some(""));
11553        let err = c.validate_repositorio().unwrap_err();
11554        assert!(
11555            matches!(err, ManifestError::RepositorioEmpty),
11556            "got {err:?}",
11557        );
11558    }
11559
11560    #[test]
11561    fn validate_repositorio_rejects_whitespace() {
11562        // Paste-from-doc whitespace footgun. The shared
11563        // `is_git_repo_url` predicate refuses any whitespace byte; a
11564        // trailing space in a `:repositorio` value silently broke
11565        // `git clone '<value> '` at clone time. The diagnostic names
11566        // the offending value verbatim.
11567        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
11568        let err = c.validate_repositorio().unwrap_err();
11569        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
11570            panic!("expected RepositorioInvalid, got {err:?}");
11571        };
11572        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
11573    }
11574
11575    #[test]
11576    fn validate_repositorio_rejects_control_char() {
11577        // Paste-from-multiline-doc CRLF footgun — control characters
11578        // at the URL boundary are a class of subprocess-arg injection
11579        // and break git's URL parser at every porcelain entry point.
11580        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
11581        let err = c.validate_repositorio().unwrap_err();
11582        assert!(
11583            matches!(err, ManifestError::RepositorioInvalid { .. }),
11584            "got {err:?}",
11585        );
11586    }
11587
11588    #[test]
11589    fn validate_repositorio_rejects_leading_dash() {
11590        // Canonical CLI-argument-injection footgun: `git clone <repo>`
11591        // interprets a leading `-` as a CLI flag, so a
11592        // `-upload-pack=…` value escapes the subprocess argument
11593        // boundary. The shared predicate refuses every leading-`-`
11594        // shape at validate time.
11595        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
11596        let err = c.validate_repositorio().unwrap_err();
11597        assert!(
11598            matches!(err, ManifestError::RepositorioInvalid { .. }),
11599            "got {err:?}",
11600        );
11601    }
11602
11603    #[test]
11604    fn validate_repositorio_rejects_missing_colon_separator() {
11605        // The bare `org/repo` ambiguity footgun — `git clone` reads
11606        // a no-`:` form as a relative filesystem path rather than the
11607        // GitHub-shorthand expansion the author probably intended.
11608        // The shared predicate refuses every shape without a `:`
11609        // separator.
11610        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
11611        let err = c.validate_repositorio().unwrap_err();
11612        assert!(
11613            matches!(err, ManifestError::RepositorioInvalid { .. }),
11614            "got {err:?}",
11615        );
11616    }
11617
11618    #[test]
11619    fn validate_repositorio_rejects_fragment_anchor() {
11620        // Paste-from-browser-address-bar footgun on the
11621        // `:repositorio` axis — an author copies a GitHub permalink
11622        // to a README section / line-permalink and forgets to trim
11623        // the `#fragment` tail. The shared `is_git_repo_url`
11624        // predicate refuses the byte at the URL-grammar layer
11625        // (libcurl strips the fragment before opening the
11626        // transport, so the byte rides verbatim into the rendered
11627        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
11628        // fields but is silently dropped on the wire — two
11629        // manifest variants whose values differ only in their
11630        // fragment anchor lock to two distinct rendered artifacts
11631        // for the byte-identical clone, defeating the THEORY.md
11632        // §V.2 render-determinism contract on the `:repositorio`
11633        // axis the peer `:fonte :repo` axis already closes).
11634        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
11635        let err = c.validate_repositorio().unwrap_err();
11636        let ManifestError::RepositorioInvalid {
11637            repositorio,
11638            reason,
11639        } = err
11640        else {
11641            panic!("expected RepositorioInvalid, got {err:?}");
11642        };
11643        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
11644        assert!(
11645            reason.contains("must not contain `#`"),
11646            "reason must surface the fragment-`#` arm, got {reason:?}"
11647        );
11648    }
11649
11650    #[test]
11651    fn validate_repositorio_rejects_query_string() {
11652        // Paste-from-browser-address-bar footgun on the
11653        // `:repositorio` axis (peer with the a68f818 fragment-`#`
11654        // arm on the same axis). An author copies a GitHub tab
11655        // deep-link out of the address bar and forgets to trim
11656        // the `?tab=…` query tail. The shared `is_git_repo_url`
11657        // predicate refuses the byte at the URL-grammar layer
11658        // (GitHub / GitLab / Bitbucket silently ignore the
11659        // `?query` tail and serve the same repo regardless, so
11660        // the byte rides verbatim into the rendered `Chart.yaml`
11661        // `home:` and FluxCD `GitRepository` `url:` fields but
11662        // is silently masked at the wire — two manifest variants
11663        // whose values differ only in their query tail lock to
11664        // two distinct rendered artifacts for the byte-identical
11665        // clone, defeating the THEORY.md §V.2 render-determinism
11666        // contract on the `:repositorio` axis the peer `:fonte
11667        // :repo` axis already closes).
11668        let c = caixa_with_repositorio(Some(
11669            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
11670        ));
11671        let err = c.validate_repositorio().unwrap_err();
11672        let ManifestError::RepositorioInvalid {
11673            repositorio,
11674            reason,
11675        } = err
11676        else {
11677            panic!("expected RepositorioInvalid, got {err:?}");
11678        };
11679        assert_eq!(
11680            repositorio,
11681            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
11682        );
11683        assert!(
11684            reason.contains("must not contain `?`"),
11685            "reason must surface the query-`?` arm, got {reason:?}"
11686        );
11687    }
11688
11689    #[test]
11690    fn validate_repositorio_rejects_embedded_backslash() {
11691        // Windows-file-path-confusion footgun on the `:repositorio`
11692        // axis (peer with the prior fragment-`#` / query-`?` arms on
11693        // the same axis, and peer with the new dep-level `:fonte :repo`
11694        // backslash arm on the URL-grammar trajectory). An author
11695        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
11696        // hello-rio` into the `:repositorio` slot, expecting the
11697        // `lareira-<nome>` chart's `home:` field and the FluxCD
11698        // `GitRepository` `url:` field to render the canonical local
11699        // file-URI. The shared `is_git_repo_url` predicate refuses
11700        // the byte at the URL-grammar layer (libcurl silently
11701        // translates `\` → `/` on some platforms and refuses it on
11702        // others, so the byte rides verbatim into the rendered
11703        // artifacts but is silently rewritten or rejected at the wire
11704        // — two manifest variants whose values differ only in
11705        // backslash-vs-forward-slash lock to two distinct rendered
11706        // artifacts for the byte-identical clone, defeating the
11707        // THEORY.md §V.2 render-determinism contract on the
11708        // `:repositorio` axis the peer `:fonte :repo` axis already
11709        // closes).
11710        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
11711        let err = c.validate_repositorio().unwrap_err();
11712        let ManifestError::RepositorioInvalid {
11713            repositorio,
11714            reason,
11715        } = err
11716        else {
11717            panic!("expected RepositorioInvalid, got {err:?}");
11718        };
11719        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
11720        assert!(
11721            reason.contains("must not contain `\\`"),
11722            "reason must surface the backslash-`\\` arm, got {reason:?}"
11723        );
11724    }
11725
11726    #[test]
11727    fn validate_repositorio_rejects_uri_template_placeholder() {
11728        // URI Template (RFC 6570) placeholder footgun on the
11729        // `:repositorio` axis (peer with the prior fragment-`#` /
11730        // query-`?` / backslash-`\` arms on the same axis, and peer
11731        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
11732        // URL-grammar trajectory). An author pastes a quick-start
11733        // README snippet / OpenAPI `servers:` URL / Helm chart
11734        // `home:` template carrying unresolved `{org}` / `{repo}`
11735        // placeholders into the `:repositorio` slot, expecting the
11736        // substrate to resolve the placeholder downstream. The
11737        // shared `is_git_repo_url` predicate refuses the byte at the
11738        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
11739        // `%7B` / `%7D` on the wire, so the byte round-trips
11740        // inconsistently between the rendered `Chart.yaml home:` /
11741        // FluxCD `GitRepository url:` and the resolver's `git clone`
11742        // invocation, defeating the THEORY.md §V.2 render-
11743        // determinism contract on the `:repositorio` axis the peer
11744        // `:fonte :repo` axis already closes; every git porcelain
11745        // entry-point additionally fetches a nonexistent literal-
11746        // `{placeholder}`-named path far from the source caixa.lisp).
11747        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
11748        let err = c.validate_repositorio().unwrap_err();
11749        let ManifestError::RepositorioInvalid {
11750            repositorio,
11751            reason,
11752        } = err
11753        else {
11754            panic!("expected RepositorioInvalid, got {err:?}");
11755        };
11756        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
11757        assert!(
11758            reason.contains("must not contain `{`"),
11759            "reason must surface the open-brace `{{` arm, got {reason:?}"
11760        );
11761        assert!(
11762            reason.contains("URI Template") || reason.contains("RFC 6570"),
11763            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
11764        );
11765    }
11766
11767    #[test]
11768    fn validate_repositorio_empty_takes_precedence_over_shape() {
11769        // Empty-first cascade pin: the empty `Some("")` surfaces the
11770        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
11771        // `RepositorioInvalid`, mirroring the peer
11772        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
11773        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
11774        // `is_git_repo_url` predicate also rejects the empty input
11775        // (defensively, with its own `"must not be empty"` reason),
11776        // but the manifest-layer empty arm runs first to surface the
11777        // narrower diagnostic verbatim.
11778        let c = caixa_with_repositorio(Some(""));
11779        let err = c.validate_repositorio().unwrap_err();
11780        assert!(
11781            matches!(err, ManifestError::RepositorioEmpty),
11782            "got {err:?}",
11783        );
11784    }
11785
11786    #[test]
11787    fn validate_repositorio_diagnostic_carries_offending_value() {
11788        // Diagnostic-shape pin (peer with
11789        // `validate_autores_diagnostic_carries_offending_author`): the
11790        // error's Display surfaces the offending value + slot name
11791        // verbatim, so a `feira lint` run can render the diagnostic
11792        // without re-parsing and the author can grep their caixa.lisp
11793        // for the offending `:repositorio` value.
11794        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
11795        let rendered = c.validate_repositorio().unwrap_err().to_string();
11796        assert!(
11797            rendered.contains(":repositorio"),
11798            "diagnostic must name the offending slot: {rendered}",
11799        );
11800        assert!(
11801            rendered.contains("pleme-io/hello-rio"),
11802            "diagnostic must quote the offending value: {rendered}",
11803        );
11804    }
11805
11806    // ── validate_descricao — universal-axis Chart.yaml description shape ──
11807
11808    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
11809        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11810        c.descricao = descricao.map(String::from);
11811        c
11812    }
11813
11814    #[test]
11815    fn validate_descricao_accepts_none() {
11816        // The omit-the-slot identity: `:descricao` is optional. The
11817        // gate is a no-op when the author didn't declare a value —
11818        // every caixa without a `:descricao` line trivially passes,
11819        // and the substrate-side renderers fall back to their
11820        // documented `caixa.nome`-derived placeholder. Mirrors the
11821        // peer `validate_repositorio_accepts_none` posture on the
11822        // sibling `Option<String>` Caixa slot.
11823        let c = caixa_with_descricao(None);
11824        c.validate_descricao().unwrap();
11825    }
11826
11827    #[test]
11828    fn validate_descricao_accepts_canonical_summary() {
11829        // Positive control: the canonical pleme-io descricao shape —
11830        // a short free-form prose summary — passes the gate. Covers
11831        // the fixture shapes the `caixa-helm` / `caixa-flux` /
11832        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
11833        // wasip2 caixa Servico."`, `"Checkout flow."`).
11834        for desc in [
11835            "Canonical Rust→wasm32-wasip2 caixa Servico.",
11836            "Checkout flow.",
11837            "AWS provider caixa for tatara-lisp",
11838            "FIXME — describe this caixa",
11839            "x",
11840        ] {
11841            let c = caixa_with_descricao(Some(desc));
11842            c.validate_descricao()
11843                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
11844        }
11845    }
11846
11847    #[test]
11848    fn validate_descricao_rejects_empty_some() {
11849        // Canonical paste-from-blank-doc footgun. Without this gate
11850        // the empty `Some("")` silently passed the renderer's
11851        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
11852        // on `None`) and landed as `description: ""` in `Chart.yaml`
11853        // and a blank `README.md` header. Mirrors the peer
11854        // [`ManifestError::RepositorioEmpty`] empty-arm on the
11855        // sibling `Option<String>` Caixa slot.
11856        let c = caixa_with_descricao(Some(""));
11857        let err = c.validate_descricao().unwrap_err();
11858        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
11859    }
11860
11861    #[test]
11862    fn validate_descricao_rejects_leading_whitespace() {
11863        // Paste-from-aligned-doc footgun: a leading ASCII space the
11864        // bare empty-arm gate accepted, the shape predicate now
11865        // refuses. The diagnostic carries the offending value
11866        // verbatim (with the leading space preserved) so the author
11867        // can grep their caixa.lisp for the exact `:descricao` line
11868        // and fix the round-trip-inconsistent leading whitespace.
11869        // Mirrors the peer
11870        // `validate_licenca_rejects_leading_whitespace` arm on the
11871        // sibling `:licenca` axis.
11872        let c = caixa_with_descricao(Some(" Checkout flow."));
11873        let err = c.validate_descricao().unwrap_err();
11874        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
11875            panic!("expected DescricaoInvalid, got {err:?}");
11876        };
11877        assert_eq!(descricao, " Checkout flow.");
11878        assert!(reason.contains("whitespace"), "got: {reason:?}");
11879    }
11880
11881    #[test]
11882    fn validate_descricao_rejects_trailing_whitespace() {
11883        // Paste-from-doc footgun: a trailing ASCII space the bare
11884        // empty-arm gate accepted, the shape predicate now refuses.
11885        let c = caixa_with_descricao(Some("Checkout flow. "));
11886        let err = c.validate_descricao().unwrap_err();
11887        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
11888            panic!("expected DescricaoInvalid, got {err:?}");
11889        };
11890        assert_eq!(descricao, "Checkout flow. ");
11891        assert!(reason.contains("whitespace"), "got: {reason:?}");
11892    }
11893
11894    #[test]
11895    fn validate_descricao_rejects_embedded_newline() {
11896        // Paste-from-multiline-doc footgun: an embedded LF the bare
11897        // empty-arm gate accepted, the shape predicate now refuses.
11898        // Without this gate the embedded newline silently landed in
11899        // the rendered Chart.yaml as a multi-line YAML block scalar,
11900        // and every chart-aware UI (`helm list`, `helm search`,
11901        // Artifact Hub) renders the description in a single-line
11902        // column so the embedded newline is silently dropped at
11903        // every downstream consumer.
11904        let c = caixa_with_descricao(Some("Checkout\nflow."));
11905        let err = c.validate_descricao().unwrap_err();
11906        assert!(
11907            matches!(err, ManifestError::DescricaoInvalid { .. }),
11908            "got {err:?}",
11909        );
11910        assert!(err.to_string().contains("newline"), "got {err}");
11911    }
11912
11913    #[test]
11914    fn validate_descricao_rejects_embedded_carriage_return() {
11915        // Paste-from-Windows-CRLF-doc footgun.
11916        let c = caixa_with_descricao(Some("Checkout\rflow."));
11917        let err = c.validate_descricao().unwrap_err();
11918        assert!(
11919            matches!(err, ManifestError::DescricaoInvalid { .. }),
11920            "got {err:?}",
11921        );
11922        assert!(err.to_string().contains("carriage return"), "got {err}");
11923    }
11924
11925    #[test]
11926    fn validate_descricao_rejects_embedded_tab() {
11927        // Tab-from-aligned-doc footgun.
11928        let c = caixa_with_descricao(Some("Checkout\tflow."));
11929        let err = c.validate_descricao().unwrap_err();
11930        assert!(
11931            matches!(err, ManifestError::DescricaoInvalid { .. }),
11932            "got {err:?}",
11933        );
11934        assert!(err.to_string().contains("tab"), "got {err}");
11935    }
11936
11937    #[test]
11938    fn validate_descricao_rejects_embedded_control_bytes() {
11939        // Paste-from-binary-blob footgun: every other control byte
11940        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
11941        // the peer SPDX-expression control-byte arm.
11942        for s in [
11943            "Checkout\x00flow.",
11944            "Checkout\x07flow.",
11945            "Checkout\x1bflow.",
11946            "Checkout\x7fflow.",
11947        ] {
11948            let c = caixa_with_descricao(Some(s));
11949            let err = c.validate_descricao().unwrap_err();
11950            assert!(
11951                matches!(err, ManifestError::DescricaoInvalid { .. }),
11952                "{s:?} got {err:?}",
11953            );
11954            assert!(
11955                err.to_string().contains("control character"),
11956                "{s:?} got {err}",
11957            );
11958        }
11959    }
11960
11961    #[test]
11962    fn validate_descricao_accepts_unicode_prose() {
11963        // Positive control: Unicode prose is accepted — the
11964        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
11965        // and `Caixa::template`'s `"FIXME — describe this caixa"`
11966        // scaffold every `feira init` emits must continue to pass.
11967        for s in [
11968            "Canonical Rust→wasm32-wasip2 caixa Servico.",
11969            "FIXME — describe this caixa",
11970            "Caixa pour le projet tâche",
11971            "日本語の説明",
11972        ] {
11973            let c = caixa_with_descricao(Some(s));
11974            c.validate_descricao()
11975                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
11976        }
11977    }
11978
11979    #[test]
11980    fn validate_descricao_empty_takes_precedence_over_shape() {
11981        // Cascade pin: a `Some("")` surfaces the narrower
11982        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
11983        // shape-predicate arm. Mirrors the peer
11984        // `validate_licenca_empty_takes_precedence_over_shape` pin
11985        // on the sibling `:licenca` axis.
11986        let c = caixa_with_descricao(Some(""));
11987        let err = c.validate_descricao().unwrap_err();
11988        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
11989    }
11990
11991    #[test]
11992    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
11993        // Diagnostic-shape pin: the error's Display surfaces both
11994        // the `:descricao` slot name and the offending value
11995        // verbatim, so a `feira lint` run can render the diagnostic
11996        // without re-parsing and the author can grep their caixa.lisp
11997        // for the offending `:descricao` line. Mirrors the peer
11998        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
11999        // pin (ee2e888) on the sibling `:licenca` axis.
12000        // The `{descricao:?}` Debug format escapes embedded control
12001        // bytes; the quoted offending value surfaces as
12002        // `"Checkout\nflow."` (literal backslash-n) in the rendered
12003        // diagnostic. The author can grep their caixa.lisp for the
12004        // literal `Checkout` summary prefix.
12005        let c = caixa_with_descricao(Some("Checkout\nflow."));
12006        let rendered = c.validate_descricao().unwrap_err().to_string();
12007        assert!(
12008            rendered.contains(":descricao"),
12009            "diagnostic must name the offending slot: {rendered}",
12010        );
12011        assert!(
12012            rendered.contains("Checkout\\nflow."),
12013            "diagnostic must quote the offending value (debug-escaped): {rendered}",
12014        );
12015    }
12016
12017    #[test]
12018    fn validate_descricao_template_passes() {
12019        // Round-trip pin: the bare `Caixa::template` shape carries
12020        // `:descricao "FIXME — describe this caixa"` (a non-empty
12021        // sentinel), so the template-derived Caixa passes the gate by
12022        // construction. A future template-shape change that omits or
12023        // empties `:descricao` would surface here as a regression.
12024        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12025        c.validate_descricao().unwrap();
12026    }
12027
12028    #[test]
12029    fn validate_descricao_diagnostic_names_offending_slot() {
12030        // Diagnostic-shape pin (peer with
12031        // `validate_repositorio_diagnostic_carries_offending_value`):
12032        // the error's Display surfaces the `:descricao` slot name
12033        // verbatim, so a `feira lint` run can render the diagnostic
12034        // without re-parsing and the author can grep their caixa.lisp
12035        // for the offending `:descricao` line.
12036        let c = caixa_with_descricao(Some(""));
12037        let rendered = c.validate_descricao().unwrap_err().to_string();
12038        assert!(
12039            rendered.contains(":descricao"),
12040            "diagnostic must name the offending slot: {rendered}",
12041        );
12042    }
12043
12044    // ── validate_licenca — universal-axis chart README license shape ──
12045
12046    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
12047        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12048        c.licenca = licenca.map(String::from);
12049        c
12050    }
12051
12052    #[test]
12053    fn validate_licenca_accepts_none() {
12054        // The omit-the-slot identity: `:licenca` is optional. The
12055        // gate is a no-op when the author didn't declare a value —
12056        // every caixa without a `:licenca` line trivially passes,
12057        // and the substrate-side `caixa-helm` renderer falls back to
12058        // the documented `"MIT"` placeholder. Mirrors the peer
12059        // `validate_descricao_accepts_none` posture on the sibling
12060        // `Option<String>` Caixa slot.
12061        let c = caixa_with_licenca(None);
12062        c.validate_licenca().unwrap();
12063    }
12064
12065    #[test]
12066    fn validate_licenca_accepts_canonical_expressions() {
12067        // Positive control: every canonical SPDX expression shape
12068        // pleme-io carries in its existing fixtures + the canonical
12069        // SPDX dual-license / with-exception / `+`-suffix / grouped /
12070        // user-defined-reference shapes all pass the gate. Covers
12071        // the single-license, `OR`-compound, `AND`-compound,
12072        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
12073        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
12074        // production the SPDX 2.1 expression grammar admits that
12075        // sits within the alphabet floor the
12076        // `is_spdx_expression_shape` predicate enforces.
12077        for lic in [
12078            "MIT",
12079            "Apache-2.0",
12080            "Apache-2.0 OR MIT",
12081            "Apache-2.0 AND MIT",
12082            "BSD-3-Clause",
12083            "MPL-2.0",
12084            "GPL-3.0-or-later",
12085            "GPL-2.0+",
12086            "Apache-2.0 WITH LLVM-exception",
12087            "(MIT OR Apache-2.0) AND BSD-3-Clause",
12088            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
12089            "LicenseRef-MyLicense",
12090            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
12091            "x",
12092        ] {
12093            let c = caixa_with_licenca(Some(lic));
12094            c.validate_licenca()
12095                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
12096        }
12097    }
12098
12099    #[test]
12100    fn validate_licenca_rejects_trailing_whitespace() {
12101        // Paste-from-doc whitespace footgun. A trailing space in the
12102        // `:licenca` value would silently break a downstream SPDX
12103        // parser that splits on exact `AND` / `OR` / `WITH` keyword
12104        // boundaries. The shape predicate refuses every trailing
12105        // whitespace byte by construction. Peer with
12106        // `validate_repositorio_rejects_whitespace` and
12107        // `validate_edicao_rejects_trailing_whitespace`.
12108        let c = caixa_with_licenca(Some("MIT "));
12109        let err = c.validate_licenca().unwrap_err();
12110        let ManifestError::LicencaInvalid { licenca, .. } = err else {
12111            panic!("expected LicencaInvalid, got {err:?}");
12112        };
12113        assert_eq!(licenca, "MIT ");
12114    }
12115
12116    #[test]
12117    fn validate_licenca_rejects_leading_whitespace() {
12118        // Symmetric paste-from-doc whitespace footgun on the leading
12119        // boundary — the gate refuses every shape that starts with a
12120        // space byte by construction. Peer with
12121        // `validate_edicao_rejects_leading_whitespace`.
12122        let c = caixa_with_licenca(Some(" MIT"));
12123        let err = c.validate_licenca().unwrap_err();
12124        assert!(
12125            matches!(err, ManifestError::LicencaInvalid { .. }),
12126            "got {err:?}",
12127        );
12128    }
12129
12130    #[test]
12131    fn validate_licenca_rejects_control_char() {
12132        // Paste-from-multiline-doc CRLF footgun — control characters
12133        // at the value boundary land as a malformed line in the
12134        // rendered chart `README.md` `## License` section. Peer with
12135        // `validate_repositorio_rejects_control_char` and
12136        // `validate_edicao_rejects_control_char`.
12137        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
12138            let c = caixa_with_licenca(Some(lic));
12139            let err = c.validate_licenca().unwrap_err();
12140            assert!(
12141                matches!(err, ManifestError::LicencaInvalid { .. }),
12142                "expected LicencaInvalid on {lic:?}, got {err:?}",
12143            );
12144        }
12145    }
12146
12147    #[test]
12148    fn validate_licenca_rejects_tab() {
12149        // Tab-from-aligned-doc footgun — SPDX expressions use a
12150        // single ASCII space between tokens; a tab breaks every
12151        // downstream SPDX parser that splits on exact `" "`
12152        // boundaries.
12153        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
12154        let err = c.validate_licenca().unwrap_err();
12155        assert!(
12156            matches!(err, ManifestError::LicencaInvalid { .. }),
12157            "got {err:?}",
12158        );
12159    }
12160
12161    #[test]
12162    fn validate_licenca_rejects_non_ascii() {
12163        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
12164        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
12165        // ".")` production. The shape predicate refuses every
12166        // non-ASCII byte by construction; peer with
12167        // `validate_edicao_rejects_non_ascii_lookalike`.
12168        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
12169            let c = caixa_with_licenca(Some(lic));
12170            let err = c.validate_licenca().unwrap_err();
12171            assert!(
12172                matches!(err, ManifestError::LicencaInvalid { .. }),
12173                "expected LicencaInvalid on {lic:?}, got {err:?}",
12174            );
12175        }
12176    }
12177
12178    #[test]
12179    fn validate_licenca_rejects_underscore() {
12180        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
12181        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
12182        // snake-case identifier conventions that don't apply to the
12183        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
12184        // "-" / "."`). The shape predicate refuses every underscore
12185        // byte by construction.
12186        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
12187            let c = caixa_with_licenca(Some(lic));
12188            let err = c.validate_licenca().unwrap_err();
12189            assert!(
12190                matches!(err, ManifestError::LicencaInvalid { .. }),
12191                "expected LicencaInvalid on {lic:?}, got {err:?}",
12192            );
12193        }
12194    }
12195
12196    #[test]
12197    fn validate_licenca_rejects_comma_separator() {
12198        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
12199        // SPDX expressions compose multiple licenses via `AND` / `OR`
12200        // keywords, not the comma separator. The shape predicate
12201        // refuses every comma byte by construction.
12202        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
12203            let c = caixa_with_licenca(Some(lic));
12204            let err = c.validate_licenca().unwrap_err();
12205            assert!(
12206                matches!(err, ManifestError::LicencaInvalid { .. }),
12207                "expected LicencaInvalid on {lic:?}, got {err:?}",
12208            );
12209        }
12210    }
12211
12212    #[test]
12213    fn validate_licenca_rejects_slash_dual_license() {
12214        // Slash-dual-license colloquial idiom footgun — the
12215        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
12216        // `package.license` field but non-SPDX; the SPDX equivalent
12217        // is `MIT OR Apache-2.0`. The shape predicate refuses every
12218        // forward-slash byte by construction.
12219        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
12220            let c = caixa_with_licenca(Some(lic));
12221            let err = c.validate_licenca().unwrap_err();
12222            assert!(
12223                matches!(err, ManifestError::LicencaInvalid { .. }),
12224                "expected LicencaInvalid on {lic:?}, got {err:?}",
12225            );
12226        }
12227    }
12228
12229    #[test]
12230    fn validate_licenca_rejects_semicolon_separator() {
12231        // Semicolon-list-separator confusion footgun — adjacent to
12232        // the comma-separator idiom, every list-separator-belongs-
12233        // to-list-grammar confusion lands here.
12234        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
12235        let err = c.validate_licenca().unwrap_err();
12236        assert!(
12237            matches!(err, ManifestError::LicencaInvalid { .. }),
12238            "got {err:?}",
12239        );
12240    }
12241
12242    #[test]
12243    fn validate_licenca_empty_takes_precedence_over_shape() {
12244        // Empty-first cascade pin: the empty `Some("")` surfaces the
12245        // narrower `LicencaEmpty` not the shape-predicate-wrapped
12246        // `LicencaInvalid`, mirroring the peer
12247        // `validate_edicao_empty_takes_precedence_over_shape` and
12248        // `validate_repositorio_empty_takes_precedence_over_shape`
12249        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
12250        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
12251        // The shape predicate also refuses the empty input
12252        // (defensively — `"must not be empty"`), but the manifest-
12253        // layer empty arm runs first to surface the narrower
12254        // diagnostic verbatim.
12255        let c = caixa_with_licenca(Some(""));
12256        let err = c.validate_licenca().unwrap_err();
12257        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12258    }
12259
12260    #[test]
12261    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
12262        // Diagnostic-shape pin on the shape-predicate arm (peer with
12263        // `validate_edicao_invalid_diagnostic_carries_offending_value`
12264        // and `validate_repositorio_diagnostic_carries_offending_value`):
12265        // the error's Display surfaces the offending value + slot
12266        // name verbatim, so a `feira lint` run can render the
12267        // diagnostic without re-parsing and the author can grep
12268        // their caixa.lisp for the offending `:licenca` value.
12269        let c = caixa_with_licenca(Some("Apache_2.0"));
12270        let rendered = c.validate_licenca().unwrap_err().to_string();
12271        assert!(
12272            rendered.contains(":licenca"),
12273            "diagnostic must name the offending slot: {rendered}",
12274        );
12275        assert!(
12276            rendered.contains("Apache_2.0"),
12277            "diagnostic must quote the offending value: {rendered}",
12278        );
12279    }
12280
12281    #[test]
12282    fn validate_licenca_rejects_empty_some() {
12283        // Canonical paste-from-blank-doc footgun. Without this gate
12284        // the empty `Some("")` silently passed the renderer's
12285        // `Option::unwrap_or_else(|| "MIT".into())` (which only
12286        // fires on `None`) and landed as a bare trailing period in
12287        // the rendered chart `README.md` `## License` section.
12288        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
12289        // arm on the sibling `Option<String>` Caixa slot.
12290        let c = caixa_with_licenca(Some(""));
12291        let err = c.validate_licenca().unwrap_err();
12292        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12293    }
12294
12295    #[test]
12296    fn validate_licenca_template_passes() {
12297        // Round-trip pin: the bare `Caixa::template` shape (whether
12298        // it carries `:licenca` or omits it) passes the gate by
12299        // construction. A future template-shape change that
12300        // introduced `(:licenca "")` would surface here as a
12301        // regression. Mirrors the peer
12302        // `validate_descricao_template_passes` pin.
12303        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12304        c.validate_licenca().unwrap();
12305    }
12306
12307    #[test]
12308    fn validate_licenca_diagnostic_names_offending_slot() {
12309        // Diagnostic-shape pin (peer with
12310        // `validate_descricao_diagnostic_names_offending_slot`):
12311        // the error's Display surfaces the `:licenca` slot name
12312        // verbatim, so a `feira lint` run can render the diagnostic
12313        // without re-parsing and the author can grep their caixa.lisp
12314        // for the offending `:licenca` line.
12315        let c = caixa_with_licenca(Some(""));
12316        let rendered = c.validate_licenca().unwrap_err().to_string();
12317        assert!(
12318            rendered.contains(":licenca"),
12319            "diagnostic must name the offending slot: {rendered}",
12320        );
12321    }
12322
12323    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
12324
12325    #[test]
12326    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
12327        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
12328        // pin: [`Caixa::licenca`] must return the `:licenca` typed
12329        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12330        // raw `self.licenca.as_deref()` access across every
12331        // representative value in the accept-set — `None` (the "omit
12332        // the slot to defer to the caixa-helm renderer's `MIT`
12333        // fallback" arm every existing fixture without a `:licenca`
12334        // line carries), `Some("")` (a past-the-guard sentinel that
12335        // pins the accessor doesn't perform a silent
12336        // `Some("") → None` collapse on the empty arm — validate
12337        // rejects `Some("")` through `LicencaEmpty` but the accessor
12338        // must ship the raw slot verbatim so a validate-time gate
12339        // regression surfaces at the caixa-helm emit boundary rather
12340        // than being silently absorbed into the fallback), `Some("MIT")`
12341        // (the canonical single-license shape every `feira init`
12342        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
12343        // canonical `OR`-compound shape the peer
12344        // `validate_licenca_accepts_canonical_expressions` positive
12345        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
12346        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
12347        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
12348        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
12349        // guard sentinels — validate rejects each through
12350        // `LicencaInvalid` but the accessor must ship the raw slot
12351        // verbatim).
12352        //
12353        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
12354        // accessor pin on the substrate primitive — opens the "outer
12355        // [`Caixa`] `Option<&str>` scalar" projection pattern the
12356        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
12357        // future lifts fold on. Sibling in shape to the peer per-`:placement`
12358        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12359        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12360        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12361        // axes, extended onto the outer top-level [`Caixa`] universal-
12362        // axis surface. Pins against a future silent detour that
12363        // returned an owned `Option<String>` (which would type-check
12364        // but silently allocate on every accessor call, breaking the
12365        // zero-cost projection every peer sibling accessor carries), a
12366        // `Some("") → None` collapse (which would silently absorb the
12367        // `LicencaEmpty` refusal case at the accessor boundary and the
12368        // caixa-helm emit path would silently fall back to `"MIT"` on
12369        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
12370        // `None → Some("MIT")` collapse (which would silently reify
12371        // the caixa-helm renderer's `"MIT"` fallback at the accessor
12372        // boundary and every downstream consumer keying off the
12373        // `Option::is_none()` discriminator would lose the "author
12374        // omitted the slot" signal).
12375        for licenca in [
12376            None,
12377            Some(""),
12378            Some("MIT"),
12379            Some("Apache-2.0 OR MIT"),
12380            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
12381            Some("MIT "),
12382            Some(" MIT"),
12383            Some("MIT\n"),
12384            Some("Apache_2.0"),
12385            Some("MIT,Apache-2.0"),
12386        ] {
12387            let c = caixa_with_licenca(licenca);
12388            assert_eq!(
12389                c.licenca(),
12390                licenca,
12391                "Caixa::licenca must return :licenca verbatim (got {:?}, \
12392                 expected {licenca:?})",
12393                c.licenca(),
12394            );
12395            assert_eq!(
12396                c.licenca(),
12397                c.licenca.as_deref(),
12398                "Caixa::licenca must byte-equal the raw \
12399                 `self.licenca.as_deref()` field access across every \
12400                 value in the Option<&str> accept-set",
12401            );
12402        }
12403    }
12404
12405    #[test]
12406    fn validate_licenca_empty_arm_routes_through_accessor() {
12407        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
12408        // must key off [`Caixa::licenca`], not the raw
12409        // `self.licenca.as_deref()` field access. Structurally: a
12410        // `Caixa { licenca: Some(""), .. }` must surface the
12411        // `LicencaEmpty` refusal exactly, and a
12412        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
12413        // single-license form) must pass validate. The pair jointly
12414        // pins the accessor + validate-gate composition: any future
12415        // silent detour that had the accessor return `None` on the
12416        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
12417        // silently absorb the `LicencaEmpty` refusal at the accessor
12418        // boundary and the validate gate would accept a struct-literal
12419        // `Caixa { licenca: Some(""), .. }` — the composition pin
12420        // catches that at caixa-core build time.
12421        //
12422        // Peer of the per-`:politicas :circuit-breaker`
12423        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12424        // accessor-composition pin
12425        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12426        // on the sibling per-M3-mesh-slot required-`u32` axis — same
12427        // "the validate / shape-gate predicate must route through the
12428        // substrate-primitive typed dispatch" discipline extended onto
12429        // the outer top-level [`Caixa`] universal-axis
12430        // `Option<&str>`-composition surface.
12431        let c = caixa_with_licenca(Some(""));
12432        assert!(
12433            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
12434            "validate_licenca must reject licenca == Some(\"\") with \
12435             LicencaEmpty — the accessor and the validate gate must \
12436             route through the same substrate-primitive typed dispatch \
12437             on the :licenca empty arm",
12438        );
12439        let c = caixa_with_licenca(Some("MIT"));
12440        assert!(
12441            c.validate_licenca().is_ok(),
12442            "validate_licenca must accept licenca == Some(\"MIT\") \
12443             (the canonical single-license SPDX shape)",
12444        );
12445    }
12446
12447    #[test]
12448    fn licenca_projects_option_str_by_borrow() {
12449        // The by-borrow pin: [`Caixa::licenca`] returns
12450        // `Option<&str>` by borrow — the `&str` borrows the underlying
12451        // `String` storage of the `Option<String>` slot and the
12452        // accessor must not allocate a fresh `String` on every call.
12453        // Peer of the per-`:placement`
12454        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12455        // borrow pin on the peer per-M3-mesh-slot
12456        // `Option<&str>`-return axis, extended onto the outer top-
12457        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12458        // accessor's returned `&str` must borrow from `&self` (the
12459        // returned reference's lifetime is tied to `&self`), and
12460        // calling the accessor twice on the same [`Caixa`] must yield
12461        // the same `Option<&str>` verbatim (idempotent, no side
12462        // effects on `&self`).
12463        //
12464        // Pins against a future silent detour that returned an owned
12465        // `Option<String>` (which would type-check but silently
12466        // allocate on every call, breaking the zero-cost projection
12467        // every peer sibling accessor carries), or a one-arm-only
12468        // accessor that returned a saturating value on some sentinel
12469        // input (breaking the pass-through invariant the sibling
12470        // required-scalar accessors carry).
12471        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
12472            let c = caixa_with_licenca(licenca);
12473            let first = c.licenca();
12474            let second = c.licenca();
12475            assert_eq!(
12476                first, second,
12477                "Caixa::licenca must be idempotent — two successive \
12478                 calls on the same &self must return the same \
12479                 Option<&str>",
12480            );
12481            assert_eq!(
12482                first, licenca,
12483                "Caixa::licenca must return :licenca verbatim by \
12484                 borrow — got {first:?}, expected {licenca:?}",
12485            );
12486        }
12487    }
12488
12489    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
12490
12491    #[test]
12492    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
12493        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
12494        // pin: [`Caixa::repositorio`] must return the `:repositorio`
12495        // typed byte-string verbatim as an `Option<&str>`, byte-equal
12496        // to the raw `self.repositorio.as_deref()` access across every
12497        // representative value in the accept-set — `None` (the "omit
12498        // the slot to defer to the per-renderer placeholder" arm every
12499        // existing fixture without a `:repositorio` line carries),
12500        // `Some("")` (a past-the-guard sentinel that pins the accessor
12501        // doesn't perform a silent `Some("") → None` collapse on the
12502        // empty arm — validate rejects `Some("")` through
12503        // `RepositorioEmpty` but the accessor must ship the raw slot
12504        // verbatim so a validate-time gate regression surfaces at the
12505        // caixa-helm / caixa-flux emit boundary rather than being
12506        // silently absorbed into the per-renderer fallback),
12507        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
12508        // shorthand every existing manifest fixture across
12509        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
12510        // `Some("https://github.com/pleme-io/checkout")` (the canonical
12511        // `https://` URL the README quickstart uses),
12512        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
12513        // `Some("git://github.com/pleme-io/checkout.git")` /
12514        // `Some("git@github.com:pleme-io/checkout.git")` /
12515        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
12516        // github scheme the shared `is_git_repo_url` predicate
12517        // documents), and five past-the-guard sentinels for the
12518        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
12519        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
12520        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
12521        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
12522        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
12523        // sentinels pin the accessor doesn't silently absorb the
12524        // refusal cases into a fallback).
12525        //
12526        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
12527        // accessor pin on the substrate primitive — sibling of the peer
12528        // [`Caixa::licenca`] (6d5bc28) pin
12529        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
12530        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
12531        // projection pin pattern this pin folds on. Sibling in shape to
12532        // the peer per-`:placement`
12533        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12534        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12535        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12536        // axes, extended onto the outer top-level [`Caixa`] universal-
12537        // axis surface. Pins against a future silent detour that
12538        // returned an owned `Option<String>` (which would type-check
12539        // but silently allocate on every accessor call, breaking the
12540        // zero-cost projection every peer sibling accessor carries), a
12541        // `Some("") → None` collapse (which would silently absorb the
12542        // `RepositorioEmpty` refusal case at the accessor boundary and
12543        // the caixa-helm `Chart.yaml` `home:` fold would silently
12544        // render a `home: null` / omitted field on a struct-literal
12545        // `Caixa { repositorio: Some(""), .. }`), or a
12546        // `None → Some(<default>)` collapse (which would silently reify
12547        // the per-renderer fallback at the accessor boundary and every
12548        // downstream consumer keying off the `Option::is_none()`
12549        // discriminator would lose the "author omitted the slot"
12550        // signal).
12551        for repositorio in [
12552            None,
12553            Some(""),
12554            Some("github:pleme-io/hello-rio"),
12555            Some("https://github.com/pleme-io/checkout"),
12556            Some("ssh://git@github.com/pleme-io/checkout.git"),
12557            Some("git://github.com/pleme-io/checkout.git"),
12558            Some("git@github.com:pleme-io/checkout.git"),
12559            Some("file:///opt/mirrors/pleme-io/checkout"),
12560            Some("pleme-io/checkout"),
12561            Some("-upload-pack=evil"),
12562            Some("github:pleme-io/checkout?ref=main"),
12563            Some("github:pleme-io/checkout#main"),
12564            Some("github:pleme-io/{tpl}"),
12565        ] {
12566            let c = caixa_with_repositorio(repositorio);
12567            assert_eq!(
12568                c.repositorio(),
12569                repositorio,
12570                "Caixa::repositorio must return :repositorio verbatim \
12571                 (got {:?}, expected {repositorio:?})",
12572                c.repositorio(),
12573            );
12574            assert_eq!(
12575                c.repositorio(),
12576                c.repositorio.as_deref(),
12577                "Caixa::repositorio must byte-equal the raw \
12578                 `self.repositorio.as_deref()` field access across every \
12579                 value in the Option<&str> accept-set",
12580            );
12581        }
12582    }
12583
12584    #[test]
12585    fn validate_repositorio_empty_arm_routes_through_accessor() {
12586        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
12587        // gate must key off [`Caixa::repositorio`], not the raw
12588        // `self.repositorio.as_deref()` field access. Structurally: a
12589        // `Caixa { repositorio: Some(""), .. }` must surface the
12590        // `RepositorioEmpty` refusal exactly, and a
12591        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
12592        // (the canonical `github:` shorthand form) must pass validate.
12593        // The pair jointly pins the accessor + validate-gate
12594        // composition: any future silent detour that had the accessor
12595        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
12596        // collapse) would silently absorb the `RepositorioEmpty` refusal
12597        // at the accessor boundary and the validate gate would accept a
12598        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
12599        // composition pin catches that at caixa-core build time.
12600        //
12601        // Peer of the [`Caixa::licenca`] (6d5bc28)
12602        // `validate_licenca_empty_arm_routes_through_accessor`
12603        // composition pin on the sibling outer top-level [`Caixa`]
12604        // `Option<&str>` universal-axis surface — same "the validate /
12605        // shape-gate predicate must route through the substrate-
12606        // primitive typed dispatch" discipline extended onto the second
12607        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
12608        // composition surface.
12609        let c = caixa_with_repositorio(Some(""));
12610        assert!(
12611            matches!(
12612                c.validate_repositorio(),
12613                Err(ManifestError::RepositorioEmpty),
12614            ),
12615            "validate_repositorio must reject repositorio == Some(\"\") \
12616             with RepositorioEmpty — the accessor and the validate gate \
12617             must route through the same substrate-primitive typed \
12618             dispatch on the :repositorio empty arm",
12619        );
12620        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
12621        assert!(
12622            c.validate_repositorio().is_ok(),
12623            "validate_repositorio must accept repositorio == \
12624             Some(\"github:pleme-io/hello-rio\") (the canonical \
12625             `github:` shorthand git-repo-URL shape)",
12626        );
12627    }
12628
12629    #[test]
12630    fn repositorio_projects_option_str_by_borrow() {
12631        // The by-borrow pin: [`Caixa::repositorio`] returns
12632        // `Option<&str>` by borrow — the `&str` borrows the underlying
12633        // `String` storage of the `Option<String>` slot and the
12634        // accessor must not allocate a fresh `String` on every call.
12635        // Peer of the per-`:placement`
12636        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
12637        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
12638        // `Option<&str>`-return axes, extended onto the second outer
12639        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
12640        // the accessor's returned `&str` must borrow from `&self` (the
12641        // returned reference's lifetime is tied to `&self`), and
12642        // calling the accessor twice on the same [`Caixa`] must yield
12643        // the same `Option<&str>` verbatim (idempotent, no side effects
12644        // on `&self`).
12645        //
12646        // Pins against a future silent detour that returned an owned
12647        // `Option<String>` (which would type-check but silently
12648        // allocate on every call, breaking the zero-cost projection
12649        // every peer sibling accessor carries), or a one-arm-only
12650        // accessor that returned a saturating value on some sentinel
12651        // input (breaking the pass-through invariant the sibling
12652        // required-scalar accessors carry).
12653        for repositorio in [
12654            None,
12655            Some(""),
12656            Some("github:pleme-io/hello-rio"),
12657            Some("https://github.com/pleme-io/checkout"),
12658        ] {
12659            let c = caixa_with_repositorio(repositorio);
12660            let first = c.repositorio();
12661            let second = c.repositorio();
12662            assert_eq!(
12663                first, second,
12664                "Caixa::repositorio must be idempotent — two successive \
12665                 calls on the same &self must return the same \
12666                 Option<&str>",
12667            );
12668            assert_eq!(
12669                first, repositorio,
12670                "Caixa::repositorio must return :repositorio verbatim by \
12671                 borrow — got {first:?}, expected {repositorio:?}",
12672            );
12673        }
12674    }
12675
12676    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
12677
12678    #[test]
12679    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
12680        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
12681        // return the author-declared `:repositorio` byte-string verbatim
12682        // on the `Some` arm — no scheme rewrite, no trailing-slash
12683        // canonicalization, no `github:` → `https://github.com/`
12684        // desugaring. The resolved-URL composer is the projection of
12685        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
12686        // the `String`-return arity every substrate-side field-fill
12687        // consumer keys off; on the `Some` arm the projection is
12688        // `str::to_owned` verbatim, so every accept-set value the
12689        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
12690        // across_permutations` pin covers (`https://…`, `github:…`,
12691        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
12692        // guard sentinel `pleme-io/…`) must survive the accessor
12693        // byte-equal. Pins against a future silent detour that rewrote
12694        // the `github:` shorthand to the `https://github.com/` full URL
12695        // at the accessor boundary (which would silently split the
12696        // resolved-URL surface from the raw [`Caixa::repositorio`]
12697        // accessor's documented pass-through invariant), or a trailing-
12698        // slash normalization (which would silently break the
12699        // FluxCD `GitRepository` `spec.url` byte-exact match every
12700        // downstream consumer keys the source-controller reconcile off).
12701        for repositorio in [
12702            "github:pleme-io/hello-rio",
12703            "https://github.com/pleme-io/checkout",
12704            "ssh://git@github.com/pleme-io/checkout.git",
12705            "git://github.com/pleme-io/checkout.git",
12706            "git@github.com:pleme-io/checkout.git",
12707            "file:///opt/mirrors/pleme-io/checkout",
12708        ] {
12709            let c = caixa_with_repositorio(Some(repositorio));
12710            assert_eq!(
12711                c.canonical_git_url(),
12712                repositorio,
12713                "Caixa::canonical_git_url on the Some arm must return \
12714                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
12715                c.canonical_git_url(),
12716            );
12717        }
12718    }
12719
12720    #[test]
12721    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
12722        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
12723        // `None` arm must emit the substrate's canonical pleme-org github
12724        // URL derived from `caixa.nome()` — `https://github.com/<org>/
12725        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
12726        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
12727        // is the exact byte-image of the prior inline
12728        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
12729        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
12730        // re-derived open-coded. Pins against a future silent detour
12731        // that migrated the `<org>` segment to a different constant (a
12732        // fork rebranding that split off a new
12733        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
12734        // to migrate onto), a scheme change (`https://` → `git://` or
12735        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
12736        // override (which would break the substrate-wide single-source-
12737        // of-truth guarantee this method encodes).
12738        let c = caixa_with_repositorio(None);
12739        let expected = format!(
12740            "https://github.com/{org}/{nome}",
12741            org = crate::DEFAULT_PLEME_GIT_ORG,
12742            nome = c.nome(),
12743        );
12744        assert_eq!(
12745            c.canonical_git_url(),
12746            expected,
12747            "Caixa::canonical_git_url on the None arm must fold through \
12748             the substrate's canonical pleme-org github URL fallback \
12749             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
12750             {:?}, expected {expected:?}",
12751            c.canonical_git_url(),
12752        );
12753    }
12754
12755    #[test]
12756    fn canonical_git_url_byte_matches_manual_composition() {
12757        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
12758        // byte-identically to the manual open-coded
12759        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
12760        //  format!("https://github.com/{org}/{nome}", ...))` composition
12761        // every prior substrate-side caller re-derived. Guards the
12762        // paired-site convergence just applied at caixa-flux's
12763        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
12764        // now routes through this accessor): a future implementation of
12765        // this method that reordered the format arguments, swapped the
12766        // `<org>` constant for a different one, or interposed a
12767        // canonicalization pass on the `Some` arm surfaces here as a
12768        // caixa-core build-time test failure rather than as a downstream
12769        // FluxCD `GitRepository` reconcile mismatch far from this
12770        // method's source.
12771        for repositorio in [
12772            None,
12773            Some("github:pleme-io/hello-rio"),
12774            Some("https://github.com/pleme-io/checkout"),
12775            Some("ssh://git@github.com/pleme-io/checkout.git"),
12776        ] {
12777            let c = caixa_with_repositorio(repositorio);
12778            let manual = c.repositorio().map_or_else(
12779                || {
12780                    format!(
12781                        "https://github.com/{org}/{nome}",
12782                        org = crate::DEFAULT_PLEME_GIT_ORG,
12783                        nome = c.nome(),
12784                    )
12785                },
12786                str::to_owned,
12787            );
12788            assert_eq!(
12789                c.canonical_git_url(),
12790                manual,
12791                "Caixa::canonical_git_url must byte-equal the manual \
12792                 open-coded `repositorio().map(str::to_owned)\
12793                 .unwrap_or_else(|| format!(...))` composition across \
12794                 every representative :repositorio input — got {:?}, \
12795                 expected {manual:?}",
12796                c.canonical_git_url(),
12797            );
12798        }
12799    }
12800
12801    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
12802
12803    #[test]
12804    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
12805        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
12806        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
12807        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
12808        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
12809        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
12810        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
12811        // the `0.0.0` boundary case. Every accept-set value the peer
12812        // validate gate lets through must survive the resolved-tag
12813        // projection byte-equal.
12814        for versao in [
12815            "0.1.0",
12816            "0.0.0",
12817            "1.0.0",
12818            "1.2.3-rc.1",
12819            "1.2.3+build.42",
12820            "1.2.3-rc.1+build.42",
12821        ] {
12822            let c = caixa_with_versao(versao);
12823            let expected = format!(
12824                "{prefix}{versao}",
12825                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12826            );
12827            assert_eq!(
12828                c.publish_tag(),
12829                expected,
12830                "Caixa::publish_tag must compose \
12831                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
12832                 :versao ({versao:?}) verbatim — got {got:?}, \
12833                 expected {expected:?}",
12834                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12835                got = c.publish_tag(),
12836            );
12837        }
12838    }
12839
12840    #[test]
12841    fn publish_tag_starts_with_default_publish_tag_prefix() {
12842        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
12843        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
12844        // byte-string on every input, guarding a hypothetical future
12845        // implementation that migrated the prefix segment to an inline
12846        // literal (`"v"`) that would silently drift from any rebrand of
12847        // the lifted constant. Peer to the sibling caixa-flux
12848        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
12849        // test which pins the same prefix invariant at the reader-side
12850        // `GitRefSpec::Tag` emit site.
12851        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
12852            let c = caixa_with_versao(versao);
12853            let tag = c.publish_tag();
12854            assert!(
12855                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
12856                "Caixa::publish_tag emission {tag:?} must start with \
12857                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
12858                 ({prefix:?})",
12859                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12860            );
12861        }
12862    }
12863
12864    #[test]
12865    fn publish_tag_byte_matches_manual_composition() {
12866        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
12867        // identically to the manual open-coded
12868        // `format!("{prefix}{versao}", prefix =
12869        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
12870        //  caixa.versao())` composition every prior substrate-side
12871        // caller re-derived. Guards the paired-site convergence just
12872        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
12873        // `git_ref` composer (which now routes through this accessor):
12874        // a future implementation of this method that reordered the
12875        // format arguments, swapped the `<prefix>` constant for a
12876        // different one, or interposed a canonicalization pass on the
12877        // `:versao` axis surfaces here as a caixa-core build-time test
12878        // failure rather than as a downstream FluxCD `GitRepository`
12879        // reconcile mismatch far from this method's source.
12880        for versao in [
12881            "0.1.0",
12882            "0.0.0",
12883            "1.2.3-rc.1",
12884            "1.2.3+build.42",
12885            "1.2.3-rc.1+build.42",
12886        ] {
12887            let c = caixa_with_versao(versao);
12888            let manual = format!(
12889                "{prefix}{versao}",
12890                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
12891                versao = c.versao(),
12892            );
12893            assert_eq!(
12894                c.publish_tag(),
12895                manual,
12896                "Caixa::publish_tag must byte-equal the manual \
12897                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
12898                 composition across every representative :versao input \
12899                 — got {got:?}, expected {manual:?}",
12900                got = c.publish_tag(),
12901            );
12902        }
12903    }
12904
12905    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
12906
12907    #[test]
12908    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
12909        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
12910        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
12911        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
12912        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
12913        // set sweep documents — single-word, hyphen-joined, version-
12914        // suffixed, single-char, two-char, digit-start, retry-suffixed.
12915        // Every accept-set value the peer validate gate lets through must
12916        // survive the resolved-chart-name projection byte-equal.
12917        for nome in [
12918            "checkout",
12919            "cart-v2",
12920            "a",
12921            "db",
12922            "3rd-party-shim",
12923            "payment-retry",
12924            "0",
12925        ] {
12926            let c = caixa_with_nome(nome);
12927            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
12928            assert_eq!(
12929                c.lareira_chart_name(),
12930                expected,
12931                "Caixa::lareira_chart_name must compose \
12932                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
12933                 :nome ({nome:?}) verbatim — got {got:?}, \
12934                 expected {expected:?}",
12935                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
12936                got = c.lareira_chart_name(),
12937            );
12938        }
12939    }
12940
12941    #[test]
12942    fn lareira_chart_name_starts_with_lifted_prefix() {
12943        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
12944        // must begin with the canonical
12945        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
12946        // input, guarding a hypothetical future implementation that
12947        // migrated the prefix segment to an inline literal (`"lareira-"`)
12948        // that would silently drift from any rebrand of the lifted
12949        // constant. Peer to the sibling
12950        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
12951        // the co-resident resolved-publish-tag composer's prefix axis.
12952        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
12953            let c = caixa_with_nome(nome);
12954            let chart = c.lareira_chart_name();
12955            assert!(
12956                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
12957                "Caixa::lareira_chart_name emission {chart:?} must start \
12958                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
12959                 ({prefix:?})",
12960                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
12961            );
12962        }
12963    }
12964
12965    #[test]
12966    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
12967        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
12968        // byte-identically to the manual open-coded
12969        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
12970        // composition every prior substrate-side caller re-derived.
12971        // Guards the paired-site convergence just applied at caixa-helm's
12972        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
12973        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
12974        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
12975        // composer (all of which now route through this accessor): a
12976        // future implementation of this method that reordered the
12977        // composition arguments, swapped the `<prefix>` constant for a
12978        // different one, or interposed a canonicalization pass on the
12979        // `:nome` axis surfaces here as a caixa-core build-time test
12980        // failure rather than as a downstream Helm chart-render / FluxCD
12981        // reconcile / tatara Process-CR mismatch far from this method's
12982        // source.
12983        for nome in [
12984            "checkout",
12985            "cart-v2",
12986            "a",
12987            "db",
12988            "3rd-party-shim",
12989            "payment-retry",
12990        ] {
12991            let c = caixa_with_nome(nome);
12992            let manual = crate::lareira_chart_name(c.nome());
12993            assert_eq!(
12994                c.lareira_chart_name(),
12995                manual,
12996                "Caixa::lareira_chart_name must byte-equal the manual \
12997                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
12998                 composition across every representative :nome input — \
12999                 got {got:?}, expected {manual:?}",
13000                got = c.lareira_chart_name(),
13001            );
13002        }
13003    }
13004
13005    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
13006
13007    #[test]
13008    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
13009        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
13010        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
13011        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
13012        // across the full paired `(registry, :nome)` accept-set — every
13013        // representative registry the substrate-side emitters carry
13014        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
13015        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
13016        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
13017        // inline_format` render-side pin exercises; `registry.example.
13018        // com`, an off-org shape; `localhost:5000`, the local-dev shape
13019        // every `feira chart` iteration path lands under) × every DNS-
13020        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
13021        // forms` positive-set sweep documents (single-word, hyphen-
13022        // joined, single-char, two-char, digit-start, retry-suffixed).
13023        // Every accept-set pair the peer validate gates let through must
13024        // survive the resolved-OCI-ref projection byte-equal.
13025        for registry in [
13026            "ghcr.io/pleme-io/charts",
13027            "ghcr.io/pleme-io",
13028            "registry.example.com",
13029            "localhost:5000",
13030        ] {
13031            for nome in [
13032                "checkout",
13033                "cart-v2",
13034                "a",
13035                "db",
13036                "3rd-party-shim",
13037                "payment-retry",
13038                "0",
13039            ] {
13040                let c = caixa_with_nome(nome);
13041                let expected = format!(
13042                    "{scheme}{registry}/{chart}",
13043                    scheme = crate::OCI_SCHEME_PREFIX,
13044                    chart = crate::lareira_chart_name(nome),
13045                );
13046                assert_eq!(
13047                    c.oci_chart_ref(registry),
13048                    expected,
13049                    "Caixa::oci_chart_ref must compose \
13050                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
13051                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
13052                     expected {expected:?}",
13053                    scheme = crate::OCI_SCHEME_PREFIX,
13054                    got = c.oci_chart_ref(registry),
13055                );
13056            }
13057        }
13058    }
13059
13060    #[test]
13061    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
13062        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
13063        // emission must begin with the canonical
13064        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
13065        // a hypothetical future implementation that migrated the scheme
13066        // segment to an inline literal (`"oci://"`) that would silently
13067        // drift from any rebrand of the lifted constant. Peer to the
13068        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
13069        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
13070        // co-resident resolved-publish-tag / resolved-chart-name
13071        // composers' prefix axes.
13072        for registry in [
13073            "ghcr.io/pleme-io/charts",
13074            "ghcr.io/pleme-io",
13075            "localhost:5000",
13076        ] {
13077            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13078                let c = caixa_with_nome(nome);
13079                let ref_ = c.oci_chart_ref(registry);
13080                assert!(
13081                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
13082                    "Caixa::oci_chart_ref emission {ref_:?} must start \
13083                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
13084                     — registry ({registry:?}), :nome ({nome:?})",
13085                    scheme = crate::OCI_SCHEME_PREFIX,
13086                );
13087            }
13088        }
13089    }
13090
13091    #[test]
13092    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
13093        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
13094        // identically to the manual open-coded
13095        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
13096        // composition every prior substrate-side caller re-derived.
13097        // Guards the paired-site convergence just applied at caixa-
13098        // tatara's [`derive_chart_ref`] helper (which now routes through
13099        // this accessor): a future implementation of this method that
13100        // reordered the composition arguments, swapped the `<scheme>`
13101        // constant for a different one, migrated the `<chart>` segment
13102        // off the paired [`crate::lareira_chart_name`] composer, or
13103        // interposed a canonicalization pass on either input axis
13104        // surfaces here as a caixa-core build-time test failure rather
13105        // than as a downstream `helm install` / FluxCD OCI-source
13106        // reconcile / tatara `Process`-CR mismatch far from this
13107        // method's source. Sibling to the peer
13108        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
13109        // / [`publish_tag_byte_matches_manual_composition`] /
13110        // [`canonical_git_url_byte_matches_manual_composition`] byte-
13111        // parity pins that carry the same discipline on the co-resident
13112        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
13113        // composers.
13114        for registry in [
13115            "ghcr.io/pleme-io/charts",
13116            "ghcr.io/pleme-io",
13117            "registry.example.com",
13118            "localhost:5000",
13119        ] {
13120            for nome in [
13121                "checkout",
13122                "cart-v2",
13123                "a",
13124                "db",
13125                "3rd-party-shim",
13126                "payment-retry",
13127            ] {
13128                let c = caixa_with_nome(nome);
13129                let manual = crate::oci_chart_ref(registry, c.nome());
13130                assert_eq!(
13131                    c.oci_chart_ref(registry),
13132                    manual,
13133                    "Caixa::oci_chart_ref must byte-equal the manual \
13134                     open-coded `caixa_core::oci_chart_ref(registry, \
13135                     caixa.nome())` composition across every representative \
13136                     (registry, :nome) pair — registry ({registry:?}), \
13137                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
13138                    got = c.oci_chart_ref(registry),
13139                );
13140            }
13141        }
13142    }
13143
13144    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
13145
13146    #[test]
13147    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
13148        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
13149        // pin: [`Caixa::descricao`] must return the `:descricao` typed
13150        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13151        // raw `self.descricao.as_deref()` access across every
13152        // representative value in the accept-set — `None` (the "omit
13153        // the slot to defer to the per-renderer `caixa.nome`-derived
13154        // fallback" arm every existing fixture without a `:descricao`
13155        // line carries), `Some("")` (a past-the-guard sentinel that
13156        // pins the accessor doesn't perform a silent `Some("") → None`
13157        // collapse on the empty arm — validate rejects `Some("")`
13158        // through `DescricaoEmpty` but the accessor must ship the raw
13159        // slot verbatim so a validate-time gate regression surfaces at
13160        // the caixa-helm / caixa-feira emit boundary rather than being
13161        // silently absorbed into the per-renderer `caixa.nome`-derived
13162        // fallback), `Some("Checkout flow.")` (the canonical one-line
13163        // prose descriptor the peer
13164        // `validate_descricao_accepts_canonical_value` positive sweep
13165        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
13166        // Servico.")` (the multi-byte Unicode continuation-byte shape
13167        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
13168        // multi-glyph Unicode shape the peer
13169        // `is_chart_description_shape` predicate accepts), and five
13170        // past-the-guard sentinels for the `DescricaoInvalid` refusal
13171        // cases (`Some(" Checkout flow.")` leading-whitespace,
13172        // `Some("Checkout flow. ")` trailing-whitespace,
13173        // `Some("Checkout\nflow.")` embedded-LF,
13174        // `Some("Checkout\tflow.")` embedded-TAB, and
13175        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
13176        // the accessor doesn't silently absorb the refusal cases into
13177        // a fallback).
13178        //
13179        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
13180        // accessor pin on the substrate primitive — sibling of the peer
13181        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
13182        // (cc7332d) pins that opened the "outer [`Caixa`]
13183        // `Option<&str>` scalar" projection pin pattern this pin folds
13184        // on. Sibling in shape to the peer per-`:placement`
13185        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13186        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13187        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13188        // axes, extended onto the outer top-level [`Caixa`] universal-
13189        // axis surface. Pins against a future silent detour that
13190        // returned an owned `Option<String>` (which would type-check
13191        // but silently allocate on every accessor call, breaking the
13192        // zero-cost projection every peer sibling accessor carries), a
13193        // `Some("") → None` collapse (which would silently absorb the
13194        // `DescricaoEmpty` refusal case at the accessor boundary and
13195        // the caixa-helm `Chart.yaml` `description:` fold would
13196        // silently render a `caixa.nome`-derived fallback on a
13197        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
13198        // `None → Some(<default>)` collapse (which would silently
13199        // reify the per-renderer `caixa.nome`-derived fallback at the
13200        // accessor boundary and every downstream consumer keying off
13201        // the `Option::is_none()` discriminator would lose the "author
13202        // omitted the slot" signal).
13203        for descricao in [
13204            None,
13205            Some(""),
13206            Some("Checkout flow."),
13207            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13208            Some("→ — · ✓"),
13209            Some(" Checkout flow."),
13210            Some("Checkout flow. "),
13211            Some("Checkout\nflow."),
13212            Some("Checkout\tflow."),
13213            Some("Checkout\x00flow."),
13214        ] {
13215            let c = caixa_with_descricao(descricao);
13216            assert_eq!(
13217                c.descricao(),
13218                descricao,
13219                "Caixa::descricao must return :descricao verbatim (got \
13220                 {:?}, expected {descricao:?})",
13221                c.descricao(),
13222            );
13223            assert_eq!(
13224                c.descricao(),
13225                c.descricao.as_deref(),
13226                "Caixa::descricao must byte-equal the raw \
13227                 `self.descricao.as_deref()` field access across every \
13228                 value in the Option<&str> accept-set",
13229            );
13230        }
13231    }
13232
13233    #[test]
13234    fn validate_descricao_empty_arm_routes_through_accessor() {
13235        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
13236        // gate must key off [`Caixa::descricao`], not the raw
13237        // `self.descricao.as_deref()` field access. Structurally: a
13238        // `Caixa { descricao: Some(""), .. }` must surface the
13239        // `DescricaoEmpty` refusal exactly, and a
13240        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
13241        // canonical one-line-prose form) must pass validate. The pair
13242        // jointly pins the accessor + validate-gate composition: any
13243        // future silent detour that had the accessor return `None` on
13244        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
13245        // silently absorb the `DescricaoEmpty` refusal at the accessor
13246        // boundary and the validate gate would accept a struct-literal
13247        // `Caixa { descricao: Some(""), .. }` — the composition pin
13248        // catches that at caixa-core build time.
13249        //
13250        // Peer of the [`Caixa::licenca`] (6d5bc28)
13251        // `validate_licenca_empty_arm_routes_through_accessor` and
13252        // [`Caixa::repositorio`] (cc7332d)
13253        // `validate_repositorio_empty_arm_routes_through_accessor`
13254        // composition pins on the sibling outer top-level [`Caixa`]
13255        // `Option<&str>` universal-axis surface — same "the validate /
13256        // shape-gate predicate must route through the substrate-
13257        // primitive typed dispatch" discipline extended onto the third
13258        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
13259        // composition surface.
13260        let c = caixa_with_descricao(Some(""));
13261        assert!(
13262            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
13263            "validate_descricao must reject descricao == Some(\"\") \
13264             with DescricaoEmpty — the accessor and the validate gate \
13265             must route through the same substrate-primitive typed \
13266             dispatch on the :descricao empty arm",
13267        );
13268        let c = caixa_with_descricao(Some("Checkout flow."));
13269        assert!(
13270            c.validate_descricao().is_ok(),
13271            "validate_descricao must accept descricao == \
13272             Some(\"Checkout flow.\") (the canonical one-line-prose \
13273             chart-description shape)",
13274        );
13275    }
13276
13277    #[test]
13278    fn descricao_projects_option_str_by_borrow() {
13279        // The by-borrow pin: [`Caixa::descricao`] returns
13280        // `Option<&str>` by borrow — the `&str` borrows the underlying
13281        // `String` storage of the `Option<String>` slot and the
13282        // accessor must not allocate a fresh `String` on every call.
13283        // Peer of the [`Caixa::licenca`] (6d5bc28) and
13284        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
13285        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
13286        // the per-`:placement`
13287        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13288        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13289        // return axis, extended onto the third outer top-level
13290        // [`Caixa`] universal-axis `Option<&str>` shape — the
13291        // accessor's returned `&str` must borrow from `&self` (the
13292        // returned reference's lifetime is tied to `&self`), and
13293        // calling the accessor twice on the same [`Caixa`] must yield
13294        // the same `Option<&str>` verbatim (idempotent, no side
13295        // effects on `&self`).
13296        //
13297        // Pins against a future silent detour that returned an owned
13298        // `Option<String>` (which would type-check but silently
13299        // allocate on every call, breaking the zero-cost projection
13300        // every peer sibling accessor carries), or a one-arm-only
13301        // accessor that returned a saturating value on some sentinel
13302        // input (breaking the pass-through invariant the sibling
13303        // required-scalar accessors carry).
13304        for descricao in [
13305            None,
13306            Some(""),
13307            Some("Checkout flow."),
13308            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13309        ] {
13310            let c = caixa_with_descricao(descricao);
13311            let first = c.descricao();
13312            let second = c.descricao();
13313            assert_eq!(
13314                first, second,
13315                "Caixa::descricao must be idempotent — two successive \
13316                 calls on the same &self must return the same \
13317                 Option<&str>",
13318            );
13319            assert_eq!(
13320                first, descricao,
13321                "Caixa::descricao must return :descricao verbatim by \
13322                 borrow — got {first:?}, expected {descricao:?}",
13323            );
13324        }
13325    }
13326
13327    // ── validate_edicao — universal-axis language-edition shape ──
13328
13329    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
13330        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13331        c.edicao = edicao.map(String::from);
13332        c
13333    }
13334
13335    #[test]
13336    fn validate_edicao_accepts_none() {
13337        // The omit-the-slot identity: `:edicao` is optional. The
13338        // gate is a no-op when the author didn't declare a value —
13339        // every caixa without an `:edicao` line trivially passes,
13340        // and the substrate-side build pipeline falls back to the
13341        // documented default edition. Mirrors the peer
13342        // `validate_licenca_accepts_none` posture on the sibling
13343        // `Option<String>` Caixa slot.
13344        let c = caixa_with_edicao(None);
13345        c.validate_edicao().unwrap();
13346    }
13347
13348    #[test]
13349    fn validate_edicao_accepts_canonical_value() {
13350        // Positive control: the canonical `"2026"` edition every
13351        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
13352        // `caixa-mesh`) carries by construction passes the gate.
13353        // Future-introduced sibling editions (`"2027"`, `"2030"`,
13354        // `"2049"`) that match the same 4-digit ASCII decimal year
13355        // shape must also trivially pass — the structural shape
13356        // predicate accepts every well-formed year regardless of
13357        // whether the substrate yet understands the specific value
13358        // (a future known-edition allowlist tightens that).
13359        for ed in ["2026", "2027", "2030", "2049"] {
13360            let c = caixa_with_edicao(Some(ed));
13361            c.validate_edicao()
13362                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
13363        }
13364    }
13365
13366    #[test]
13367    fn validate_edicao_rejects_empty_some() {
13368        // Canonical paste-from-blank-doc footgun. Without this gate
13369        // the empty `Some("")` silently lands as `(:edicao "")` in
13370        // the rendered caixa.lisp and a future renderer-side
13371        // consumer's `Option::unwrap_or_else` (which only fires on
13372        // `None`) skips its fallback. Mirrors the peer
13373        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
13374        // `Option<String>` Caixa slot.
13375        let c = caixa_with_edicao(Some(""));
13376        let err = c.validate_edicao().unwrap_err();
13377        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13378    }
13379
13380    #[test]
13381    fn validate_edicao_rejects_free_form_non_year() {
13382        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
13383        // `"nightly"` shapes carry no operational meaning on the
13384        // substrate's build-time edition selector. Until this gate
13385        // landed the bare empty-arm check let every such value
13386        // through and broke far from the source caixa.lisp. Peer
13387        // with the shape-predicate cascade
13388        // `validate_repositorio_rejects_missing_colon_separator`
13389        // establishes past its own empty arm.
13390        for ed in ["x", "latest", "nightly", "stable"] {
13391            let c = caixa_with_edicao(Some(ed));
13392            let err = c.validate_edicao().unwrap_err();
13393            assert!(
13394                matches!(err, ManifestError::EdicaoInvalid { .. }),
13395                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13396            );
13397        }
13398    }
13399
13400    #[test]
13401    fn validate_edicao_rejects_trailing_whitespace() {
13402        // Paste-from-doc whitespace footgun. A trailing space in
13403        // the `:edicao` value would silently break the substrate's
13404        // build-time edition match-table lookup at the rendered
13405        // artifact's edition-selector consumer. The shape predicate
13406        // refuses every whitespace byte by construction (any byte
13407        // outside `0-9` fails `is_ascii_digit`). Peer with
13408        // `validate_repositorio_rejects_whitespace`.
13409        let c = caixa_with_edicao(Some("2026 "));
13410        let err = c.validate_edicao().unwrap_err();
13411        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
13412            panic!("expected EdicaoInvalid, got {err:?}");
13413        };
13414        assert_eq!(edicao, "2026 ");
13415    }
13416
13417    #[test]
13418    fn validate_edicao_rejects_leading_whitespace() {
13419        // Symmetric paste-from-doc whitespace footgun on the leading
13420        // boundary — the gate refuses every shape with a non-digit
13421        // byte by construction.
13422        let c = caixa_with_edicao(Some(" 2026"));
13423        let err = c.validate_edicao().unwrap_err();
13424        assert!(
13425            matches!(err, ManifestError::EdicaoInvalid { .. }),
13426            "got {err:?}",
13427        );
13428    }
13429
13430    #[test]
13431    fn validate_edicao_rejects_control_char() {
13432        // Paste-from-multiline-doc CRLF footgun — control characters
13433        // at the value boundary break the substrate's build-time
13434        // edition-selector parser. Peer with
13435        // `validate_repositorio_rejects_control_char`.
13436        let c = caixa_with_edicao(Some("2026\n"));
13437        let err = c.validate_edicao().unwrap_err();
13438        assert!(
13439            matches!(err, ManifestError::EdicaoInvalid { .. }),
13440            "got {err:?}",
13441        );
13442    }
13443
13444    #[test]
13445    fn validate_edicao_rejects_non_ascii_lookalike() {
13446        // Fullwidth-keyboard look-alike footgun — `"2026"` is
13447        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
13448        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
13449        // edition selector wants an ASCII year, and the gate
13450        // refuses every non-ASCII shape by construction (length in
13451        // bytes is 12 ≠ 4, *and* every byte falls outside
13452        // `is_ascii_digit`'s `0-9` range).
13453        let c = caixa_with_edicao(Some("2026"));
13454        let err = c.validate_edicao().unwrap_err();
13455        assert!(
13456            matches!(err, ManifestError::EdicaoInvalid { .. }),
13457            "got {err:?}",
13458        );
13459    }
13460
13461    #[test]
13462    fn validate_edicao_rejects_version_tag_prefix() {
13463        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
13464        // / `"r2026"` are familiar shapes from git-tag / Rust
13465        // edition / release-tag conventions that don't apply to
13466        // the year-shaped edition axis. The shape predicate refuses
13467        // every leading non-digit prefix.
13468        for ed in ["v2026", "e2026", "r2026"] {
13469            let c = caixa_with_edicao(Some(ed));
13470            let err = c.validate_edicao().unwrap_err();
13471            assert!(
13472                matches!(err, ManifestError::EdicaoInvalid { .. }),
13473                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13474            );
13475        }
13476    }
13477
13478    #[test]
13479    fn validate_edicao_rejects_decimal_shape() {
13480        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
13481        // `"2026.0"` are familiar shapes from semver / float
13482        // conventions that don't apply to the year-shaped edition
13483        // axis. The shape predicate refuses every non-digit byte
13484        // (`.` falls outside `is_ascii_digit`).
13485        for ed in ["2026.1", "2026.0", "2026.0.1"] {
13486            let c = caixa_with_edicao(Some(ed));
13487            let err = c.validate_edicao().unwrap_err();
13488            assert!(
13489                matches!(err, ManifestError::EdicaoInvalid { .. }),
13490                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13491            );
13492        }
13493    }
13494
13495    #[test]
13496    fn validate_edicao_rejects_wrong_length_numeric() {
13497        // Wrong-length numeric footgun — `"26"` (truncated) /
13498        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
13499        // (zero-padded too wide) all parse as integers but don't
13500        // name a 4-digit year. The shape predicate refuses every
13501        // value whose length isn't exactly 4 bytes.
13502        for ed in ["26", "202", "20260", "00026", "9"] {
13503            let c = caixa_with_edicao(Some(ed));
13504            let err = c.validate_edicao().unwrap_err();
13505            assert!(
13506                matches!(err, ManifestError::EdicaoInvalid { .. }),
13507                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13508            );
13509        }
13510    }
13511
13512    #[test]
13513    fn validate_edicao_empty_takes_precedence_over_shape() {
13514        // Empty-first cascade pin: the empty `Some("")` surfaces
13515        // the narrower `EdicaoEmpty` not the shape-predicate-
13516        // wrapped `EdicaoInvalid`, mirroring the peer
13517        // `validate_repositorio_empty_takes_precedence_over_shape`
13518        // (`RepositorioEmpty` → `RepositorioInvalid`),
13519        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
13520        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
13521        // cascades. The shape predicate also refuses the empty
13522        // input (defensively — `s.len() != 4`), but the
13523        // manifest-layer empty arm runs first to surface the
13524        // narrower diagnostic verbatim.
13525        let c = caixa_with_edicao(Some(""));
13526        let err = c.validate_edicao().unwrap_err();
13527        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13528    }
13529
13530    #[test]
13531    fn validate_edicao_template_passes() {
13532        // Round-trip pin: the bare `Caixa::template` shape (which
13533        // carries `:edicao "2026"` verbatim) passes the gate by
13534        // construction. A future template-shape change that
13535        // introduced `(:edicao "")` or a non-year value would
13536        // surface here as a regression. Mirrors the peer
13537        // `validate_licenca_template_passes` pin.
13538        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13539        c.validate_edicao().unwrap();
13540    }
13541
13542    #[test]
13543    fn validate_edicao_diagnostic_names_offending_slot() {
13544        // Diagnostic-shape pin (peer with
13545        // `validate_licenca_diagnostic_names_offending_slot`): the
13546        // error's Display surfaces the `:edicao` slot name verbatim,
13547        // so a `feira lint` run can render the diagnostic without
13548        // re-parsing and the author can grep their caixa.lisp for
13549        // the offending `:edicao` line.
13550        let c = caixa_with_edicao(Some(""));
13551        let rendered = c.validate_edicao().unwrap_err().to_string();
13552        assert!(
13553            rendered.contains(":edicao"),
13554            "diagnostic must name the offending slot: {rendered}",
13555        );
13556    }
13557
13558    #[test]
13559    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
13560        // Diagnostic-shape pin on the shape-predicate arm (peer
13561        // with `validate_repositorio_diagnostic_carries_offending_value`):
13562        // the error's Display surfaces the offending value + slot
13563        // name verbatim, so a `feira lint` run can render the
13564        // diagnostic without re-parsing and the author can grep
13565        // their caixa.lisp for the offending `:edicao` value.
13566        let c = caixa_with_edicao(Some("v2026"));
13567        let rendered = c.validate_edicao().unwrap_err().to_string();
13568        assert!(
13569            rendered.contains(":edicao"),
13570            "diagnostic must name the offending slot: {rendered}",
13571        );
13572        assert!(
13573            rendered.contains("v2026"),
13574            "diagnostic must quote the offending value: {rendered}",
13575        );
13576    }
13577
13578    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
13579
13580    #[test]
13581    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
13582        // The canonical per-`Caixa` `:edicao` language-edition scalar
13583        // pin: [`Caixa::edicao`] must return the `:edicao` typed
13584        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13585        // raw `self.edicao.as_deref()` access across every representative
13586        // value in the accept-set — `None` (the "omit the slot to defer
13587        // to the substrate's default edition" arm every existing
13588        // [`caixa-resolver`] fixture without an `:edicao` line carries),
13589        // `Some("")` (a past-the-guard sentinel that pins the accessor
13590        // doesn't perform a silent `Some("") → None` collapse on the
13591        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
13592        // but the accessor must ship the raw slot verbatim so a
13593        // validate-time gate regression surfaces at any future edition-
13594        // aware consumer's boundary rather than being silently absorbed
13595        // into the substrate's default edition), `Some("2026")` (the
13596        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
13597        // template scaffolds via [`Caixa::template`] and every
13598        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
13599        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
13600        // carries by construction), `Some("2018")` / `Some("2021")` /
13601        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
13602        // peer with Cargo's `[package] edition` grammar every future-
13603        // introduced sibling to `"2026"` will follow), and eight
13604        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
13605        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
13606        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
13607        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
13608        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
13609        // length-numeric, `Some("latest")` free-form-non-year — the
13610        // sentinels pin the accessor doesn't silently absorb the
13611        // refusal cases into a substrate-default-edition fallback).
13612        //
13613        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
13614        // return scalar accessor pin on the substrate primitive —
13615        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
13616        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13617        // (3f16e2f) pins that opened the "outer [`Caixa`]
13618        // `Option<&str>` scalar" projection pin pattern this pin folds
13619        // on. Sibling in shape to the peer per-`:placement`
13620        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13621        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13622        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13623        // axes, extended onto the outer top-level [`Caixa`] universal-
13624        // axis surface's last unlifted `Option<String>` slot. Pins
13625        // against a future silent detour that returned an owned
13626        // `Option<String>` (which would type-check but silently
13627        // allocate on every accessor call, breaking the zero-cost
13628        // projection every peer sibling accessor carries), a
13629        // `Some("") → None` collapse (which would silently absorb the
13630        // `EdicaoEmpty` refusal case at the accessor boundary and any
13631        // future edition-aware consumer would silently fall back to
13632        // the substrate's default edition on a struct-literal
13633        // `Caixa { edicao: Some(""), .. }`), or a
13634        // `None → Some("2026")` collapse (which would silently reify
13635        // the substrate's default edition at the accessor boundary
13636        // and every downstream consumer keying off the
13637        // `Option::is_none()` discriminator would lose the "author
13638        // omitted the slot" signal).
13639        for edicao in [
13640            None,
13641            Some(""),
13642            Some("2026"),
13643            Some("2018"),
13644            Some("2021"),
13645            Some("2024"),
13646            Some("2026 "),
13647            Some(" 2026"),
13648            Some("2026\n"),
13649            Some("2026"),
13650            Some("v2026"),
13651            Some("2026.1"),
13652            Some("26"),
13653            Some("latest"),
13654        ] {
13655            let c = caixa_with_edicao(edicao);
13656            assert_eq!(
13657                c.edicao(),
13658                edicao,
13659                "Caixa::edicao must return :edicao verbatim (got {:?}, \
13660                 expected {edicao:?})",
13661                c.edicao(),
13662            );
13663            assert_eq!(
13664                c.edicao(),
13665                c.edicao.as_deref(),
13666                "Caixa::edicao must byte-equal the raw \
13667                 `self.edicao.as_deref()` field access across every \
13668                 value in the Option<&str> accept-set",
13669            );
13670        }
13671    }
13672
13673    #[test]
13674    fn validate_edicao_empty_arm_routes_through_accessor() {
13675        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
13676        // must key off [`Caixa::edicao`], not the raw
13677        // `self.edicao.as_deref()` field access. Structurally: a
13678        // `Caixa { edicao: Some(""), .. }` must surface the
13679        // `EdicaoEmpty` refusal exactly, and a
13680        // `Caixa { edicao: Some("2026"), .. }` (the canonical
13681        // 4-digit-ASCII-decimal-year form) must pass validate. The
13682        // pair jointly pins the accessor + validate-gate composition:
13683        // any future silent detour that had the accessor return `None`
13684        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
13685        // would silently absorb the `EdicaoEmpty` refusal at the
13686        // accessor boundary and the validate gate would accept a
13687        // struct-literal `Caixa { edicao: Some(""), .. }` — the
13688        // composition pin catches that at caixa-core build time.
13689        //
13690        // Peer of the [`Caixa::licenca`] (6d5bc28)
13691        // `validate_licenca_empty_arm_routes_through_accessor`,
13692        // [`Caixa::repositorio`] (cc7332d)
13693        // `validate_repositorio_empty_arm_routes_through_accessor`,
13694        // and [`Caixa::descricao`] (3f16e2f)
13695        // `validate_descricao_empty_arm_routes_through_accessor`
13696        // composition pins on the sibling outer top-level [`Caixa`]
13697        // `Option<&str>` universal-axis surface — same "the validate /
13698        // shape-gate predicate must route through the substrate-
13699        // primitive typed dispatch" discipline extended onto the
13700        // fourth and final outer top-level [`Caixa`] universal-axis
13701        // `Option<&str>`-composition surface, closing the accessor-
13702        // composition family.
13703        let c = caixa_with_edicao(Some(""));
13704        assert!(
13705            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
13706            "validate_edicao must reject edicao == Some(\"\") with \
13707             EdicaoEmpty — the accessor and the validate gate must \
13708             route through the same substrate-primitive typed dispatch \
13709             on the :edicao empty arm",
13710        );
13711        let c = caixa_with_edicao(Some("2026"));
13712        assert!(
13713            c.validate_edicao().is_ok(),
13714            "validate_edicao must accept edicao == Some(\"2026\") \
13715             (the canonical 4-digit-ASCII-decimal-year shape)",
13716        );
13717    }
13718
13719    #[test]
13720    fn edicao_projects_option_str_by_borrow() {
13721        // The by-borrow pin: [`Caixa::edicao`] returns
13722        // `Option<&str>` by borrow — the `&str` borrows the underlying
13723        // `String` storage of the `Option<String>` slot and the
13724        // accessor must not allocate a fresh `String` on every call.
13725        // Peer of the [`Caixa::licenca`] (6d5bc28),
13726        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13727        // (3f16e2f) by-borrow pins on the peer outer top-level
13728        // [`Caixa`] `Option<&str>`-return axes, and of the
13729        // per-`:placement`
13730        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13731        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13732        // return axis, extended onto the fourth and final outer top-
13733        // level [`Caixa`] universal-axis `Option<&str>` shape — the
13734        // accessor's returned `&str` must borrow from `&self` (the
13735        // returned reference's lifetime is tied to `&self`), and
13736        // calling the accessor twice on the same [`Caixa`] must yield
13737        // the same `Option<&str>` verbatim (idempotent, no side
13738        // effects on `&self`).
13739        //
13740        // Pins against a future silent detour that returned an owned
13741        // `Option<String>` (which would type-check but silently
13742        // allocate on every call, breaking the zero-cost projection
13743        // every peer sibling accessor carries), or a one-arm-only
13744        // accessor that returned a saturating value on some sentinel
13745        // input (breaking the pass-through invariant the sibling
13746        // required-scalar accessors carry).
13747        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
13748            let c = caixa_with_edicao(edicao);
13749            let first = c.edicao();
13750            let second = c.edicao();
13751            assert_eq!(
13752                first, second,
13753                "Caixa::edicao must be idempotent — two successive \
13754                 calls on the same &self must return the same \
13755                 Option<&str>",
13756            );
13757            assert_eq!(
13758                first, edicao,
13759                "Caixa::edicao must return :edicao verbatim by \
13760                 borrow — got {first:?}, expected {edicao:?}",
13761            );
13762        }
13763    }
13764
13765    #[test]
13766    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
13767        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
13768        // label caixa-identity scalar pin: [`Caixa::nome`] must return
13769        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
13770        // the raw field access across every representative value in
13771        // the accept-set — the canonical `"demo"` template baseline
13772        // (the same `feira init`-scaffolded default the sibling
13773        // `validate_nome_accepts_canonical_template` positive-control
13774        // gate pins), plus every sibling per-typed-slot atom accessor's
13775        // canonical positive-arm byte-string (`"catalog"` per
13776        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
13777        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
13778        // `caixa-helm`/`caixa-flux` cross-crate integration-test
13779        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
13780        // canonical example), plus every past-the-guard sentinel for
13781        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
13782        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
13783        // the bare DNS-1123 63-byte cap but overflows the joint
13784        // `lareira-<nome>` chart-name budget the sibling
13785        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
13786        //
13787        // The past-the-guard sentinels pin the accessor doesn't
13788        // silently absorb the refusal cases into a template-derived
13789        // fallback (a future `.nome().is_empty().then(|| "demo")`
13790        // collapse would silently absorb the `NomeEmpty` refusal at
13791        // the accessor boundary and the validate gate would accept a
13792        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
13793        // catches that at caixa-core build time).
13794        //
13795        // First outer top-level [`Caixa`] `&str`-return required-
13796        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
13797        // required-scalar" projection pattern the sibling per-`Caixa`
13798        // `:versao` future lift folds on. Sibling in shape to the peer
13799        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
13800        // required-`String`-carry accessor pin on the sibling per-
13801        // sub-struct required-axis, extended onto the outer top-level
13802        // [`Caixa`] universal-axis required-`String`-carry axis.
13803        for nome in [
13804            "demo",
13805            "catalog",
13806            "cart",
13807            "hello-rio",
13808            "checkout",
13809            "",
13810            "Bad_Name",
13811            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
13812        ] {
13813            let c = caixa_with_nome(nome);
13814            assert_eq!(
13815                c.nome(),
13816                nome,
13817                "Caixa::nome must return :nome verbatim (got {}, \
13818                 expected {nome})",
13819                c.nome(),
13820            );
13821            assert_eq!(
13822                c.nome(),
13823                c.nome.as_str(),
13824                "Caixa::nome must byte-equal the raw .nome field \
13825                 access across every value in the String accept-set",
13826            );
13827        }
13828    }
13829
13830    #[test]
13831    fn validate_nome_empty_arm_routes_through_accessor() {
13832        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
13833        // key off [`Caixa::nome`], not the raw `.nome` field access.
13834        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
13835        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
13836        // template baseline (the peer positive-arm the sibling
13837        // `validate_nome_accepts_canonical_template` gate carves out)
13838        // must pass validate. The pair jointly pins the accessor +
13839        // validate-gate composition: any future silent detour that
13840        // had the accessor return a fresh `"demo"` on the empty arm
13841        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
13842        // would silently absorb the `NomeEmpty` refusal at the
13843        // accessor boundary and the validate gate would accept a
13844        // struct-literal `Caixa { nome: "".into(), .. }` — the
13845        // composition pin catches that at caixa-core build time.
13846        //
13847        // Peer of the sibling per-`Caixa`
13848        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
13849        // / `validate_repositorio_empty_arm_routes_through_accessor`
13850        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
13851        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
13852        // (2641cbd) composition pins on the sibling outer top-level
13853        // [`Caixa`] `Option<&str>` axes — same "the validate /
13854        // shape-gate predicate must route through the substrate-
13855        // primitive typed dispatch" discipline extended onto the peer
13856        // outer top-level [`Caixa`] required-`&str` composition axis.
13857        let c = caixa_with_nome("");
13858        assert!(
13859            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
13860            "validate_nome must reject nome == \"\" with NomeEmpty — \
13861             the accessor and the validate gate must route through the \
13862             same substrate-primitive typed dispatch on the :nome \
13863             empty-arm",
13864        );
13865        let c = caixa_with_nome("demo");
13866        assert!(
13867            c.validate_nome().is_ok(),
13868            "validate_nome must accept nome == \"demo\" (the canonical \
13869             DNS-1123-label template baseline)",
13870        );
13871    }
13872
13873    #[test]
13874    fn nome_projects_str_by_borrow() {
13875        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
13876        // — the `&str` borrows the underlying `String` storage of the
13877        // required `nome` slot and the accessor must not allocate a
13878        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
13879        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
13880        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
13881        // by-borrow pins on the peer outer top-level [`Caixa`]
13882        // `Option<&str>`-return axes, extended onto the first outer
13883        // top-level [`Caixa`] required-`&str`-return axis — the
13884        // accessor's returned `&str` must borrow from `&self` (the
13885        // returned reference's lifetime is tied to `&self`), and
13886        // calling the accessor twice on the same [`Caixa`] must yield
13887        // the same `&str` verbatim (idempotent, no side effects on
13888        // `&self`).
13889        //
13890        // Pins against a future silent detour that returned an owned
13891        // `String` (which would type-check but silently allocate on
13892        // every call, breaking the zero-cost projection every peer
13893        // sibling accessor carries), an accidental
13894        // `.nome.to_lowercase()` detour that returned a fresh
13895        // allocation through an already-DNS-1123-lowercase-only
13896        // string (breaking a future `const fn` regression), or a
13897        // one-arm-only accessor that returned a canonicalized value
13898        // on some sentinel input (breaking the pass-through invariant
13899        // the sibling required-scalar accessors carry).
13900        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
13901            let c = caixa_with_nome(nome);
13902            let first = c.nome();
13903            let second = c.nome();
13904            assert_eq!(
13905                first, second,
13906                "Caixa::nome must be idempotent — two successive calls \
13907                 on the same &self must return the same &str",
13908            );
13909            assert_eq!(
13910                first, nome,
13911                "Caixa::nome must return :nome verbatim by borrow — \
13912                 got {first}, expected {nome}",
13913            );
13914        }
13915    }
13916
13917    #[test]
13918    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
13919        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
13920        // pinned-version scalar pin: [`Caixa::versao`] must return the
13921        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
13922        // raw `.versao` field access across every representative value
13923        // in the accept-set — the canonical `"0.1.0"` template baseline
13924        // (the same `feira init`-scaffolded default the sibling
13925        // `validate_versao_accepts_canonical_template` positive-control
13926        // gate pins), plus every canonical SemVer-2 shape the sibling
13927        // `validate_versao_accepts_canonical_forms` positive-arm sweep
13928        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
13929        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
13930        // `"10.20.30"`), plus every past-the-guard sentinel for the
13931        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
13932        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
13933        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
13934        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
13935        // `"latest"` the docker-tag-shape footgun — the sentinels pin
13936        // the accessor doesn't silently absorb the refusal cases into a
13937        // template-derived fallback like `"0.1.0"`).
13938        //
13939        // The past-the-guard sentinels pin the accessor doesn't silently
13940        // absorb the refusal cases into a template-derived fallback (a
13941        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
13942        // silently absorb the `VersaoEmpty` refusal at the accessor
13943        // boundary and the validate gate would accept a struct-literal
13944        // `Caixa { versao: "".into(), .. }` — the pin catches that at
13945        // caixa-core build time).
13946        //
13947        // Second outer top-level [`Caixa`] `&str`-return required-scalar
13948        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
13949        // scalar" projection pattern the sibling per-`Caixa`
13950        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
13951        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
13952        // (4127bb6) / per-`:children`
13953        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
13954        // / per-`:upgrade-from`
13955        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
13956        // struct `:versao`-shaped `&str`-return accessor pins on the
13957        // sibling per-typed-slot version-carrier axes, extended onto the
13958        // second outer top-level [`Caixa`] universal-axis required-
13959        // `String`-carry axis so the two universal-axis identity-
13960        // carrying scalars every `defcaixa` form supplies (`:nome` +
13961        // `:versao`) share the same "one typed dispatch per axis" pin
13962        // discipline.
13963        for versao in [
13964            "0.1.0",
13965            "0.0.0",
13966            "1.0.0",
13967            "0.2.0-rc.1",
13968            "1.0.0-alpha.0",
13969            "1.0.0+build.42",
13970            "1.0.0-rc.1+build.42",
13971            "10.20.30",
13972            "",
13973            "v0.1.0",
13974            "0.1",
13975            "^0.1",
13976            "0.1.0.0",
13977            "latest",
13978        ] {
13979            let c = caixa_with_versao(versao);
13980            assert_eq!(
13981                c.versao(),
13982                versao,
13983                "Caixa::versao must return :versao verbatim (got {}, \
13984                 expected {versao})",
13985                c.versao(),
13986            );
13987            assert_eq!(
13988                c.versao(),
13989                c.versao.as_str(),
13990                "Caixa::versao must byte-equal the raw .versao field \
13991                 access across every value in the String accept-set",
13992            );
13993        }
13994    }
13995
13996    #[test]
13997    fn validate_versao_empty_arm_routes_through_accessor() {
13998        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
13999        // must key off [`Caixa::versao`], not the raw `.versao` field
14000        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
14001        // surface the `VersaoEmpty` refusal exactly, and the canonical
14002        // `"0.1.0"` template baseline (the peer positive-arm the sibling
14003        // `validate_versao_accepts_canonical_template` gate carves out)
14004        // must pass validate. The pair jointly pins the accessor +
14005        // validate-gate composition: any future silent detour that had
14006        // the accessor return a fresh `"0.1.0"` on the empty arm
14007        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
14008        // would silently absorb the `VersaoEmpty` refusal at the
14009        // accessor boundary and the validate gate would accept a
14010        // struct-literal `Caixa { versao: "".into(), .. }` — the
14011        // composition pin catches that at caixa-core build time.
14012        //
14013        // Peer of the sibling per-`Caixa`
14014        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
14015        // composition pin on the sibling outer top-level [`Caixa`]
14016        // required-`&str` universal-axis surface — same "the validate /
14017        // shape-gate predicate must route through the substrate-
14018        // primitive typed dispatch" discipline extended onto the peer
14019        // outer top-level [`Caixa`] required-`&str` universal-axis
14020        // pinned-version composition axis, closing the second
14021        // coordinate of the "one canonical typed dispatch per per-Caixa
14022        // required-`&str` universal-axis" discipline.
14023        let c = caixa_with_versao("");
14024        assert!(
14025            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
14026            "validate_versao must reject versao == \"\" with VersaoEmpty — \
14027             the accessor and the validate gate must route through the \
14028             same substrate-primitive typed dispatch on the :versao \
14029             empty-arm",
14030        );
14031        let c = caixa_with_versao("0.1.0");
14032        assert!(
14033            c.validate_versao().is_ok(),
14034            "validate_versao must accept versao == \"0.1.0\" (the \
14035             canonical SemVer-2 template baseline)",
14036        );
14037    }
14038
14039    #[test]
14040    fn versao_projects_str_by_borrow() {
14041        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
14042        // — the `&str` borrows the underlying `String` storage of the
14043        // required `versao` slot and the accessor must not allocate a
14044        // fresh `String` on every call. Peer of the [`Caixa::nome`]
14045        // (e6b7d97) by-borrow pin on the sibling outer top-level
14046        // [`Caixa`] required-`&str`-return axis, extended onto the
14047        // second outer top-level [`Caixa`] required-`&str`-return
14048        // universal-axis pinned-version surface — the accessor's
14049        // returned `&str` must borrow from `&self` (the returned
14050        // reference's lifetime is tied to `&self`), and calling the
14051        // accessor twice on the same [`Caixa`] must yield the same
14052        // `&str` verbatim (idempotent, no side effects on `&self`).
14053        //
14054        // Pins against a future silent detour that returned an owned
14055        // `String` (which would type-check but silently allocate on
14056        // every call, breaking the zero-cost projection every peer
14057        // sibling accessor carries), an accidental
14058        // `semver::Version::parse(&self.versao).unwrap().to_string()`
14059        // detour that returned a canonicalized fresh allocation through
14060        // an already-canonical byte-string (breaking a future `const fn`
14061        // regression and silently absorbing the `VersaoInvalid` refusal
14062        // at the accessor boundary), or a one-arm-only accessor that
14063        // returned a canonicalized value on some sentinel input
14064        // (breaking the pass-through invariant the sibling required-
14065        // scalar accessors carry).
14066        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
14067            let c = caixa_with_versao(versao);
14068            let first = c.versao();
14069            let second = c.versao();
14070            assert_eq!(
14071                first, second,
14072                "Caixa::versao must be idempotent — two successive \
14073                 calls on the same &self must return the same &str",
14074            );
14075            assert_eq!(
14076                first, versao,
14077                "Caixa::versao must return :versao verbatim by borrow \
14078                 — got {first}, expected {versao}",
14079            );
14080        }
14081    }
14082
14083    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
14084        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14085        c.kind = kind;
14086        c
14087    }
14088
14089    #[test]
14090    fn kind_returns_kind_variant_verbatim_across_permutations() {
14091        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
14092        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
14093        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
14094        // the raw `.kind` field access across every variant in the
14095        // closed accept-set (`Biblioteca` — the library kind that
14096        // exports lisp forms; `Binario` — the nix-built executable kind
14097        // under `exe/`; `Servico` — the wasm-component daemon kind
14098        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
14099        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
14100        // composition kind).
14101        //
14102        // Pins against a future silent detour that re-derived the kind
14103        // from a peer axis (an accidental fallback to
14104        // `if !servicos.is_empty() { Servico } else if
14105        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
14106        // collapse that read the code-surface / mesh-slot columns into
14107        // the kind discriminator), a variant remap the operator
14108        // authors on one consumer without the other, or a stale-derive
14109        // detour that substituted [`CaixaKind::Biblioteca`] as the
14110        // default when the field held any other variant (which would
14111        // silently collapse the distinction between "author explicitly
14112        // declared `:kind Servico`" and "author declared any other
14113        // kind" every downstream renderer-dispatch site depends on).
14114        //
14115        // First outer top-level [`Caixa`] `Copy`-return required-enum-
14116        // discriminant accessor pin — opens the "outer [`Caixa`]
14117        // `Copy`-return required-discriminant" projection pattern.
14118        // Sibling in shape to the peer per-`:supervisor`
14119        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
14120        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
14121        // (921fe1b), and per-`:children`
14122        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
14123        // `Copy`-return closed-set-enum discriminant accessor pins on
14124        // the sibling nested-spec typed-slot discriminator axes,
14125        // extended here to the outer top-level [`Caixa`] universal-
14126        // axis surface.
14127        for kind in [
14128            CaixaKind::Biblioteca,
14129            CaixaKind::Binario,
14130            CaixaKind::Servico,
14131            CaixaKind::Supervisor,
14132            CaixaKind::Aplicacao,
14133        ] {
14134            let c = caixa_with_kind(kind);
14135            assert_eq!(
14136                c.kind(),
14137                kind,
14138                "Caixa::kind must return :kind verbatim (got {:?}, \
14139                 expected {kind:?})",
14140                c.kind(),
14141            );
14142            assert_eq!(
14143                c.kind(),
14144                c.kind,
14145                "Caixa::kind accessor and .kind field access must \
14146                 byte-equal — the accessor is the substrate-primitive \
14147                 typed dispatch every downstream kind-gate consumer \
14148                 must route through",
14149            );
14150        }
14151    }
14152
14153    #[test]
14154    fn require_kind_reads_through_lifted_kind_accessor() {
14155        // Two-consumer coherence pin: the [`crate::render::require_kind`]
14156        // entry-gate predicate (the canonical two-line
14157        // `require_kind(caixa, Servico)?` prelude every per-Servico /
14158        // per-Aplicacao renderer runs at its entry-point) and the
14159        // sibling [`crate::render::KindMismatch`] error carrier's
14160        // `actual:` field (which names the offending caixa's variant
14161        // in the diagnostic) must both key off the lifted accessor, so
14162        // any future rebrand on the typed slot's reader shape lands at
14163        // exactly one place. Pins the two-site coherence by exercising
14164        // every off-diagonal `(actual, expected)` pair across the
14165        // closed accept-set — the `KindMismatch { actual, expected }`
14166        // surfaced on the mismatch arm must byte-equal the pair the
14167        // accessor returns for each side.
14168        //
14169        // Peer of the sibling per-`:placement`
14170        // `validate_placement_reads_through_lifted_estrategia_accessor`
14171        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
14172        // `Copy`-return discriminant axis — same "the entry-gate
14173        // predicate and the error carrier's `actual:` field must route
14174        // through the substrate-primitive typed dispatch" discipline
14175        // extended onto the outer top-level [`Caixa`] universal-axis
14176        // discriminant surface.
14177        for expected in [
14178            CaixaKind::Biblioteca,
14179            CaixaKind::Binario,
14180            CaixaKind::Servico,
14181            CaixaKind::Supervisor,
14182            CaixaKind::Aplicacao,
14183        ] {
14184            for actual in [
14185                CaixaKind::Biblioteca,
14186                CaixaKind::Binario,
14187                CaixaKind::Servico,
14188                CaixaKind::Supervisor,
14189                CaixaKind::Aplicacao,
14190            ] {
14191                let c = caixa_with_kind(actual);
14192                let result = crate::render::require_kind(&c, expected);
14193                if expected == actual {
14194                    assert!(
14195                        result.is_ok(),
14196                        "require_kind must accept when actual == expected \
14197                         (actual={actual:?}, expected={expected:?})",
14198                    );
14199                } else {
14200                    let err = result.expect_err("require_kind must reject when actual != expected");
14201                    assert_eq!(
14202                        err.actual,
14203                        c.kind(),
14204                        "KindMismatch.actual must byte-equal Caixa::kind() \
14205                         — the error carrier's `actual:` field reads \
14206                         through the lifted accessor",
14207                    );
14208                    assert_eq!(
14209                        err.expected, expected,
14210                        "KindMismatch.expected must byte-equal the \
14211                         expected variant passed to require_kind",
14212                    );
14213                }
14214            }
14215        }
14216    }
14217
14218    #[test]
14219    fn aplicacao_view_kind_gate_routes_through_accessor() {
14220        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
14221        // must key off [`Caixa::kind`], not the raw `.kind` field
14222        // access. Structurally: a `Caixa { kind: X, .. }` for any
14223        // non-`Aplicacao` variant must fold to `None` on the
14224        // `aplicacao_view` composer (the "kind mismatch → no typed
14225        // view" contract every downstream Aplicacao consumer keys off
14226        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
14227        // `Some(_)`. The pair jointly pins the accessor + view-gate
14228        // composition: any future silent detour that had the accessor
14229        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
14230        // input would silently absorb the kind-mismatch case at the
14231        // accessor boundary and every per-Aplicacao renderer would
14232        // silently render a non-Aplicacao caixa's mesh slots — the
14233        // composition pin catches that at caixa-core build time.
14234        //
14235        // Peer of the sibling per-`Caixa`
14236        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
14237        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
14238        // composition pins on the sibling outer top-level [`Caixa`]
14239        // required-`&str` universal-axis surfaces — same "the
14240        // composer / validate gate must route through the substrate-
14241        // primitive typed dispatch" discipline extended onto the
14242        // outer top-level [`Caixa`] `Copy`-return required-
14243        // discriminant composition axis.
14244        for kind in [
14245            CaixaKind::Biblioteca,
14246            CaixaKind::Binario,
14247            CaixaKind::Servico,
14248            CaixaKind::Supervisor,
14249        ] {
14250            let c = caixa_with_kind(kind);
14251            assert!(
14252                c.aplicacao_view().is_none(),
14253                "aplicacao_view must return None on non-Aplicacao \
14254                 kind {kind:?} — the composer's kind-gate must route \
14255                 through Caixa::kind()",
14256            );
14257        }
14258        let c = caixa_with_kind(CaixaKind::Aplicacao);
14259        assert!(
14260            c.aplicacao_view().is_some(),
14261            "aplicacao_view must return Some on kind Aplicacao — \
14262             the composer's kind-gate must accept the matching arm \
14263             through Caixa::kind()",
14264        );
14265    }
14266
14267    #[test]
14268    fn supervisor_view_kind_gate_routes_through_accessor() {
14269        // Composition pin (mirror of the sibling
14270        // `aplicacao_view_kind_gate_routes_through_accessor` on the
14271        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
14272        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
14273        // field access. A `Caixa { kind: X, .. }` for any non-
14274        // `Supervisor` variant must fold to `None` on the
14275        // `supervisor_view` composer, and a `Caixa { kind:
14276        // Supervisor, .. }` must fold to `Some(_)`. Same peer
14277        // composition pin discipline on the second `_view` composer
14278        // axis.
14279        for kind in [
14280            CaixaKind::Biblioteca,
14281            CaixaKind::Binario,
14282            CaixaKind::Servico,
14283            CaixaKind::Aplicacao,
14284        ] {
14285            let c = caixa_with_kind(kind);
14286            assert!(
14287                c.supervisor_view().is_none(),
14288                "supervisor_view must return None on non-Supervisor \
14289                 kind {kind:?} — the composer's kind-gate must route \
14290                 through Caixa::kind()",
14291            );
14292        }
14293        let mut c = caixa_with_kind(CaixaKind::Supervisor);
14294        // A Supervisor caixa needs a strategy + at least one child to
14295        // fold to a Some(_) that also validates; the composer itself
14296        // requires only the kind arm, so bare kind flip is enough to
14297        // pin the `Some(_)` return, but we populate the minimum
14298        // supervisor shape so a future strengthening of the composer
14299        // to reject an empty spec doesn't false-positive this pin.
14300        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
14301        c.children = vec![crate::supervisor::ChildSpec {
14302            caixa: "child".into(),
14303            versao: "^0.1".into(),
14304            restart: crate::supervisor::RestartPolicy::Permanent,
14305        }];
14306        assert!(
14307            c.supervisor_view().is_some(),
14308            "supervisor_view must return Some on kind Supervisor — \
14309             the composer's kind-gate must accept the matching arm \
14310             through Caixa::kind()",
14311        );
14312    }
14313
14314    #[test]
14315    fn kind_projects_by_copy() {
14316        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
14317        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
14318        // `&self` (the returned value is owned, `Copy`-projected from
14319        // the underlying [`CaixaKind`] storage; two calls on the same
14320        // [`Caixa`] must yield byte-equal values). Peer of the peer
14321        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
14322        // `SupervisorSpec::estrategia` / per-`:children`
14323        // `ChildSpec::restart` `Copy`-return discriminant accessor
14324        // pins on the sibling nested-spec typed-slot discriminator
14325        // axes, extended onto the first outer top-level [`Caixa`]
14326        // required-`Copy`-return axis — pins against a future silent
14327        // detour that returned `&CaixaKind` (which would type-check
14328        // but silently constrain every consumer's callsite to a
14329        // borrow-shaped dispatch, breaking the zero-cost `Copy`
14330        // projection every peer sibling accessor carries).
14331        for kind in [
14332            CaixaKind::Biblioteca,
14333            CaixaKind::Binario,
14334            CaixaKind::Servico,
14335            CaixaKind::Supervisor,
14336            CaixaKind::Aplicacao,
14337        ] {
14338            let c = caixa_with_kind(kind);
14339            let first: CaixaKind = c.kind();
14340            let second: CaixaKind = c.kind();
14341            assert_eq!(
14342                first, second,
14343                "Caixa::kind must be idempotent — two successive \
14344                 calls on the same &self must return the same \
14345                 CaixaKind variant",
14346            );
14347            assert_eq!(
14348                first, kind,
14349                "Caixa::kind must return :kind verbatim by Copy — \
14350                 got {first:?}, expected {kind:?}",
14351            );
14352        }
14353    }
14354
14355    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
14356
14357    #[test]
14358    fn autores_returns_autores_slice_verbatim_across_permutations() {
14359        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
14360        // name-list slice pin: [`Caixa::autores`] must return the
14361        // `:autores` typed [`Vec<String>`] list verbatim as a
14362        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
14363        // access across every representative value in the accept-set —
14364        // `[]` (the "no maintainers declared" arm every existing
14365        // fixture without an `:autores` line carries), `[""]` (a past-
14366        // the-guard sentinel that pins the accessor doesn't perform a
14367        // silent `[""] → []` collapse on the empty-entry arm — validate
14368        // rejects `[""]` through `AutorEmpty` but the accessor must
14369        // ship the raw slot verbatim so a validate-time gate regression
14370        // surfaces at the caixa-helm emit boundary rather than being
14371        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
14372        // canonical single-maintainer form every `feira init` template
14373        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
14374        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
14375        // (the canonical RFC-5322 `<name> <email>` form the
14376        // `is_chart_maintainer_name_shape` predicate accepts), and
14377        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
14378        // sentinel — validate rejects through `AutorDuplicate` but the
14379        // accessor must ship the raw slot verbatim).
14380        //
14381        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
14382        // pin on the substrate primitive — opens the "outer [`Caixa`]
14383        // `&[T]` slice" projection pattern the sibling per-`Caixa`
14384        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
14385        // / `:servicos` / `:upgrade-from` / `:children` future lifts
14386        // fold on. Sibling in shape to the peer per-`:supervisor`
14387        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
14388        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
14389        // (a6e18d7), per-`:membros`
14390        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
14391        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14392        // (0dcc926), and per-`:upgrade-from :instructions`
14393        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
14394        // `&[T]`-return slice accessor pins on the sibling per-M2 /
14395        // per-M3 typed-slot list axes, extended onto the outer top-
14396        // level [`Caixa`] universal-axis surface. Pins against a future
14397        // silent detour that returned an owned `Vec<String>` (which
14398        // would type-check but silently clone on every accessor call,
14399        // breaking the zero-cost projection every peer sibling slice
14400        // accessor carries), a `[""] → []` collapse (which would
14401        // silently absorb the `AutorEmpty` refusal case at the accessor
14402        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
14403        // would silently absorb the `AutorDuplicate` refusal case at
14404        // the accessor boundary and the caixa-helm `maintainers:` fold
14405        // would silently render a dedupped list on a struct-literal
14406        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
14407        for autores in [
14408            vec![],
14409            vec![""],
14410            vec!["pleme-io"],
14411            vec!["alice", "bob"],
14412            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
14413            vec!["pleme-io", "pleme-io"],
14414        ] {
14415            let c = caixa_with_autores(autores.clone());
14416            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14417            assert_eq!(
14418                c.autores(),
14419                expected.as_slice(),
14420                "Caixa::autores must return :autores verbatim (got {:?}, \
14421                 expected {expected:?})",
14422                c.autores(),
14423            );
14424            assert_eq!(
14425                c.autores(),
14426                c.autores.as_slice(),
14427                "Caixa::autores must byte-equal the raw \
14428                 `self.autores.as_slice()` field access across every \
14429                 value in the Vec<String> accept-set",
14430            );
14431        }
14432    }
14433
14434    #[test]
14435    fn validate_autores_empty_entry_arm_routes_through_accessor() {
14436        // Composition pin: [`Caixa::validate_autores`]'s per-entry
14437        // empty-arm gate must key off [`Caixa::autores`], not the raw
14438        // `&self.autores` field-borrow walk. Structurally: a
14439        // `Caixa { autores: vec!["".into()], .. }` must surface the
14440        // `AutorEmpty` refusal exactly, and a
14441        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
14442        // canonical single-maintainer form) must pass validate. The
14443        // pair jointly pins the accessor + validate-gate composition:
14444        // any future silent detour that had the accessor return an
14445        // empty slice on the `[""]` arm (a
14446        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
14447        // would silently absorb the `AutorEmpty` refusal at the
14448        // accessor boundary and the validate gate would accept a
14449        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
14450        // the composition pin catches that at caixa-core build time.
14451        //
14452        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
14453        // accessor-composition pin
14454        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
14455        // sibling `Option<&str>`-composition axis and the
14456        // per-`:politicas :circuit-breaker`
14457        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
14458        // accessor-composition pin
14459        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
14460        // on the sibling required-`u32`-composition axis — same "the
14461        // validate / shape-gate predicate must route through the
14462        // substrate-primitive typed dispatch" discipline extended onto
14463        // the outer top-level [`Caixa`] universal-axis `&[T]`-
14464        // composition surface.
14465        let c = caixa_with_autores(vec![""]);
14466        assert!(
14467            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
14468            "validate_autores must reject autores == vec![\"\"] with \
14469             AutorEmpty — the accessor and the validate gate must \
14470             route through the same substrate-primitive typed dispatch \
14471             on the :autores per-entry empty arm",
14472        );
14473        let c = caixa_with_autores(vec!["pleme-io"]);
14474        assert!(
14475            c.validate_autores().is_ok(),
14476            "validate_autores must accept autores == vec![\"pleme-io\"] \
14477             (the canonical single-maintainer shape every `feira init` \
14478             template scaffolds)",
14479        );
14480    }
14481
14482    #[test]
14483    fn autores_projects_slice_by_borrow() {
14484        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
14485        // borrow — the returned slice borrows the underlying
14486        // `Vec<String>` storage of the `:autores` slot and the
14487        // accessor must not clone the backing `Vec` on every call.
14488        // Peer of the per-`:membros`
14489        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
14490        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14491        // (0dcc926) / per-`:placement`
14492        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
14493        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
14494        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
14495        // typed-slot `&[T]`-return axes, extended onto the outer top-
14496        // level [`Caixa`] universal-axis `&[String]` shape — the
14497        // accessor's returned slice must borrow from `&self` (the
14498        // returned reference's lifetime is tied to `&self`), and
14499        // calling the accessor twice on the same [`Caixa`] must yield
14500        // slices that are pointer-equal (the underlying byte-buffer is
14501        // the storage `Vec`'s allocation, not a fresh copy) as well as
14502        // value-equal (idempotent, no side effects on `&self`).
14503        //
14504        // Pins against a future silent detour that returned an owned
14505        // `Vec<String>` (which would type-check but silently clone on
14506        // every call, breaking the zero-cost projection every peer
14507        // sibling slice accessor carries), a `&Vec<String>` return
14508        // (which would leak the backing `Vec`'s grow/push/reserve
14509        // surface no downstream consumer reaches for), or a one-arm-
14510        // only accessor that returned a saturating value on some
14511        // sentinel input (breaking the pass-through invariant the
14512        // sibling slice accessors carry).
14513        for autores in [
14514            vec![],
14515            vec!["pleme-io"],
14516            vec!["alice", "bob"],
14517            vec!["pleme-io", "pleme-io"],
14518        ] {
14519            let c = caixa_with_autores(autores.clone());
14520            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14521            let first = c.autores();
14522            let second = c.autores();
14523            assert_eq!(
14524                first, second,
14525                "Caixa::autores must be idempotent — two successive \
14526                 calls on the same &self must return the same \
14527                 &[String]",
14528            );
14529            assert_eq!(
14530                first.as_ptr(),
14531                second.as_ptr(),
14532                "Caixa::autores must borrow the underlying Vec<String> \
14533                 storage — two successive calls must return slices \
14534                 with the same backing pointer (a fresh Vec<String> \
14535                 clone would change the pointer on every call)",
14536            );
14537            assert_eq!(
14538                first,
14539                expected.as_slice(),
14540                "Caixa::autores must return :autores verbatim by \
14541                 borrow — got {first:?}, expected {expected:?}",
14542            );
14543        }
14544    }
14545
14546    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
14547
14548    #[test]
14549    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
14550        // The canonical per-`Caixa` `:etiquetas` universal-axis
14551        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
14552        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
14553        // as a `&[String]`, byte-equal to the raw
14554        // `self.etiquetas.as_slice()` access across every representative
14555        // value in the accept-set — `[]` (the "no tags declared" arm
14556        // every existing fixture without an `:etiquetas` line carries),
14557        // `[""]` (a past-the-guard sentinel that pins the accessor
14558        // doesn't perform a silent `[""] → []` collapse on the empty-
14559        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
14560        // but the accessor must ship the raw slot verbatim so a
14561        // validate-time gate regression surfaces at the caixa-helm emit
14562        // boundary rather than being silently absorbed into a keyword-
14563        // drop), `["demo"]` (the canonical single-tag form every
14564        // `feira init` template scaffolds), `["example", "aplicacao",
14565        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
14566        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
14567        // (a past-the-guard duplicate sentinel — validate rejects
14568        // through `EtiquetaDuplicate` but the accessor must ship the
14569        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
14570        // at chart-render time isn't silently promoted into the
14571        // accessor boundary and struct-literal
14572        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
14573        // fixtures continue to expose the duplicate at the accessor).
14574        //
14575        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
14576        // pin on the substrate primitive — folds on the "outer
14577        // [`Caixa`] `&[T]` slice" projection pattern
14578        // `autores_returns_autores_slice_verbatim_across_permutations`
14579        // (b5d813f) opened, sibling in shape and idiom. Pins against a
14580        // future silent detour that returned an owned `Vec<String>`
14581        // (which would type-check but silently clone on every accessor
14582        // call, breaking the zero-cost projection every peer sibling
14583        // slice accessor carries), a `[""] → []` collapse (which would
14584        // silently absorb the `EtiquetaEmpty` refusal case at the
14585        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
14586        // (which would silently absorb the `EtiquetaDuplicate` refusal
14587        // case at the accessor boundary — the caixa-helm chart-render
14588        // `BTreeSet::collect` dedup is downstream of the accessor and
14589        // must not be silently promoted into it).
14590        for etiquetas in [
14591            vec![],
14592            vec![""],
14593            vec!["demo"],
14594            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
14595            vec!["demo", "demo"],
14596        ] {
14597            let c = caixa_with_etiquetas(etiquetas.clone());
14598            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14599            assert_eq!(
14600                c.etiquetas(),
14601                expected.as_slice(),
14602                "Caixa::etiquetas must return :etiquetas verbatim (got \
14603                 {:?}, expected {expected:?})",
14604                c.etiquetas(),
14605            );
14606            assert_eq!(
14607                c.etiquetas(),
14608                c.etiquetas.as_slice(),
14609                "Caixa::etiquetas must byte-equal the raw \
14610                 `self.etiquetas.as_slice()` field access across every \
14611                 value in the Vec<String> accept-set",
14612            );
14613        }
14614    }
14615
14616    #[test]
14617    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
14618        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
14619        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
14620        // `&self.etiquetas` field-borrow walk. Structurally: a
14621        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
14622        // `EtiquetaEmpty` refusal exactly, and a
14623        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
14624        // single-tag form) must pass validate. The pair jointly pins
14625        // the accessor + validate-gate composition: any future silent
14626        // detour that had the accessor return an empty slice on the
14627        // `[""]` arm (a
14628        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
14629        // silently absorb the `EtiquetaEmpty` refusal at the accessor
14630        // boundary and the validate gate would accept a struct-literal
14631        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
14632        // pin catches that at caixa-core build time.
14633        //
14634        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
14635        // through_accessor` (b5d813f) accessor-composition pin on the
14636        // sibling `&[T]`-composition axis — same "the validate / shape-
14637        // gate predicate must route through the substrate-primitive
14638        // typed dispatch" discipline extended onto the sibling outer
14639        // top-level [`Caixa`] `&[T]`-composition surface.
14640        let c = caixa_with_etiquetas(vec![""]);
14641        assert!(
14642            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
14643            "validate_etiquetas must reject etiquetas == vec![\"\"] \
14644             with EtiquetaEmpty — the accessor and the validate gate \
14645             must route through the same substrate-primitive typed \
14646             dispatch on the :etiquetas per-entry empty arm",
14647        );
14648        let c = caixa_with_etiquetas(vec!["demo"]);
14649        assert!(
14650            c.validate_etiquetas().is_ok(),
14651            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
14652             (the canonical single-tag shape every `feira init` \
14653             template scaffolds)",
14654        );
14655    }
14656
14657    #[test]
14658    fn etiquetas_projects_slice_by_borrow() {
14659        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
14660        // by borrow — the returned slice borrows the underlying
14661        // `Vec<String>` storage of the `:etiquetas` slot and the
14662        // accessor must not clone the backing `Vec` on every call.
14663        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14664        // (b5d813f) by-borrow pin on the sibling outer top-level
14665        // [`Caixa`] `&[String]`-return axis — the accessor's returned
14666        // slice must borrow from `&self` (the returned reference's
14667        // lifetime is tied to `&self`), and calling the accessor twice
14668        // on the same [`Caixa`] must yield slices that are pointer-
14669        // equal (the underlying byte-buffer is the storage `Vec`'s
14670        // allocation, not a fresh copy) as well as value-equal
14671        // (idempotent, no side effects on `&self`).
14672        //
14673        // Pins against a future silent detour that returned an owned
14674        // `Vec<String>` (which would type-check but silently clone on
14675        // every call, breaking the zero-cost projection every peer
14676        // sibling slice accessor carries), a `&Vec<String>` return
14677        // (which would leak the backing `Vec`'s grow/push/reserve
14678        // surface no downstream consumer reaches for), or a one-arm-
14679        // only accessor that returned a saturating value on some
14680        // sentinel input (breaking the pass-through invariant the
14681        // sibling slice accessors carry).
14682        for etiquetas in [
14683            vec![],
14684            vec!["demo"],
14685            vec!["example", "aplicacao", "mesh"],
14686            vec!["demo", "demo"],
14687        ] {
14688            let c = caixa_with_etiquetas(etiquetas.clone());
14689            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14690            let first = c.etiquetas();
14691            let second = c.etiquetas();
14692            assert_eq!(
14693                first, second,
14694                "Caixa::etiquetas must be idempotent — two successive \
14695                 calls on the same &self must return the same \
14696                 &[String]",
14697            );
14698            assert_eq!(
14699                first.as_ptr(),
14700                second.as_ptr(),
14701                "Caixa::etiquetas must borrow the underlying \
14702                 Vec<String> storage — two successive calls must \
14703                 return slices with the same backing pointer (a fresh \
14704                 Vec<String> clone would change the pointer on every \
14705                 call)",
14706            );
14707            assert_eq!(
14708                first,
14709                expected.as_slice(),
14710                "Caixa::etiquetas must return :etiquetas verbatim by \
14711                 borrow — got {first:?}, expected {expected:?}",
14712            );
14713        }
14714    }
14715
14716    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
14717
14718    #[test]
14719    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
14720        // The canonical per-`Caixa` `:bibliotecas` universal-axis
14721        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
14722        // must return the `:bibliotecas` typed [`Vec<String>`] list
14723        // verbatim as a `&[String]`, byte-equal to the raw
14724        // `self.bibliotecas.as_slice()` access across every
14725        // representative value in the accept-set — `[]` (the "no
14726        // libraries declared" arm every `:kind` other than `Biblioteca`
14727        // + every `Biblioteca` relying on the canonical
14728        // `lib/<nome>.lisp` implicit-default path carries; the
14729        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
14730        // fires exactly on this empty-slot + `Biblioteca`-kind
14731        // combination), `[""]` (a past-the-guard sentinel that pins
14732        // the accessor doesn't perform a silent `[""] → []` collapse
14733        // on the empty-entry arm — validate rejects `[""]` through
14734        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
14735        // must ship the raw slot verbatim so a validate-time gate
14736        // regression surfaces at the `feira build` phase-1 parse
14737        // boundary rather than being silently absorbed into a
14738        // library-drop), `["lib/demo.lisp"]` (the canonical single-
14739        // entry form `Caixa::template` scaffolds and every `feira init`
14740        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
14741        // (the canonical multi-library form the
14742        // `validate_code_paths_accepts_explicit_relative_paths_on_
14743        // every_slot` fixture emits), and `["lib/foo.lisp",
14744        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
14745        // validate rejects through `CodePathDuplicate { slot:
14746        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
14747        // but the accessor must ship the raw slot verbatim so the
14748        // `feira build` `for entry in caixa.bibliotecas()` parse walk
14749        // sees the duplicate at the accessor boundary and struct-
14750        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
14751        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
14752        // the duplicate at the accessor).
14753        //
14754        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
14755        // pin on the substrate primitive — folds on the "outer
14756        // [`Caixa`] `&[T]` slice" projection pattern
14757        // `autores_returns_autores_slice_verbatim_across_permutations`
14758        // (b5d813f) opened and
14759        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14760        // (78c7d3c) folded on, sibling in shape and idiom. Pins
14761        // against a future silent detour that returned an owned
14762        // `Vec<String>` (which would type-check but silently clone on
14763        // every accessor call, breaking the zero-cost projection
14764        // every peer sibling slice accessor carries), a `[""] → []`
14765        // collapse (which would silently absorb the `CodePathEmpty`
14766        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
14767        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
14768        // would silently absorb the `CodePathDuplicate` refusal case
14769        // at the accessor boundary — the per-slot set-not-multiset
14770        // gate is downstream of the accessor and must not be silently
14771        // promoted into it).
14772        for bibliotecas in [
14773            vec![],
14774            vec![""],
14775            vec!["lib/demo.lisp"],
14776            vec!["lib/demo.lisp", "lib/helpers.lisp"],
14777            vec!["lib/foo.lisp", "lib/foo.lisp"],
14778        ] {
14779            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
14780            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
14781            assert_eq!(
14782                c.bibliotecas(),
14783                expected.as_slice(),
14784                "Caixa::bibliotecas must return :bibliotecas verbatim \
14785                 (got {:?}, expected {expected:?})",
14786                c.bibliotecas(),
14787            );
14788            assert_eq!(
14789                c.bibliotecas(),
14790                c.bibliotecas.as_slice(),
14791                "Caixa::bibliotecas must byte-equal the raw \
14792                 `self.bibliotecas.as_slice()` field access across \
14793                 every value in the Vec<String> accept-set",
14794            );
14795        }
14796    }
14797
14798    #[test]
14799    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
14800        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
14801        // empty-arm gate on the `:bibliotecas` slot must key off
14802        // [`Caixa::bibliotecas`], not a divergent raw
14803        // `&self.bibliotecas` field-borrow walk. Structurally: a
14804        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
14805        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
14806        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
14807        // into()], .. }` (the canonical single-library form
14808        // `Caixa::template` scaffolds) must pass validate. The pair
14809        // jointly pins the accessor + validate-gate composition: any
14810        // future silent detour that had the accessor return an empty
14811        // slice on the `[""]` arm (a `.iter().filter(|s|
14812        // !s.is_empty()).collect()` collapse) would silently absorb
14813        // the `CodePathEmpty` refusal at the accessor boundary and
14814        // the validate gate would accept a struct-literal
14815        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
14816        // composition pin catches that at caixa-core build time.
14817        //
14818        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
14819        // through_accessor` (b5d813f) and
14820        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
14821        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
14822        // composition axes — same "the validate / shape-gate
14823        // predicate must route through the substrate-primitive typed
14824        // dispatch" discipline extended onto the sibling outer top-
14825        // level [`Caixa`] `&[T]`-composition surface. Nominally the
14826        // in-tree `validate_code_paths` production body still keys
14827        // off the internal `[(":bibliotecas", &self.bibliotecas,
14828        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
14829        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
14830        // (the tuple's homogeneous slice-typed shape blocks a per-
14831        // element accessor swap in isolation — a future companion
14832        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
14833        // `&[T]` slice-accessor axis closes that tuple onto the
14834        // triple of typed dispatches as a unit); the composition pin
14835        // catches any future accessor-side silent filter drop against
14836        // that eventual tuple-closure regardless of whether the
14837        // `:bibliotecas` slot is threaded through the accessor or the
14838        // raw field access at the tuple's construction site.
14839        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
14840        assert!(
14841            matches!(
14842                c.validate_code_paths(),
14843                Err(ManifestError::CodePathEmpty {
14844                    slot: ":bibliotecas"
14845                })
14846            ),
14847            "validate_code_paths must reject bibliotecas == vec![\"\"] \
14848             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
14849             accessor and the validate gate must route through the \
14850             same substrate-primitive typed dispatch on the \
14851             :bibliotecas per-entry empty arm",
14852        );
14853        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
14854        assert!(
14855            c.validate_code_paths().is_ok(),
14856            "validate_code_paths must accept bibliotecas == \
14857             vec![\"lib/demo.lisp\"] (the canonical single-library \
14858             shape every `feira init` template scaffolds)",
14859        );
14860    }
14861
14862    #[test]
14863    fn bibliotecas_projects_slice_by_borrow() {
14864        // The by-borrow pin: [`Caixa::bibliotecas`] returns
14865        // `&[String]` by borrow — the returned slice borrows the
14866        // underlying `Vec<String>` storage of the `:bibliotecas` slot
14867        // and the accessor must not clone the backing `Vec` on every
14868        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14869        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
14870        // by-borrow pins on the sibling outer top-level [`Caixa`]
14871        // `&[String]`-return axes — the accessor's returned slice
14872        // must borrow from `&self` (the returned reference's lifetime
14873        // is tied to `&self`), and calling the accessor twice on the
14874        // same [`Caixa`] must yield slices that are pointer-equal
14875        // (the underlying byte-buffer is the storage `Vec`'s
14876        // allocation, not a fresh copy) as well as value-equal
14877        // (idempotent, no side effects on `&self`).
14878        //
14879        // Pins against a future silent detour that returned an owned
14880        // `Vec<String>` (which would type-check but silently clone on
14881        // every call, breaking the zero-cost projection every peer
14882        // sibling slice accessor carries), a `&Vec<String>` return
14883        // (which would leak the backing `Vec`'s grow/push/reserve
14884        // surface no downstream consumer reaches for), or a one-arm-
14885        // only accessor that returned a saturating value on some
14886        // sentinel input (breaking the pass-through invariant the
14887        // sibling slice accessors carry).
14888        for bibliotecas in [
14889            vec![],
14890            vec!["lib/demo.lisp"],
14891            vec!["lib/demo.lisp", "lib/helpers.lisp"],
14892            vec!["lib/foo.lisp", "lib/foo.lisp"],
14893        ] {
14894            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
14895            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
14896            let first = c.bibliotecas();
14897            let second = c.bibliotecas();
14898            assert_eq!(
14899                first, second,
14900                "Caixa::bibliotecas must be idempotent — two \
14901                 successive calls on the same &self must return the \
14902                 same &[String]",
14903            );
14904            assert_eq!(
14905                first.as_ptr(),
14906                second.as_ptr(),
14907                "Caixa::bibliotecas must borrow the underlying \
14908                 Vec<String> storage — two successive calls must \
14909                 return slices with the same backing pointer (a \
14910                 fresh Vec<String> clone would change the pointer on \
14911                 every call)",
14912            );
14913            assert_eq!(
14914                first,
14915                expected.as_slice(),
14916                "Caixa::bibliotecas must return :bibliotecas verbatim \
14917                 by borrow — got {first:?}, expected {expected:?}",
14918            );
14919        }
14920    }
14921
14922    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
14923
14924    #[test]
14925    fn exe_returns_exe_slice_verbatim_across_permutations() {
14926        // The canonical per-`Caixa` `:exe` universal-axis
14927        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
14928        // must return the `:exe` typed [`Vec<String>`] list verbatim as
14929        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
14930        // access across every representative value in the accept-set —
14931        // `[]` (the "no executable declared" arm every `:kind` other
14932        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
14933        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
14934        // + `Binario`-kind combination), `[""]` (a past-the-guard
14935        // sentinel that pins the accessor doesn't perform a silent
14936        // `[""] → []` collapse on the empty-entry arm — validate rejects
14937        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
14938        // accessor must ship the raw slot verbatim so a validate-time
14939        // gate regression surfaces at the layout / `feira nix` boundary
14940        // rather than being silently absorbed into an executable-drop),
14941        // `["exe/cli"]` (the canonical single-entry Binario form every
14942        // in-tree `caixa_with_code_paths` positive control uses),
14943        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
14944        // form the `validate_code_paths_accepts_explicit_relative_paths_
14945        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
14946        // (a past-the-guard duplicate sentinel — validate rejects
14947        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
14948        // set-not-multiset gate, but the accessor must ship the raw
14949        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
14950        // into(), "exe/cli".into()], .. }` fixtures continue to expose
14951        // the duplicate at the accessor).
14952        //
14953        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
14954        // pin on the substrate primitive — folds on the "outer
14955        // [`Caixa`] `&[T]` slice" projection pattern
14956        // `autores_returns_autores_slice_verbatim_across_permutations`
14957        // (b5d813f) opened,
14958        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14959        // (78c7d3c) folded on, and
14960        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
14961        // (8a36c23) closed the universal-axis text-tag family of.
14962        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
14963        // the sibling `:servicos` future lift closes onto. Pins against
14964        // a future silent detour that returned an owned `Vec<String>`
14965        // (which would type-check but silently clone on every accessor
14966        // call, breaking the zero-cost projection every peer sibling
14967        // slice accessor carries), a `[""] → []` collapse (which would
14968        // silently absorb the `CodePathEmpty` refusal case at the
14969        // accessor boundary), or an `["exe/cli", "exe/cli"] →
14970        // ["exe/cli"]` dedup collapse (which would silently absorb the
14971        // `CodePathDuplicate` refusal case at the accessor boundary —
14972        // the per-slot set-not-multiset gate is downstream of the
14973        // accessor and must not be silently promoted into it).
14974        for exe in [
14975            vec![],
14976            vec![""],
14977            vec!["exe/cli"],
14978            vec!["exe/cli", "exe/serve"],
14979            vec!["exe/cli", "exe/cli"],
14980        ] {
14981            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
14982            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
14983            assert_eq!(
14984                c.exe(),
14985                expected.as_slice(),
14986                "Caixa::exe must return :exe verbatim (got {:?}, \
14987                 expected {expected:?})",
14988                c.exe(),
14989            );
14990            assert_eq!(
14991                c.exe(),
14992                c.exe.as_slice(),
14993                "Caixa::exe must byte-equal the raw \
14994                 `self.exe.as_slice()` field access across every value \
14995                 in the Vec<String> accept-set",
14996            );
14997        }
14998    }
14999
15000    #[test]
15001    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
15002        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15003        // empty-arm gate on the `:exe` slot must key off
15004        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
15005        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
15006        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
15007        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
15008        // (the canonical single-executable form every in-tree
15009        // `caixa_with_code_paths` positive control uses) must pass
15010        // validate. The pair jointly pins the accessor + validate-gate
15011        // composition: any future silent detour that had the accessor
15012        // return an empty slice on the `[""]` arm (a
15013        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
15014        // silently absorb the `CodePathEmpty` refusal at the accessor
15015        // boundary and the validate gate would accept a struct-literal
15016        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
15017        // catches that at caixa-core build time.
15018        //
15019        // Peer of the per-`Caixa`
15020        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15021        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
15022        // (b5d813f), and
15023        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15024        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15025        // composition axes — same "the validate / shape-gate predicate
15026        // must route through the substrate-primitive typed dispatch"
15027        // discipline extended onto the sibling outer top-level [`Caixa`]
15028        // `&[T]`-composition surface. Nominally the in-tree
15029        // `validate_code_paths` production body still keys off the
15030        // internal `[(":bibliotecas", &self.bibliotecas,
15031        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15032        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15033        // (the tuple's homogeneous slice-typed shape blocks a per-
15034        // element accessor swap in isolation — a future companion lift
15035        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
15036        // accessor axis closes that tuple onto the triple of typed
15037        // dispatches as a unit); the composition pin catches any future
15038        // accessor-side silent filter drop against that eventual tuple-
15039        // closure regardless of whether the `:exe` slot is threaded
15040        // through the accessor or the raw field access at the tuple's
15041        // construction site.
15042        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
15043        assert!(
15044            matches!(
15045                c.validate_code_paths(),
15046                Err(ManifestError::CodePathEmpty { slot: ":exe" })
15047            ),
15048            "validate_code_paths must reject exe == vec![\"\"] \
15049             with CodePathEmpty {{ slot: \":exe\" }} — the \
15050             accessor and the validate gate must route through the \
15051             same substrate-primitive typed dispatch on the \
15052             :exe per-entry empty arm",
15053        );
15054        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
15055        assert!(
15056            c.validate_code_paths().is_ok(),
15057            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
15058             (the canonical single-executable shape every in-tree \
15059             `caixa_with_code_paths` positive control uses)",
15060        );
15061    }
15062
15063    #[test]
15064    fn exe_projects_slice_by_borrow() {
15065        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
15066        // borrow — the returned slice borrows the underlying
15067        // `Vec<String>` storage of the `:exe` slot and the accessor
15068        // must not clone the backing `Vec` on every call. Peer of the
15069        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
15070        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
15071        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
15072        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
15073        // return axes — the accessor's returned slice must borrow from
15074        // `&self` (the returned reference's lifetime is tied to
15075        // `&self`), and calling the accessor twice on the same
15076        // [`Caixa`] must yield slices that are pointer-equal (the
15077        // underlying byte-buffer is the storage `Vec`'s allocation,
15078        // not a fresh copy) as well as value-equal (idempotent, no
15079        // side effects on `&self`).
15080        //
15081        // Pins against a future silent detour that returned an owned
15082        // `Vec<String>` (which would type-check but silently clone on
15083        // every call, breaking the zero-cost projection every peer
15084        // sibling slice accessor carries), a `&Vec<String>` return
15085        // (which would leak the backing `Vec`'s grow/push/reserve
15086        // surface no downstream consumer reaches for), or a one-arm-
15087        // only accessor that returned a saturating value on some
15088        // sentinel input (breaking the pass-through invariant the
15089        // sibling slice accessors carry).
15090        for exe in [
15091            vec![],
15092            vec!["exe/cli"],
15093            vec!["exe/cli", "exe/serve"],
15094            vec!["exe/cli", "exe/cli"],
15095        ] {
15096            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15097            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15098            let first = c.exe();
15099            let second = c.exe();
15100            assert_eq!(
15101                first, second,
15102                "Caixa::exe must be idempotent — two successive calls \
15103                 on the same &self must return the same &[String]",
15104            );
15105            assert_eq!(
15106                first.as_ptr(),
15107                second.as_ptr(),
15108                "Caixa::exe must borrow the underlying Vec<String> \
15109                 storage — two successive calls must return slices \
15110                 with the same backing pointer (a fresh Vec<String> \
15111                 clone would change the pointer on every call)",
15112            );
15113            assert_eq!(
15114                first,
15115                expected.as_slice(),
15116                "Caixa::exe must return :exe verbatim by borrow — \
15117                 got {first:?}, expected {expected:?}",
15118            );
15119        }
15120    }
15121
15122    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
15123
15124    #[test]
15125    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
15126        // The canonical per-`Caixa` `:servicos` universal-axis
15127        // ComputeUnit-CR-YAML-entry-path-list slice pin:
15128        // [`Caixa::servicos`] must return the `:servicos` typed
15129        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
15130        // the raw `self.servicos.as_slice()` access across every
15131        // representative value in the accept-set — `[]` (the "no
15132        // ComputeUnit-CR declared" arm every `:kind` other than
15133        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
15134        // `ServicoWithoutServicos` arm-gate fires exactly on this
15135        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
15136        // guard sentinel that pins the accessor doesn't perform a
15137        // silent `[""] → []` collapse on the empty-entry arm — validate
15138        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
15139        // but the accessor must ship the raw slot verbatim so a
15140        // validate-time gate regression surfaces at the layout /
15141        // per-Servico renderer boundary rather than being silently
15142        // absorbed into a component-drop),
15143        // `["servicos/demo.computeunit.yaml"]` (the canonical
15144        // singleton V0-shape every in-tree `caixa_with_code_paths`
15145        // positive control uses; the same shape
15146        // [`crate::require_single_servico`] admits),
15147        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
15148        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
15149        // singularity gate rejects through `ServicoCountMismatch
15150        // { count: 2 }` but the accessor must ship the raw slot
15151        // verbatim so struct-literal `Caixa { servicos: vec![...,
15152        // ...], .. }` fixtures continue to expose the count at the
15153        // accessor), and `["servicos/a.computeunit.yaml",
15154        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
15155        // sentinel — validate rejects through
15156        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
15157        // set-not-multiset gate, but the accessor must ship the raw
15158        // slot verbatim so struct-literal fixtures continue to expose
15159        // the duplicate at the accessor).
15160        //
15161        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
15162        // slice accessor pin on the substrate primitive — folds on the
15163        // "outer [`Caixa`] `&[T]` slice" projection pattern
15164        // `autores_returns_autores_slice_verbatim_across_permutations`
15165        // (b5d813f) opened,
15166        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15167        // (78c7d3c) folded on,
15168        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15169        // (8a36c23) closed the universal-axis text-tag family of, and
15170        // `exe_returns_exe_slice_verbatim_across_permutations`
15171        // (65d9527) opened the foreign-code-slot sub-family of. Closes
15172        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
15173        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
15174        // `:servicos`) now each carries a substrate-canonical slice
15175        // accessor. Pins against a future silent detour that returned
15176        // an owned `Vec<String>` (which would type-check but silently
15177        // clone on every accessor call, breaking the zero-cost
15178        // projection every peer sibling slice accessor carries), a
15179        // `[""] → []` collapse (which would silently absorb the
15180        // `CodePathEmpty` refusal case at the accessor boundary), an
15181        // `[a, a] → [a]` dedup collapse (which would silently absorb
15182        // the `CodePathDuplicate` refusal case at the accessor
15183        // boundary — the per-slot set-not-multiset gate is downstream
15184        // of the accessor and must not be silently promoted into it),
15185        // or a `[a, b] → [a]` singleton collapse (which would silently
15186        // absorb the V0 `ServicoCountMismatch` refusal case at the
15187        // accessor boundary — the V0 singularity gate is downstream of
15188        // the accessor and must not be silently promoted into it).
15189        for servicos in [
15190            vec![],
15191            vec![""],
15192            vec!["servicos/demo.computeunit.yaml"],
15193            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15194            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15195        ] {
15196            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15197            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15198            assert_eq!(
15199                c.servicos(),
15200                expected.as_slice(),
15201                "Caixa::servicos must return :servicos verbatim (got \
15202                 {:?}, expected {expected:?})",
15203                c.servicos(),
15204            );
15205            assert_eq!(
15206                c.servicos(),
15207                c.servicos.as_slice(),
15208                "Caixa::servicos must byte-equal the raw \
15209                 `self.servicos.as_slice()` field access across every \
15210                 value in the Vec<String> accept-set",
15211            );
15212        }
15213    }
15214
15215    #[test]
15216    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
15217        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15218        // empty-arm gate on the `:servicos` slot must key off
15219        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
15220        // field-borrow walk. Structurally: a `Caixa { servicos:
15221        // vec!["".into()], .. }` must surface the `CodePathEmpty
15222        // { slot: ":servicos" }` refusal exactly, and a `Caixa
15223        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
15224        // .. }` (the canonical singleton V0-shape every in-tree
15225        // `caixa_with_code_paths` positive control uses) must pass
15226        // validate. The pair jointly pins the accessor + validate-gate
15227        // composition: any future silent detour that had the accessor
15228        // return an empty slice on the `[""]` arm (a `.iter().filter
15229        // (|s| !s.is_empty()).collect()` collapse) would silently
15230        // absorb the `CodePathEmpty` refusal at the accessor boundary
15231        // and the validate gate would accept a struct-literal
15232        // `Caixa { servicos: vec!["".into()], .. }` — the composition
15233        // pin catches that at caixa-core build time.
15234        //
15235        // Peer of the per-`Caixa`
15236        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15237        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15238        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
15239        // (b5d813f), and
15240        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15241        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15242        // composition axes — same "the validate / shape-gate predicate
15243        // must route through the substrate-primitive typed dispatch"
15244        // discipline extended onto the sibling outer top-level
15245        // [`Caixa`] `&[T]`-composition surface, closing the trio of
15246        // code-surface accessor-composition pins on the same axis.
15247        // Nominally the in-tree `validate_code_paths` production body
15248        // still keys off the internal
15249        // `[(":bibliotecas", &self.bibliotecas,
15250        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15251        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15252        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
15253        // per-element accessor swap in isolation — a future companion
15254        // lift promotes the tuple's element type to `&[String]` and
15255        // threads the triple of typed dispatches through as a unit);
15256        // the composition pin catches any future accessor-side silent
15257        // filter drop against that eventual tuple-closure regardless
15258        // of whether the `:servicos` slot is threaded through the
15259        // accessor or the raw field access at the tuple's construction
15260        // site.
15261        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
15262        assert!(
15263            matches!(
15264                c.validate_code_paths(),
15265                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
15266            ),
15267            "validate_code_paths must reject servicos == vec![\"\"] \
15268             with CodePathEmpty {{ slot: \":servicos\" }} — the \
15269             accessor and the validate gate must route through the \
15270             same substrate-primitive typed dispatch on the \
15271             :servicos per-entry empty arm",
15272        );
15273        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
15274        assert!(
15275            c.validate_code_paths().is_ok(),
15276            "validate_code_paths must accept servicos == \
15277             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
15278             singleton V0-shape every in-tree `caixa_with_code_paths` \
15279             positive control uses)",
15280        );
15281    }
15282
15283    #[test]
15284    fn servicos_projects_slice_by_borrow() {
15285        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
15286        // borrow — the returned slice borrows the underlying
15287        // `Vec<String>` storage of the `:servicos` slot and the
15288        // accessor must not clone the backing `Vec` on every call.
15289        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15290        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
15291        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
15292        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
15293        // the sibling outer top-level [`Caixa`] `&[String]`-return
15294        // axes — the accessor's returned slice must borrow from
15295        // `&self` (the returned reference's lifetime is tied to
15296        // `&self`), and calling the accessor twice on the same
15297        // [`Caixa`] must yield slices that are pointer-equal (the
15298        // underlying byte-buffer is the storage `Vec`'s allocation,
15299        // not a fresh copy) as well as value-equal (idempotent, no
15300        // side effects on `&self`).
15301        //
15302        // Pins against a future silent detour that returned an owned
15303        // `Vec<String>` (which would type-check but silently clone on
15304        // every call, breaking the zero-cost projection every peer
15305        // sibling slice accessor carries), a `&Vec<String>` return
15306        // (which would leak the backing `Vec`'s grow/push/reserve
15307        // surface no downstream consumer reaches for), or a one-arm-
15308        // only accessor that returned a saturating value on some
15309        // sentinel input (breaking the pass-through invariant the
15310        // sibling slice accessors carry).
15311        for servicos in [
15312            vec![],
15313            vec!["servicos/demo.computeunit.yaml"],
15314            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15315            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15316        ] {
15317            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15318            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15319            let first = c.servicos();
15320            let second = c.servicos();
15321            assert_eq!(
15322                first, second,
15323                "Caixa::servicos must be idempotent — two successive \
15324                 calls on the same &self must return the same &[String]",
15325            );
15326            assert_eq!(
15327                first.as_ptr(),
15328                second.as_ptr(),
15329                "Caixa::servicos must borrow the underlying \
15330                 Vec<String> storage — two successive calls must \
15331                 return slices with the same backing pointer (a fresh \
15332                 Vec<String> clone would change the pointer on every \
15333                 call)",
15334            );
15335            assert_eq!(
15336                first,
15337                expected.as_slice(),
15338                "Caixa::servicos must return :servicos verbatim by \
15339                 borrow — got {first:?}, expected {expected:?}",
15340            );
15341        }
15342    }
15343
15344    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
15345
15346    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
15347        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15348        c.deps = deps;
15349        c
15350    }
15351
15352    #[test]
15353    fn deps_returns_deps_slice_verbatim_across_permutations() {
15354        // The canonical per-`Caixa` `:deps` universal-axis runtime-
15355        // dependency-declaration-list slice pin: [`Caixa::deps`] must
15356        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
15357        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
15358        // access across every representative value in the accept-set —
15359        // `[]` (the "no runtime deps declared" arm every existing
15360        // fixture without a `:deps` line carries; the
15361        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
15362        // single-entry list (the shape most consumer caixas carry), a
15363        // canonical two-entry list (the multi-dep runtime closure), and
15364        // two past-the-guard sentinels — a `[""]`-`:nome` entry
15365        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15366        // `NomeInvalid` but the accessor must ship the raw slot
15367        // verbatim) and a `[a, a]` duplicate (validate rejects through
15368        // `DuplicateNome { list: ":deps" }` but the accessor must ship
15369        // the raw slot verbatim so struct-literal fixtures continue to
15370        // expose the duplicate at the accessor).
15371        //
15372        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
15373        // pin on the substrate primitive — opens the outer-`Caixa`
15374        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
15375        // future lift closes on. Peer of the closed outer-`Caixa`
15376        // foreign-code-slot `&[String]` sub-family
15377        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15378        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
15379        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
15380        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
15381        // (`autores_returns_autores_slice_verbatim_across_permutations`
15382        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15383        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
15384        // projection pattern onto a novel element-type axis (`Dep`
15385        // composite vs the prior sibling family's `String` scalar).
15386        // Pins against a future silent detour that returned an owned
15387        // `Vec<Dep>` (which would type-check but silently clone on every
15388        // accessor call, breaking the zero-cost projection every peer
15389        // sibling slice accessor carries), a `[""] → []` collapse (which
15390        // would silently absorb the `NomeEmpty` refusal case at the
15391        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15392        // would silently absorb the `DuplicateNome` refusal case at the
15393        // accessor boundary).
15394        for deps in [
15395            vec![],
15396            vec![Dep::simple("", "^0.1")],
15397            vec![Dep::simple("caixa-teia", "^0.1")],
15398            vec![
15399                Dep::simple("caixa-teia", "^0.1"),
15400                Dep::simple("caixa-core", "^0.1"),
15401            ],
15402            vec![
15403                Dep::simple("caixa-teia", "^0.1"),
15404                Dep::simple("caixa-teia", "^0.2"),
15405            ],
15406        ] {
15407            let c = caixa_with_deps(deps.clone());
15408            assert_eq!(
15409                c.deps(),
15410                deps.as_slice(),
15411                "Caixa::deps must return :deps verbatim (got {:?}, \
15412                 expected {deps:?})",
15413                c.deps(),
15414            );
15415            assert_eq!(
15416                c.deps(),
15417                c.deps.as_slice(),
15418                "Caixa::deps must element-equal the raw \
15419                 `self.deps.as_slice()` field access across every \
15420                 value in the Vec<Dep> accept-set",
15421            );
15422        }
15423    }
15424
15425    #[test]
15426    fn validate_deps_duplicate_arm_routes_through_accessor() {
15427        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
15428        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
15429        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
15430        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
15431        // "^0.2")], .. }` must surface the `DuplicateNome { list:
15432        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
15433        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
15434        // form) must pass validate. The pair jointly pins the accessor +
15435        // validate-gate composition: any future silent detour that had
15436        // the accessor return a dedupped slice on the `[a, a]` arm (a
15437        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15438        // would silently absorb the `DuplicateNome` refusal at the
15439        // accessor boundary and the validate gate would accept a
15440        // struct-literal `Caixa` carrying the drift — the composition
15441        // pin catches that at caixa-core build time.
15442        //
15443        // Peer of the per-`Caixa`
15444        // `validate_autores_empty_entry_arm_routes_through_accessor`
15445        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15446        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15447        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15448        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
15449        // (611f78b) accessor-composition pins on the sibling `&[T]`-
15450        // composition axes — same "the validate gate must route through
15451        // the substrate-primitive typed dispatch" discipline extended
15452        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
15453        // composition surface, opening the outer-`Caixa` dependency-slot
15454        // arm of the composition-pin family.
15455        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15456        let err = c.validate_deps().unwrap_err();
15457        assert!(
15458            matches!(
15459                err,
15460                DepError::DuplicateNome { ref nome, list } if nome == "d"
15461                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
15462            ),
15463            "validate_deps must reject deps == \
15464             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15465             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
15466             accessor and the validate gate must route through the \
15467             same substrate-primitive typed dispatch on the :deps \
15468             within-list duplicate arm (got {err:?})",
15469        );
15470        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
15471        assert!(
15472            c.validate_deps().is_ok(),
15473            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
15474             (the canonical single-entry form)",
15475        );
15476    }
15477
15478    #[test]
15479    fn deps_projects_slice_by_borrow() {
15480        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
15481        // — the returned slice borrows the underlying `Vec<Dep>` storage
15482        // of the `:deps` slot and the accessor must not clone the
15483        // backing `Vec` on every call. Peer of the per-`Caixa`
15484        // `autores_projects_slice_by_borrow` (b5d813f),
15485        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15486        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15487        // `exe_projects_slice_by_borrow` (65d9527), and
15488        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15489        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15490        // axes — the accessor's returned slice must borrow from `&self`
15491        // (the returned reference's lifetime is tied to `&self`), and
15492        // calling the accessor twice on the same [`Caixa`] must yield
15493        // slices that are pointer-equal (the underlying byte-buffer is
15494        // the storage `Vec`'s allocation, not a fresh copy) as well as
15495        // value-equal (idempotent, no side effects on `&self`).
15496        //
15497        // Pins against a future silent detour that returned an owned
15498        // `Vec<Dep>` (which would type-check but silently clone on
15499        // every call), a `&Vec<Dep>` return (which would leak the
15500        // backing `Vec`'s grow/push/reserve surface no downstream
15501        // consumer reaches for), or a one-arm-only accessor that
15502        // returned a saturating value on some sentinel input.
15503        for deps in [
15504            vec![],
15505            vec![Dep::simple("caixa-teia", "^0.1")],
15506            vec![
15507                Dep::simple("caixa-teia", "^0.1"),
15508                Dep::simple("caixa-core", "^0.1"),
15509            ],
15510        ] {
15511            let c = caixa_with_deps(deps.clone());
15512            let first = c.deps();
15513            let second = c.deps();
15514            assert_eq!(
15515                first, second,
15516                "Caixa::deps must be idempotent — two successive calls \
15517                 on the same &self must return the same &[Dep]",
15518            );
15519            assert_eq!(
15520                first.as_ptr(),
15521                second.as_ptr(),
15522                "Caixa::deps must borrow the underlying Vec<Dep> \
15523                 storage — two successive calls must return slices \
15524                 with the same backing pointer (a fresh Vec<Dep> clone \
15525                 would change the pointer on every call)",
15526            );
15527            assert_eq!(
15528                first,
15529                deps.as_slice(),
15530                "Caixa::deps must return :deps verbatim by borrow — \
15531                 got {first:?}, expected {deps:?}",
15532            );
15533        }
15534    }
15535
15536    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
15537
15538    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
15539        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15540        c.deps_dev = deps_dev;
15541        c
15542    }
15543
15544    #[test]
15545    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
15546        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
15547        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
15548        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
15549        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
15550        // access across every representative value in the accept-set —
15551        // `[]` (the "no dev deps declared" arm every existing fixture
15552        // without a `:deps-dev` line carries; the [`Caixa::template`]
15553        // scaffold emits `:deps-dev ()`), a canonical single-entry list
15554        // (the shape most consumer caixas carry — a `tatara-check` dev
15555        // pin), a canonical two-entry list (the multi-dev-dep closure),
15556        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
15557        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15558        // `NomeInvalid` but the accessor must ship the raw slot
15559        // verbatim) and a `[a, a]` duplicate (validate rejects through
15560        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
15561        // ship the raw slot verbatim so struct-literal fixtures continue
15562        // to expose the duplicate at the accessor).
15563        //
15564        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
15565        // pin on the substrate primitive — closes the outer-`Caixa`
15566        // dependency-slot `&[Dep]` sub-family the sibling
15567        // `deps_returns_deps_slice_verbatim_across_permutations`
15568        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
15569        // slice" projection pattern onto the sibling dev-dep axis —
15570        // pins against a future silent detour that returned an owned
15571        // `Vec<Dep>` (which would type-check but silently clone on every
15572        // accessor call, breaking the zero-cost projection every peer
15573        // sibling slice accessor carries), a `[""] → []` collapse (which
15574        // would silently absorb the `NomeEmpty` refusal case at the
15575        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15576        // would silently absorb the `DuplicateNome` refusal case at the
15577        // accessor boundary).
15578        for deps_dev in [
15579            vec![],
15580            vec![Dep::simple("", "^0.1")],
15581            vec![Dep::simple("tatara-check", "^0.1")],
15582            vec![
15583                Dep::simple("tatara-check", "^0.1"),
15584                Dep::simple("caixa-lint", "^0.1"),
15585            ],
15586            vec![
15587                Dep::simple("tatara-check", "^0.1"),
15588                Dep::simple("tatara-check", "^0.2"),
15589            ],
15590        ] {
15591            let c = caixa_with_deps_dev(deps_dev.clone());
15592            assert_eq!(
15593                c.deps_dev(),
15594                deps_dev.as_slice(),
15595                "Caixa::deps_dev must return :deps-dev verbatim (got \
15596                 {:?}, expected {deps_dev:?})",
15597                c.deps_dev(),
15598            );
15599            assert_eq!(
15600                c.deps_dev(),
15601                c.deps_dev.as_slice(),
15602                "Caixa::deps_dev must element-equal the raw \
15603                 `self.deps_dev.as_slice()` field access across every \
15604                 value in the Vec<Dep> accept-set",
15605            );
15606        }
15607    }
15608
15609    #[test]
15610    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
15611        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
15612        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
15613        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
15614        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
15615        // Dep::simple("d", "^0.2")], .. }` must surface the
15616        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
15617        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
15618        // canonical single-entry form) must pass validate. The pair
15619        // jointly pins the accessor + validate-gate composition: any
15620        // future silent detour that had the accessor return a dedupped
15621        // slice on the `[a, a]` arm (a
15622        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15623        // would silently absorb the `DuplicateNome` refusal at the
15624        // accessor boundary and the validate gate would accept a
15625        // struct-literal `Caixa` carrying the drift — the composition
15626        // pin catches that at caixa-core build time.
15627        //
15628        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
15629        // (ad34b4e) on the sibling `:deps` axis — same "the validate
15630        // gate must route through the substrate-primitive typed
15631        // dispatch" discipline folded onto the sibling `:deps-dev`
15632        // axis, closing the two-list dep-graph composition-pin family.
15633        // The `:deps-dev` diagnostic must carry the
15634        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
15635        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
15636        // offending list unambiguously.
15637        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15638        let err = c.validate_deps().unwrap_err();
15639        assert!(
15640            matches!(
15641                err,
15642                DepError::DuplicateNome { ref nome, list } if nome == "d"
15643                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
15644            ),
15645            "validate_deps must reject deps_dev == \
15646             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15647             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
15648             accessor and the validate gate must route through the \
15649             same substrate-primitive typed dispatch on the :deps-dev \
15650             within-list duplicate arm (got {err:?})",
15651        );
15652        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
15653        assert!(
15654            c.validate_deps().is_ok(),
15655            "validate_deps must accept deps_dev == \
15656             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
15657        );
15658    }
15659
15660    #[test]
15661    fn deps_dev_projects_slice_by_borrow() {
15662        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
15663        // borrow — the returned slice borrows the underlying `Vec<Dep>`
15664        // storage of the `:deps-dev` slot and the accessor must not
15665        // clone the backing `Vec` on every call. Peer of
15666        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
15667        // `:deps` axis, and of the per-`Caixa`
15668        // `autores_projects_slice_by_borrow` (b5d813f),
15669        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15670        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15671        // `exe_projects_slice_by_borrow` (65d9527), and
15672        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15673        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15674        // axes — the accessor's returned slice must borrow from `&self`
15675        // (the returned reference's lifetime is tied to `&self`), and
15676        // calling the accessor twice on the same [`Caixa`] must yield
15677        // slices that are pointer-equal (the underlying byte-buffer is
15678        // the storage `Vec`'s allocation, not a fresh copy) as well as
15679        // value-equal (idempotent, no side effects on `&self`).
15680        //
15681        // Pins against a future silent detour that returned an owned
15682        // `Vec<Dep>` (which would type-check but silently clone on
15683        // every call), a `&Vec<Dep>` return (which would leak the
15684        // backing `Vec`'s grow/push/reserve surface no downstream
15685        // consumer reaches for), or a one-arm-only accessor that
15686        // returned a saturating value on some sentinel input.
15687        for deps_dev in [
15688            vec![],
15689            vec![Dep::simple("tatara-check", "^0.1")],
15690            vec![
15691                Dep::simple("tatara-check", "^0.1"),
15692                Dep::simple("caixa-lint", "^0.1"),
15693            ],
15694        ] {
15695            let c = caixa_with_deps_dev(deps_dev.clone());
15696            let first = c.deps_dev();
15697            let second = c.deps_dev();
15698            assert_eq!(
15699                first, second,
15700                "Caixa::deps_dev must be idempotent — two successive \
15701                 calls on the same &self must return the same &[Dep]",
15702            );
15703            assert_eq!(
15704                first.as_ptr(),
15705                second.as_ptr(),
15706                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
15707                 storage — two successive calls must return slices \
15708                 with the same backing pointer (a fresh Vec<Dep> clone \
15709                 would change the pointer on every call)",
15710            );
15711            assert_eq!(
15712                first,
15713                deps_dev.as_slice(),
15714                "Caixa::deps_dev must return :deps-dev verbatim by \
15715                 borrow — got {first:?}, expected {deps_dev:?}",
15716            );
15717        }
15718    }
15719
15720    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
15721
15722    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
15723        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15724        c.limits = limits;
15725        c
15726    }
15727
15728    #[test]
15729    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
15730        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
15731        // composite optional-composite-reference-shape pin:
15732        // [`Caixa::limits`] must return the `:limits` typed
15733        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
15734        // reference over the same backing storage the raw
15735        // `self.limits.as_ref()` field access borrows from, byte-equal
15736        // across every representative fixture in the accept-set — the
15737        // author-omitted `None` shape (the "engine-default applies"
15738        // partition every downstream Servico M2 overlay emitter treats
15739        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
15740        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
15741        // per-axis cap is `None`, so the peer M2 overlay emitter's
15742        // `.is_empty()`-gated projection still emits nothing but the
15743        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
15744        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
15745        // fixture (only `:memory` set — the canonical shape most
15746        // memory-heavy Servicos carry), and a fully-populated composite
15747        // (every per-axis cap set — the canonical shape a
15748        // sandboxed-by-default Servico carries).
15749        //
15750        // Pins against a future silent detour that returned a fresh-
15751        // cloned [`LimitsSpec`] copy (which would type-check via the
15752        // `Clone` impl but silently break every downstream caller that
15753        // relied on the reference sharing the composite's backing
15754        // identity), a reference to an operator-resolved overlay (the
15755        // future per-cluster `:limits-overrides` slot — its resolution
15756        // must land at exactly this accessor body, not silently divert
15757        // the raw slot away from a second consumer), a
15758        // `None` → `Some(LimitsSpec::default)` cluster-default
15759        // projection (which would collapse the load-bearing
15760        // "author-omitted `:limits` ⇒ engine-default applies" partition
15761        // the peer [`crate::render::servico_m2_overlay`] emitter and
15762        // the peer [`Caixa::declared_servico_slots`] enumerator both
15763        // read), or an axis-shuffled projection (a future detour that
15764        // swapped `memory` and `fuel` through the accessor would
15765        // silently split the paired [`crate::StandardLayout::verify`]
15766        // per-`:limits` shape gate's traversal input from the peer
15767        // `servico_m2_overlay` emitter's projection input).
15768        //
15769        // First outer top-level [`Caixa`] `Option<&Composite>`-return
15770        // composite-reference accessor pin on the substrate primitive
15771        // — opens the outer-`Caixa` `Option<&Composite>` composite-
15772        // reference projection pattern the sibling `:behavior`
15773        // [`crate::BehaviorSpec`] / `:politicas`
15774        // [`crate::aplicacao::MeshPolicy`] / `:placement`
15775        // [`crate::aplicacao::Placement`] / `:entrada`
15776        // [`crate::aplicacao::Entrada`] future outer-composite lifts
15777        // fold on. Peer of the closed M3 outer-composite family the
15778        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
15779        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
15780        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
15781        // reference accessor pins already carry on the outer
15782        // [`crate::AplicacaoSpec`] altitude — extends the outer-
15783        // accessor byte-equal-projection discipline onto the outer
15784        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
15785        use crate::LimitsSpec;
15786        use std::time::Duration;
15787        let fixtures: Vec<Option<LimitsSpec>> = vec![
15788            None,
15789            Some(LimitsSpec::default()),
15790            Some(LimitsSpec {
15791                memory: Some(64 * 1024 * 1024),
15792                ..Default::default()
15793            }),
15794            Some(LimitsSpec {
15795                memory: Some(64 * 1024 * 1024),
15796                fuel: Some(1_000_000),
15797                wall_clock: Some(Duration::from_secs(30)),
15798                cpu: Some(500),
15799            }),
15800        ];
15801        for limits in fixtures {
15802            let c = caixa_with_limits(limits.clone());
15803            assert_eq!(
15804                c.limits(),
15805                limits.as_ref(),
15806                "Caixa::limits must return :limits verbatim (got {:?}, \
15807                 expected {:?})",
15808                c.limits(),
15809                limits.as_ref(),
15810            );
15811            match (c.limits(), c.limits.as_ref()) {
15812                (Some(a), Some(b)) => assert!(
15813                    std::ptr::eq(a, b),
15814                    "Caixa::limits accessor and self.limits.as_ref() \
15815                     field access must borrow the same backing storage \
15816                     — the accessor is the substrate-primitive typed \
15817                     dispatch every downstream Servico-M2-overlay \
15818                     composite consumer must route through, and a \
15819                     reference-identity split would silently break \
15820                     every consumer that relied on the borrow sharing \
15821                     the composite's storage",
15822                ),
15823                (None, None) => {}
15824                _ => panic!(
15825                    "Caixa::limits presence bit must byte-equal \
15826                     self.limits.is_some() — a presence-bit drift would \
15827                     silently split the paired StandardLayout::verify \
15828                     per-`:limits` shape gate's traversal head from \
15829                     the peer render::servico_m2_overlay M2 overlay \
15830                     emitter's traversal head from the peer \
15831                     Caixa::declared_servico_slots M2 declared-slot \
15832                     enumerator's presence probe",
15833                ),
15834            }
15835            assert_eq!(
15836                c.limits().is_some(),
15837                c.limits.is_some(),
15838                "Caixa::limits().is_some() must byte-equal \
15839                 self.limits.is_some() — a presence-bit drift would \
15840                 silently split every downstream Option<&LimitsSpec> \
15841                 consumer's partition on the engine-default arm",
15842            );
15843        }
15844    }
15845
15846    #[test]
15847    fn declared_servico_slots_limits_arm_routes_through_accessor() {
15848        // Composition pin: [`Caixa::declared_servico_slots`]'s
15849        // `:limits` presence-probe arm must key off [`Caixa::limits`],
15850        // not the raw `self.limits.is_some()` field-probe. Structurally:
15851        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
15852        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
15853        // (the presence bit is `Some`, so the M2 kind-coherence gate
15854        // must surface the slot as "declared" even when every per-axis
15855        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
15856        // push the label (the "author omitted the slot entirely"
15857        // partition). The pair jointly pins the accessor + declared-
15858        // slot enumerator composition: any future silent detour that
15859        // had the accessor collapse `Some(LimitsSpec::default())` to
15860        // `None` (a `.filter(|l| !l.is_empty())` projection) would
15861        // silently absorb the "declared but empty" arm at the
15862        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
15863        // kind-coherence gate would silently accept a
15864        // struct-literal `Caixa` carrying the drift.
15865        //
15866        // Peer of the sibling per-`Caixa`
15867        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
15868        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
15869        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
15870        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
15871        // enumerator gate must route through the substrate-primitive
15872        // typed dispatch" discipline extended onto the outer top-level
15873        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
15874        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
15875        // composition-pin family.
15876        use crate::LimitsSpec;
15877        let c = caixa_with_limits(Some(LimitsSpec::default()));
15878        let slots = c.declared_servico_slots();
15879        assert!(
15880            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
15881            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
15882             when `:limits` is Some (even for LimitsSpec::default()) \
15883             — the accessor and the enumerator gate must route through \
15884             the same substrate-primitive typed dispatch on the outer \
15885             :limits presence bit (got slots={slots:?})",
15886        );
15887        let c = caixa_with_limits(None);
15888        let slots = c.declared_servico_slots();
15889        assert!(
15890            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
15891            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
15892             when `:limits` is None — the author-omitted arm must \
15893             route through the accessor's None-return unchanged (got \
15894             slots={slots:?})",
15895        );
15896    }
15897
15898    #[test]
15899    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
15900        // Composition pin: [`crate::render::servico_m2_overlay`]'s
15901        // per-`:limits` M2 overlay emit arm must key off
15902        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
15903        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
15904        // Some(64 MiB), .. default }), .. }` must surface the
15905        // `M2_KEY_LIMITS` key with the per-axis
15906        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
15907        // limits: Some(LimitsSpec::default()), .. }` must omit the
15908        // key entirely (the `.is_empty()`-gated inner arm elides an
15909        // empty composite even when the outer presence bit is `Some`),
15910        // and a `Caixa { limits: None, .. }` must also omit the key
15911        // (the "author omitted the slot entirely" partition). The
15912        // three-fixture family jointly pins the accessor + M2 overlay
15913        // emitter composition: any future silent detour that had the
15914        // accessor return a fresh-cloned copy on the `Some` arm (a
15915        // `LimitsSpec::clone()` projection) would silently break the
15916        // reference-identity pin the peer per-axis
15917        // `serde_yaml::to_value(limits)` projection reads from.
15918        use crate::LimitsSpec;
15919        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
15920        let c = caixa_with_limits(Some(LimitsSpec {
15921            memory: Some(64 * 1024 * 1024),
15922            ..Default::default()
15923        }));
15924        let overlay = servico_m2_overlay(&c).unwrap();
15925        assert!(
15926            overlay.contains_key(M2_KEY_LIMITS),
15927            "servico_m2_overlay must surface M2_KEY_LIMITS when \
15928             `:limits` carries a non-empty composite — the accessor \
15929             and the M2 overlay emitter must route through the same \
15930             substrate-primitive typed dispatch on the outer :limits \
15931             composite (got overlay={overlay:?})",
15932        );
15933        let c = caixa_with_limits(Some(LimitsSpec::default()));
15934        let overlay = servico_m2_overlay(&c).unwrap();
15935        assert!(
15936            !overlay.contains_key(M2_KEY_LIMITS),
15937            "servico_m2_overlay must omit M2_KEY_LIMITS when \
15938             `:limits` is Some(LimitsSpec::default()) — the empty \
15939             composite's `.is_empty()`-gated inner arm must elide \
15940             the key regardless of the outer presence bit (got \
15941             overlay={overlay:?})",
15942        );
15943        let c = caixa_with_limits(None);
15944        let overlay = servico_m2_overlay(&c).unwrap();
15945        assert!(
15946            !overlay.contains_key(M2_KEY_LIMITS),
15947            "servico_m2_overlay must omit M2_KEY_LIMITS when \
15948             `:limits` is None — the author-omitted arm must route \
15949             through the accessor's None-return unchanged (got \
15950             overlay={overlay:?})",
15951        );
15952    }
15953
15954    #[test]
15955    fn limits_projects_option_ref_by_borrow() {
15956        // The by-borrow pin: [`Caixa::limits`] returns
15957        // `Option<&LimitsSpec>` by borrow — the returned reference
15958        // borrows the underlying `Option<LimitsSpec>` storage of the
15959        // `:limits` slot and the accessor must not clone the backing
15960        // composite on every call. Peer of the sibling
15961        // `deps_projects_slice_by_borrow` (ad34b4e) /
15962        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
15963        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
15964        // extended here to the outer [`Caixa`] `Option<&Composite>`-
15965        // return axis: the accessor's returned reference must borrow
15966        // from `&self` (the returned reference's lifetime is tied to
15967        // `&self`), and calling the accessor twice on the same
15968        // [`Caixa`] must yield references that are pointer-equal (the
15969        // underlying byte-buffer is the storage `LimitsSpec`'s
15970        // allocation, not a fresh copy) as well as value-equal
15971        // (idempotent, no side effects on `&self`).
15972        //
15973        // Pins against a future silent detour that returned an owned
15974        // `LimitsSpec` (which would type-check via the `Clone` impl
15975        // but silently clone on every call), a `&LimitsSpec` panic-
15976        // return on the `None` arm (which would collapse the load-
15977        // bearing `Option` presence-bit into a runtime panic), or a
15978        // one-arm-only accessor that returned a saturating composite
15979        // on some sentinel input.
15980        use crate::LimitsSpec;
15981        use std::time::Duration;
15982        for limits in [
15983            Some(LimitsSpec::default()),
15984            Some(LimitsSpec {
15985                memory: Some(64 * 1024 * 1024),
15986                fuel: Some(1_000_000),
15987                wall_clock: Some(Duration::from_secs(30)),
15988                cpu: Some(500),
15989            }),
15990        ] {
15991            let c = caixa_with_limits(limits.clone());
15992            let first = c.limits().unwrap();
15993            let second = c.limits().unwrap();
15994            assert_eq!(
15995                first, second,
15996                "Caixa::limits must be idempotent — two successive \
15997                 calls on the same &self must return the same \
15998                 &LimitsSpec",
15999            );
16000            assert!(
16001                std::ptr::eq(first, second),
16002                "Caixa::limits must borrow the underlying \
16003                 Option<LimitsSpec> storage — two successive calls \
16004                 must return references with the same backing pointer \
16005                 (a fresh LimitsSpec clone would change the pointer \
16006                 on every call)",
16007            );
16008            assert_eq!(
16009                Some(first),
16010                limits.as_ref(),
16011                "Caixa::limits must return :limits verbatim by borrow \
16012                 — got {first:?}, expected {:?}",
16013                limits.as_ref(),
16014            );
16015        }
16016        let c = caixa_with_limits(None);
16017        assert!(
16018            c.limits().is_none(),
16019            "Caixa::limits must return None when :limits is absent — \
16020             the author-omitted arm must project through the \
16021             accessor's Option::None unchanged",
16022        );
16023    }
16024
16025    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
16026
16027    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
16028        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16029        c.behavior = behavior;
16030        c
16031    }
16032
16033    #[test]
16034    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
16035        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
16036        // composite optional-composite-reference-shape pin:
16037        // [`Caixa::behavior`] must return the `:behavior` typed
16038        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
16039        // reference over the same backing storage the raw
16040        // `self.behavior.as_ref()` field access borrows from, byte-equal
16041        // across every representative fixture in the accept-set — the
16042        // author-omitted `None` shape (the "runtime-default applies"
16043        // partition every downstream Servico M2 overlay emitter treats
16044        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
16045        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
16046        // every per-callback path is `None`, so the peer M2 overlay
16047        // emitter's `.is_empty()`-gated projection still emits nothing
16048        // but the outer presence-bit is `Some`, so
16049        // [`Caixa::declared_servico_slots`] still pushes the
16050        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
16051        // (only `:on-state-change` set — the canonical shape a caixa
16052        // that only wires the hot-upgrade migration path carries), and
16053        // a fully-populated composite (every per-callback path set —
16054        // the canonical shape a fully-instrumented gen_server-shaped
16055        // Servico carries).
16056        //
16057        // Peer of the sibling
16058        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16059        // (b2bd9d7) opening fixture-family + reference-identity +
16060        // presence-bit tetrad pin on the outer top-level [`Caixa`]
16061        // `Option<&Composite>`-return sub-family — extended here to the
16062        // second axis of that sub-family so both of the currently-lifted
16063        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
16064        // `:behavior`) carry the same "byte-equal, borrow-shared,
16065        // presence-bit-preserved" outer-accessor discipline.
16066        //
16067        // Pins against a future silent detour that returned a fresh-
16068        // cloned [`crate::BehaviorSpec`] copy (which would type-check
16069        // via the `Clone` impl but silently break every downstream
16070        // caller that relied on the reference sharing the composite's
16071        // backing identity), a reference to an operator-resolved
16072        // overlay (a future per-cluster `:behavior-overrides` slot —
16073        // its resolution must land at exactly this accessor body, not
16074        // silently divert the raw slot away from a second consumer), a
16075        // `None` → `Some(BehaviorSpec::default)` cluster-default
16076        // projection (which would collapse the load-bearing
16077        // "author-omitted `:behavior` ⇒ runtime-default applies"
16078        // partition the peer [`crate::render::servico_m2_overlay`]
16079        // emitter, the peer [`Caixa::declared_servico_slots`]
16080        // enumerator, and the cross-slot
16081        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
16082        // gate all read), or a callback-shuffled projection (a future
16083        // detour that swapped `on_init` and `on_terminate` through the
16084        // accessor would silently split the paired
16085        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
16086        // traversal input from the peer `servico_m2_overlay` emitter's
16087        // projection input from the cross-slot `:state-change`
16088        // composition gate's traversal input).
16089        use crate::BehaviorSpec;
16090        use std::path::PathBuf;
16091        let fixtures: Vec<Option<BehaviorSpec>> = vec![
16092            None,
16093            Some(BehaviorSpec::default()),
16094            Some(BehaviorSpec {
16095                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16096                ..Default::default()
16097            }),
16098            Some(BehaviorSpec {
16099                on_init: Some(PathBuf::from("lib/init.lisp")),
16100                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16101                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16102                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16103                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16104                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16105            }),
16106        ];
16107        for behavior in fixtures {
16108            let c = caixa_with_behavior(behavior.clone());
16109            assert_eq!(
16110                c.behavior(),
16111                behavior.as_ref(),
16112                "Caixa::behavior must return :behavior verbatim (got \
16113                 {:?}, expected {:?})",
16114                c.behavior(),
16115                behavior.as_ref(),
16116            );
16117            match (c.behavior(), c.behavior.as_ref()) {
16118                (Some(a), Some(b)) => assert!(
16119                    std::ptr::eq(a, b),
16120                    "Caixa::behavior accessor and self.behavior.as_ref() \
16121                     field access must borrow the same backing storage \
16122                     — the accessor is the substrate-primitive typed \
16123                     dispatch every downstream Servico-M2-overlay \
16124                     composite consumer must route through, and a \
16125                     reference-identity split would silently break \
16126                     every consumer that relied on the borrow sharing \
16127                     the composite's storage",
16128                ),
16129                (None, None) => {}
16130                _ => panic!(
16131                    "Caixa::behavior presence bit must byte-equal \
16132                     self.behavior.is_some() — a presence-bit drift \
16133                     would silently split the paired \
16134                     StandardLayout::verify per-`:behavior` shape \
16135                     gate's traversal head from the peer \
16136                     render::servico_m2_overlay M2 overlay emitter's \
16137                     traversal head from the cross-slot \
16138                     validate_upgrade_from_against_behavior \
16139                     composition gate's traversal head from the peer \
16140                     Caixa::declared_servico_slots M2 declared-slot \
16141                     enumerator's presence probe",
16142                ),
16143            }
16144            assert_eq!(
16145                c.behavior().is_some(),
16146                c.behavior.is_some(),
16147                "Caixa::behavior().is_some() must byte-equal \
16148                 self.behavior.is_some() — a presence-bit drift would \
16149                 silently split every downstream Option<&BehaviorSpec> \
16150                 consumer's partition on the runtime-default arm",
16151            );
16152        }
16153    }
16154
16155    #[test]
16156    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
16157        // Composition pin: [`Caixa::declared_servico_slots`]'s
16158        // `:behavior` presence-probe arm must key off
16159        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
16160        // field-probe. Structurally: a `Caixa { behavior:
16161        // Some(BehaviorSpec::default()), .. }` must still push
16162        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
16163        // presence bit is `Some`, so the M2 kind-coherence gate must
16164        // surface the slot as "declared" even when every per-callback
16165        // path is unset), and a `Caixa { behavior: None, .. }` must
16166        // NOT push the label (the "author omitted the slot entirely"
16167        // partition). The pair jointly pins the accessor + declared-
16168        // slot enumerator composition: any future silent detour that
16169        // had the accessor collapse `Some(BehaviorSpec::default())`
16170        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
16171        // silently absorb the "declared but empty" arm at the
16172        // accessor boundary and the
16173        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
16174        // kind-coherence gate would silently accept a struct-literal
16175        // `Caixa` carrying the drift.
16176        //
16177        // Peer of the sibling
16178        // `declared_servico_slots_limits_arm_routes_through_accessor`
16179        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16180        // `Option<&LimitsSpec>` arm of the same
16181        // [`Caixa::declared_servico_slots`] M2 declared-slot
16182        // enumerator's traversal — same "the enumerator gate must
16183        // route through the substrate-primitive typed dispatch"
16184        // discipline extended onto the outer top-level [`Caixa`]
16185        // `Option<&BehaviorSpec>`-composition surface.
16186        use crate::BehaviorSpec;
16187        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16188        let slots = c.declared_servico_slots();
16189        assert!(
16190            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16191            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
16192             when `:behavior` is Some (even for BehaviorSpec::default()) \
16193             — the accessor and the enumerator gate must route through \
16194             the same substrate-primitive typed dispatch on the outer \
16195             :behavior presence bit (got slots={slots:?})",
16196        );
16197        let c = caixa_with_behavior(None);
16198        let slots = c.declared_servico_slots();
16199        assert!(
16200            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16201            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
16202             when `:behavior` is None — the author-omitted arm must \
16203             route through the accessor's None-return unchanged (got \
16204             slots={slots:?})",
16205        );
16206    }
16207
16208    #[test]
16209    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
16210        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16211        // per-`:behavior` M2 overlay emit arm must key off
16212        // [`Caixa::behavior`], not the raw `&caixa.behavior`
16213        // field-borrow. Structurally: a `Caixa { behavior:
16214        // Some(BehaviorSpec { on_state_change: Some(...), .. default
16215        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
16216        // per-callback `onStateChange` sub-mapping in the overlay, a
16217        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
16218        // must omit the key entirely (the `.is_empty()`-gated inner
16219        // arm elides an empty composite even when the outer presence
16220        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
16221        // also omit the key (the "author omitted the slot entirely"
16222        // partition). The three-fixture family jointly pins the
16223        // accessor + M2 overlay emitter composition: any future
16224        // silent detour that had the accessor return a fresh-cloned
16225        // copy on the `Some` arm (a `BehaviorSpec::clone()`
16226        // projection) would silently break the reference-identity
16227        // pin the peer per-callback `serde_yaml::to_value(behavior)`
16228        // projection reads from.
16229        //
16230        // Peer of the sibling
16231        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16232        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16233        // `Option<&LimitsSpec>` arm of the same
16234        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
16235        // traversal — same "the emitter must route through the
16236        // substrate-primitive typed dispatch on the outer composite"
16237        // discipline extended onto the outer top-level [`Caixa`]
16238        // `Option<&BehaviorSpec>`-composition surface.
16239        use crate::BehaviorSpec;
16240        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
16241        use std::path::PathBuf;
16242        let c = caixa_with_behavior(Some(BehaviorSpec {
16243            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16244            ..Default::default()
16245        }));
16246        let overlay = servico_m2_overlay(&c).unwrap();
16247        assert!(
16248            overlay.contains_key(M2_KEY_BEHAVIOR),
16249            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
16250             `:behavior` carries a non-empty composite — the accessor \
16251             and the M2 overlay emitter must route through the same \
16252             substrate-primitive typed dispatch on the outer :behavior \
16253             composite (got overlay={overlay:?})",
16254        );
16255        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16256        let overlay = servico_m2_overlay(&c).unwrap();
16257        assert!(
16258            !overlay.contains_key(M2_KEY_BEHAVIOR),
16259            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16260             `:behavior` is Some(BehaviorSpec::default()) — the empty \
16261             composite's `.is_empty()`-gated inner arm must elide the \
16262             key regardless of the outer presence bit (got \
16263             overlay={overlay:?})",
16264        );
16265        let c = caixa_with_behavior(None);
16266        let overlay = servico_m2_overlay(&c).unwrap();
16267        assert!(
16268            !overlay.contains_key(M2_KEY_BEHAVIOR),
16269            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16270             `:behavior` is None — the author-omitted arm must route \
16271             through the accessor's None-return unchanged (got \
16272             overlay={overlay:?})",
16273        );
16274    }
16275
16276    #[test]
16277    fn behavior_projects_option_ref_by_borrow() {
16278        // The by-borrow pin: [`Caixa::behavior`] returns
16279        // `Option<&BehaviorSpec>` by borrow — the returned reference
16280        // borrows the underlying `Option<BehaviorSpec>` storage of the
16281        // `:behavior` slot and the accessor must not clone the backing
16282        // composite on every call. Peer of the sibling
16283        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
16284        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
16285        // return sub-family — extended here to the second axis of the
16286        // same sub-family: the accessor's returned reference must
16287        // borrow from `&self` (the returned reference's lifetime is
16288        // tied to `&self`), and calling the accessor twice on the same
16289        // [`Caixa`] must yield references that are pointer-equal (the
16290        // underlying byte-buffer is the storage `BehaviorSpec`'s
16291        // allocation, not a fresh copy) as well as value-equal
16292        // (idempotent, no side effects on `&self`).
16293        //
16294        // Pins against a future silent detour that returned an owned
16295        // `BehaviorSpec` (which would type-check via the `Clone` impl
16296        // but silently clone on every call), a `&BehaviorSpec` panic-
16297        // return on the `None` arm (which would collapse the load-
16298        // bearing `Option` presence-bit into a runtime panic), or a
16299        // one-arm-only accessor that returned a saturating composite
16300        // on some sentinel input.
16301        use crate::BehaviorSpec;
16302        use std::path::PathBuf;
16303        for behavior in [
16304            Some(BehaviorSpec::default()),
16305            Some(BehaviorSpec {
16306                on_init: Some(PathBuf::from("lib/init.lisp")),
16307                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16308                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16309                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16310                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16311                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16312            }),
16313        ] {
16314            let c = caixa_with_behavior(behavior.clone());
16315            let first = c.behavior().unwrap();
16316            let second = c.behavior().unwrap();
16317            assert_eq!(
16318                first, second,
16319                "Caixa::behavior must be idempotent — two successive \
16320                 calls on the same &self must return the same \
16321                 &BehaviorSpec",
16322            );
16323            assert!(
16324                std::ptr::eq(first, second),
16325                "Caixa::behavior must borrow the underlying \
16326                 Option<BehaviorSpec> storage — two successive calls \
16327                 must return references with the same backing pointer \
16328                 (a fresh BehaviorSpec clone would change the pointer \
16329                 on every call)",
16330            );
16331            assert_eq!(
16332                Some(first),
16333                behavior.as_ref(),
16334                "Caixa::behavior must return :behavior verbatim by \
16335                 borrow — got {first:?}, expected {:?}",
16336                behavior.as_ref(),
16337            );
16338        }
16339        let c = caixa_with_behavior(None);
16340        assert!(
16341            c.behavior().is_none(),
16342            "Caixa::behavior must return None when :behavior is absent \
16343             — the author-omitted arm must project through the \
16344             accessor's Option::None unchanged",
16345        );
16346    }
16347
16348    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
16349
16350    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
16351        use crate::aplicacao::{Membro, WitContract};
16352        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16353        c.kind = CaixaKind::Aplicacao;
16354        c.membros = vec![Membro {
16355            caixa: "a".into(),
16356            versao: "^0.1".into(),
16357        }];
16358        c.contratos = vec![WitContract {
16359            de: "a".into(),
16360            para: "a".into(),
16361            wit: "wasi:http/proxy".into(),
16362            endpoint: Some("/x".into()),
16363            subject: None,
16364            slot: None,
16365        }];
16366        c.politicas = politicas;
16367        c
16368    }
16369
16370    #[test]
16371    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
16372        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
16373        // composite optional-composite-reference-shape pin:
16374        // [`Caixa::politicas`] must return the `:politicas` typed
16375        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
16376        // reference over the same backing storage the raw
16377        // `self.politicas.as_ref()` field access borrows from,
16378        // byte-equal across every representative fixture in the
16379        // accept-set — the author-omitted `None` shape (the "cluster-
16380        // default applies" partition every downstream mesh-artifact
16381        // emitter treats as "emit no `:politicas` overlay"), the
16382        // empty-composite `Some(MeshPolicy { .. default })` shape
16383        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
16384        // per-axis mesh-policy scalar is `None`, so the peer inner
16385        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
16386        // caixa-mesh overlay elides every per-axis emit but the outer
16387        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
16388        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
16389        // single-axis fixture (only `:timeout` set — the canonical
16390        // shape a latency-sensitive Aplicacao carries), and a
16391        // fully-populated composite (every per-axis mesh-policy
16392        // scalar set — the canonical shape a fully-governed
16393        // Aplicacao carries).
16394        //
16395        // Pins against a future silent detour that returned a fresh-
16396        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
16397        // type-check via the `Clone` impl but silently break every
16398        // downstream caller that relied on the reference sharing the
16399        // composite's backing identity), a reference to an operator-
16400        // resolved overlay (the future per-cluster
16401        // `:politicas-overrides` slot — its resolution must land at
16402        // exactly this accessor body, not silently divert the raw
16403        // slot away from the peer [`Caixa::declared_mesh_slots`]
16404        // enumerator's presence probe), a
16405        // `None` → `Some(MeshPolicy::default)` cluster-default
16406        // projection (which would collapse the load-bearing
16407        // "author-omitted `:politicas` ⇒ cluster-default applies"
16408        // partition the peer [`Caixa::declared_mesh_slots`]
16409        // enumerator and the peer [`Caixa::aplicacao_view`]
16410        // Aplicacao-composition seed both read), or an axis-shuffled
16411        // projection (a future detour that swapped `timeout` and
16412        // `retries` through the accessor would silently split the
16413        // paired [`Caixa::aplicacao_view`] seed's fold input from the
16414        // sibling M3 mesh-artifact emitter's projection input).
16415        //
16416        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
16417        // composite-reference accessor pin on the substrate primitive
16418        // — peer of the sibling
16419        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16420        // (b2bd9d7) and
16421        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16422        // (35d8b52) opening tetrad pins on the outer top-level
16423        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16424        // here to the first of the three M3 mesh-slot axes so the
16425        // opening third of the outer `Option<&Composite>` sub-family
16426        // carries the same "byte-equal, borrow-shared, presence-bit-
16427        // preserved" outer-accessor discipline.
16428        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16429        use std::time::Duration;
16430        let fixtures: Vec<Option<MeshPolicy>> = vec![
16431            None,
16432            Some(MeshPolicy::default()),
16433            Some(MeshPolicy {
16434                timeout: Some(Duration::from_secs(30)),
16435                ..Default::default()
16436            }),
16437            Some(MeshPolicy {
16438                timeout: Some(Duration::from_secs(30)),
16439                retries: Some(3),
16440                circuit_breaker: Some(CircuitBreaker {
16441                    max_failures: 5,
16442                    window: Duration::from_secs(60),
16443                }),
16444                mtls_required: Some(true),
16445                rate_limit: Some(RateLimit {
16446                    rate: 100,
16447                    window: Duration::from_secs(1),
16448                }),
16449            }),
16450        ];
16451        for politicas in fixtures {
16452            let c = caixa_aplicacao_with_politicas(politicas.clone());
16453            assert_eq!(
16454                c.politicas(),
16455                politicas.as_ref(),
16456                "Caixa::politicas must return :politicas verbatim (got \
16457                 {:?}, expected {:?})",
16458                c.politicas(),
16459                politicas.as_ref(),
16460            );
16461            match (c.politicas(), c.politicas.as_ref()) {
16462                (Some(a), Some(b)) => assert!(
16463                    std::ptr::eq(a, b),
16464                    "Caixa::politicas accessor and self.politicas.as_ref() \
16465                     field access must borrow the same backing storage \
16466                     — the accessor is the substrate-primitive typed \
16467                     dispatch every downstream Aplicacao-mesh-overlay \
16468                     composite consumer must route through, and a \
16469                     reference-identity split would silently break \
16470                     every consumer that relied on the borrow sharing \
16471                     the composite's storage",
16472                ),
16473                (None, None) => {}
16474                _ => panic!(
16475                    "Caixa::politicas presence bit must byte-equal \
16476                     self.politicas.is_some() — a presence-bit drift \
16477                     would silently split the paired \
16478                     Caixa::aplicacao_view Aplicacao-composition seed's \
16479                     traversal head from the peer \
16480                     Caixa::declared_mesh_slots M3 declared-slot \
16481                     enumerator's presence probe",
16482                ),
16483            }
16484            assert_eq!(
16485                c.politicas().is_some(),
16486                c.politicas.is_some(),
16487                "Caixa::politicas().is_some() must byte-equal \
16488                 self.politicas.is_some() — a presence-bit drift would \
16489                 silently split every downstream Option<&MeshPolicy> \
16490                 consumer's partition on the cluster-default arm",
16491            );
16492        }
16493    }
16494
16495    #[test]
16496    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
16497        // Composition pin: [`Caixa::declared_mesh_slots`]'s
16498        // `:politicas` presence-probe arm must key off
16499        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
16500        // field-probe. Structurally: a `Caixa { politicas:
16501        // Some(MeshPolicy::default()), .. }` must still push
16502        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
16503        // presence bit is `Some`, so the M3 kind-coherence gate must
16504        // surface the slot as "declared" even when every per-axis
16505        // scalar is unset), and a `Caixa { politicas: None, .. }` must
16506        // NOT push the label (the "author omitted the slot entirely"
16507        // partition). The pair jointly pins the accessor + declared-
16508        // slot enumerator composition: any future silent detour that
16509        // had the accessor collapse `Some(MeshPolicy::default())` to
16510        // `None` (a `.filter(|p| !p.is_empty())` projection) would
16511        // silently absorb the "declared but empty" arm at the
16512        // accessor boundary and the
16513        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16514        // coherence gate would silently accept a struct-literal
16515        // `Caixa` carrying the drift.
16516        //
16517        // Peer of the sibling
16518        // `declared_servico_slots_limits_arm_routes_through_accessor`
16519        // (b2bd9d7) and
16520        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16521        // (35d8b52) composition pins on the sibling `:limits` /
16522        // `:behavior` outer-`Option<&Composite>` arms of the peer
16523        // [`Caixa::declared_servico_slots`] M2 declared-slot
16524        // enumerator's traversal — same "the enumerator gate must
16525        // route through the substrate-primitive typed dispatch"
16526        // discipline extended onto the outer top-level [`Caixa`] M3
16527        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
16528        // enumerator carries the same routing invariant as its M2
16529        // sibling.
16530        use crate::aplicacao::MeshPolicy;
16531        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16532        let slots = c.declared_mesh_slots();
16533        assert!(
16534            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16535            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
16536             when `:politicas` is Some (even for MeshPolicy::default()) \
16537             — the accessor and the enumerator gate must route through \
16538             the same substrate-primitive typed dispatch on the outer \
16539             :politicas presence bit (got slots={slots:?})",
16540        );
16541        let c = caixa_aplicacao_with_politicas(None);
16542        let slots = c.declared_mesh_slots();
16543        assert!(
16544            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16545            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
16546             when `:politicas` is None — the author-omitted arm must \
16547             route through the accessor's None-return unchanged (got \
16548             slots={slots:?})",
16549        );
16550    }
16551
16552    #[test]
16553    fn aplicacao_view_politicas_arm_folds_through_accessor() {
16554        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
16555        // Aplicacao-composition seed must fold through
16556        // [`Caixa::politicas`], not the raw
16557        // `self.politicas.clone().unwrap_or_default()` field-borrow.
16558        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
16559        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
16560        // must surface a projected [`crate::AplicacaoSpec`] whose
16561        // `politicas().timeout()` field byte-equals the outer
16562        // composite's `timeout` scalar (the fold must project the
16563        // authored composite verbatim), a `Caixa { politicas:
16564        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
16565        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
16566        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
16567        // fold's empty-composite arm collapses to the same default the
16568        // author-omitted arm does), and a `Caixa { politicas: None,
16569        // kind: Aplicacao, .. }` must surface an
16570        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
16571        // [`crate::aplicacao::MeshPolicy::default`] (the "author
16572        // omitted the slot entirely" arm folds through the
16573        // `unwrap_or_default` onto the cluster-default). The triad
16574        // jointly pins the accessor + Aplicacao-composition seed
16575        // composition: any future silent detour that had the accessor
16576        // divert the raw slot away from the seed's fold (an operator-
16577        // resolved overlay's default-fold arm silently differing from
16578        // the raw slot's default-fold arm) would silently split the
16579        // build-time mesh-artifact emission gate from the caixa-mesh
16580        // renderer's Aplicacao-view input at the composition boundary.
16581        use crate::aplicacao::MeshPolicy;
16582        use std::time::Duration;
16583        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
16584            timeout: Some(Duration::from_secs(30)),
16585            ..Default::default()
16586        }));
16587        let view = c.aplicacao_view().unwrap();
16588        assert_eq!(
16589            view.politicas().timeout(),
16590            Some(Duration::from_secs(30)),
16591            "Caixa::aplicacao_view must fold the authored :politicas \
16592             :timeout scalar through the accessor verbatim onto the \
16593             projected AplicacaoSpec — a future silent detour at the \
16594             seed's fold arm would surface here as a projected-scalar \
16595             drift (got {:?})",
16596            view.politicas().timeout(),
16597        );
16598        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16599        let view = c.aplicacao_view().unwrap();
16600        assert_eq!(
16601            view.politicas(),
16602            &MeshPolicy::default(),
16603            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
16604             through the accessor onto MeshPolicy::default — the empty- \
16605             composite arm collapses to the same default the author- \
16606             omitted arm does (got {:?})",
16607            view.politicas(),
16608        );
16609        let c = caixa_aplicacao_with_politicas(None);
16610        let view = c.aplicacao_view().unwrap();
16611        assert_eq!(
16612            view.politicas(),
16613            &MeshPolicy::default(),
16614            "Caixa::aplicacao_view must fold None through the accessor's \
16615             unwrap_or_default onto MeshPolicy::default — the author- \
16616             omitted arm must route through the accessor's None-return \
16617             unchanged (got {:?})",
16618            view.politicas(),
16619        );
16620    }
16621
16622    #[test]
16623    fn politicas_projects_option_ref_by_borrow() {
16624        // The by-borrow pin: [`Caixa::politicas`] returns
16625        // `Option<&MeshPolicy>` by borrow — the returned reference
16626        // borrows the underlying `Option<MeshPolicy>` storage of the
16627        // `:politicas` slot and the accessor must not clone the
16628        // backing composite on every call. Peer of the sibling
16629        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
16630        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
16631        // pins on the outer top-level [`Caixa`]
16632        // `Option<&Composite>`-return sub-family — extended here to
16633        // the third axis of the same sub-family: the accessor's
16634        // returned reference must borrow from `&self` (the returned
16635        // reference's lifetime is tied to `&self`), and calling the
16636        // accessor twice on the same [`Caixa`] must yield references
16637        // that are pointer-equal (the underlying byte-buffer is the
16638        // storage `MeshPolicy`'s allocation, not a fresh copy) as
16639        // well as value-equal (idempotent, no side effects on
16640        // `&self`).
16641        //
16642        // Pins against a future silent detour that returned an owned
16643        // `MeshPolicy` (which would type-check via the `Clone` impl
16644        // but silently clone on every call), a `&MeshPolicy` panic-
16645        // return on the `None` arm (which would collapse the load-
16646        // bearing `Option` presence-bit into a runtime panic), or a
16647        // one-arm-only accessor that returned a saturating composite
16648        // on some sentinel input.
16649        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16650        use std::time::Duration;
16651        for politicas in [
16652            Some(MeshPolicy::default()),
16653            Some(MeshPolicy {
16654                timeout: Some(Duration::from_secs(30)),
16655                retries: Some(3),
16656                circuit_breaker: Some(CircuitBreaker {
16657                    max_failures: 5,
16658                    window: Duration::from_secs(60),
16659                }),
16660                mtls_required: Some(true),
16661                rate_limit: Some(RateLimit {
16662                    rate: 100,
16663                    window: Duration::from_secs(1),
16664                }),
16665            }),
16666        ] {
16667            let c = caixa_aplicacao_with_politicas(politicas.clone());
16668            let first = c.politicas().unwrap();
16669            let second = c.politicas().unwrap();
16670            assert_eq!(
16671                first, second,
16672                "Caixa::politicas must be idempotent — two successive \
16673                 calls on the same &self must return the same \
16674                 &MeshPolicy",
16675            );
16676            assert!(
16677                std::ptr::eq(first, second),
16678                "Caixa::politicas must borrow the underlying \
16679                 Option<MeshPolicy> storage — two successive calls \
16680                 must return references with the same backing pointer \
16681                 (a fresh MeshPolicy clone would change the pointer on \
16682                 every call)",
16683            );
16684            assert_eq!(
16685                Some(first),
16686                politicas.as_ref(),
16687                "Caixa::politicas must return :politicas verbatim by \
16688                 borrow — got {first:?}, expected {:?}",
16689                politicas.as_ref(),
16690            );
16691        }
16692        let c = caixa_aplicacao_with_politicas(None);
16693        assert!(
16694            c.politicas().is_none(),
16695            "Caixa::politicas must return None when :politicas is \
16696             absent — the author-omitted arm must project through the \
16697             accessor's Option::None unchanged",
16698        );
16699    }
16700
16701    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
16702
16703    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
16704        use crate::aplicacao::{Membro, WitContract};
16705        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16706        c.kind = CaixaKind::Aplicacao;
16707        c.membros = vec![Membro {
16708            caixa: "a".into(),
16709            versao: "^0.1".into(),
16710        }];
16711        c.contratos = vec![WitContract {
16712            de: "a".into(),
16713            para: "a".into(),
16714            wit: "wasi:http/proxy".into(),
16715            endpoint: Some("/x".into()),
16716            subject: None,
16717            slot: None,
16718        }];
16719        c.placement = placement;
16720        c
16721    }
16722
16723    #[test]
16724    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
16725        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
16726        // composite optional-composite-reference-shape pin:
16727        // [`Caixa::placement`] must return the `:placement` typed
16728        // `Option<Placement>` verbatim as an `Option<&Placement>`
16729        // reference over the same backing storage the raw
16730        // `self.placement.as_ref()` field access borrows from,
16731        // byte-equal across every representative fixture in the
16732        // accept-set — the author-omitted `None` shape (the
16733        // "cluster-default applies" partition every downstream mesh-
16734        // artifact emitter treats as "emit no `:placement` overlay"),
16735        // the empty-composite `Some(Placement { .. default })` shape
16736        // (`estrategia: SingleNode`, empty clusters, no shard-key /
16737        // affinity — the outer presence-bit is `Some` so
16738        // [`Caixa::declared_mesh_slots`] still pushes the
16739        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
16740        // `Replicated`-on-two-clusters fixture (the canonical shape a
16741        // stateless HTTP Aplicacao carries), and a fully-populated
16742        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
16743        // shape a stateful Akka-style cluster-sharding Aplicacao
16744        // carries).
16745        //
16746        // Pins against a future silent detour that returned a fresh-
16747        // cloned [`crate::aplicacao::Placement`] copy (which would
16748        // type-check via the `Clone` impl but silently break every
16749        // downstream caller that relied on the reference sharing the
16750        // composite's backing identity), a reference to an operator-
16751        // resolved overlay (the future per-cluster
16752        // `:placement-overrides` slot — its resolution must land at
16753        // exactly this accessor body, not silently divert the raw
16754        // slot away from the peer [`Caixa::declared_mesh_slots`]
16755        // enumerator's presence probe), a `None` →
16756        // `Some(Placement::default)` cluster-default projection (which
16757        // would collapse the load-bearing "author-omitted `:placement`
16758        // ⇒ cluster-default applies" partition the peer
16759        // [`Caixa::declared_mesh_slots`] enumerator and the peer
16760        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
16761        // read), or an axis-shuffled projection (a future detour that
16762        // swapped `clusters` and `affinity` through the accessor would
16763        // silently split the paired [`Caixa::aplicacao_view`] seed's
16764        // fold input from the sibling M3 mesh-artifact emitter's
16765        // projection input).
16766        //
16767        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
16768        // composite-reference accessor pin on the substrate primitive
16769        // — peer of the sibling
16770        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16771        // (b2bd9d7),
16772        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16773        // (35d8b52), and
16774        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
16775        // (5d23d29) opening triad pins on the outer top-level
16776        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16777        // here to the second of the three M3 mesh-slot axes so the
16778        // opening four-fifths of the outer `Option<&Composite>` sub-
16779        // family carries the same "byte-equal, borrow-shared,
16780        // presence-bit-preserved" outer-accessor discipline.
16781        use crate::aplicacao::{Placement, PlacementStrategy};
16782        let fixtures: Vec<Option<Placement>> = vec![
16783            None,
16784            Some(Placement::default()),
16785            Some(Placement {
16786                estrategia: PlacementStrategy::Replicated,
16787                clusters: vec!["rio".into(), "sao-paulo".into()],
16788                affinity: None,
16789                shard_key: None,
16790            }),
16791            Some(Placement {
16792                estrategia: PlacementStrategy::Sharded,
16793                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
16794                affinity: Some("data-locality".into()),
16795                shard_key: Some("$tenantId".into()),
16796            }),
16797        ];
16798        for placement in fixtures {
16799            let c = caixa_aplicacao_with_placement(placement.clone());
16800            assert_eq!(
16801                c.placement(),
16802                placement.as_ref(),
16803                "Caixa::placement must return :placement verbatim (got \
16804                 {:?}, expected {:?})",
16805                c.placement(),
16806                placement.as_ref(),
16807            );
16808            match (c.placement(), c.placement.as_ref()) {
16809                (Some(a), Some(b)) => assert!(
16810                    std::ptr::eq(a, b),
16811                    "Caixa::placement accessor and self.placement.as_ref() \
16812                     field access must borrow the same backing storage \
16813                     — the accessor is the substrate-primitive typed \
16814                     dispatch every downstream Aplicacao-distribution- \
16815                     overlay composite consumer must route through, and \
16816                     a reference-identity split would silently break \
16817                     every consumer that relied on the borrow sharing \
16818                     the composite's storage",
16819                ),
16820                (None, None) => {}
16821                _ => panic!(
16822                    "Caixa::placement presence bit must byte-equal \
16823                     self.placement.is_some() — a presence-bit drift \
16824                     would silently split the paired \
16825                     Caixa::aplicacao_view Aplicacao-composition seed's \
16826                     traversal head from the peer \
16827                     Caixa::declared_mesh_slots M3 declared-slot \
16828                     enumerator's presence probe",
16829                ),
16830            }
16831            assert_eq!(
16832                c.placement().is_some(),
16833                c.placement.is_some(),
16834                "Caixa::placement().is_some() must byte-equal \
16835                 self.placement.is_some() — a presence-bit drift would \
16836                 silently split every downstream Option<&Placement> \
16837                 consumer's partition on the cluster-default arm",
16838            );
16839        }
16840    }
16841
16842    #[test]
16843    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
16844        // Composition pin: [`Caixa::declared_mesh_slots`]'s
16845        // `:placement` presence-probe arm must key off
16846        // [`Caixa::placement`], not the raw `self.placement.is_some()`
16847        // field-probe. Structurally: a `Caixa { placement:
16848        // Some(Placement::default()), .. }` must still push
16849        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
16850        // presence bit is `Some`, so the M3 kind-coherence gate must
16851        // surface the slot as "declared" even when every per-axis
16852        // scalar defers to the cluster-default arm), and a `Caixa {
16853        // placement: None, .. }` must NOT push the label (the "author
16854        // omitted the slot entirely" partition). The pair jointly pins
16855        // the accessor + declared-slot enumerator composition: any
16856        // future silent detour that had the accessor collapse
16857        // `Some(Placement::default())` to `None` (a `.filter(|p|
16858        // p.clusters().is_empty().not())` projection) would silently
16859        // absorb the "declared but empty" arm at the accessor boundary
16860        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
16861        // kind-coherence gate would silently accept a struct-literal
16862        // `Caixa` carrying the drift.
16863        //
16864        // Peer of the sibling
16865        // `declared_servico_slots_limits_arm_routes_through_accessor`
16866        // (b2bd9d7),
16867        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16868        // (35d8b52), and
16869        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
16870        // (5d23d29) composition pins on the sibling `:limits` /
16871        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
16872        // — same "the enumerator gate must route through the
16873        // substrate-primitive typed dispatch" discipline extended onto
16874        // the second of the three M3 mesh-slot axes so the
16875        // [`Caixa::declared_mesh_slots`] enumerator carries the same
16876        // routing invariant on the `:placement` arm as the peer
16877        // `:politicas` arm.
16878        use crate::aplicacao::Placement;
16879        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
16880        let slots = c.declared_mesh_slots();
16881        assert!(
16882            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
16883            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
16884             when `:placement` is Some (even for Placement::default()) \
16885             — the accessor and the enumerator gate must route through \
16886             the same substrate-primitive typed dispatch on the outer \
16887             :placement presence bit (got slots={slots:?})",
16888        );
16889        let c = caixa_aplicacao_with_placement(None);
16890        let slots = c.declared_mesh_slots();
16891        assert!(
16892            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
16893            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
16894             when `:placement` is None — the author-omitted arm must \
16895             route through the accessor's None-return unchanged (got \
16896             slots={slots:?})",
16897        );
16898    }
16899
16900    #[test]
16901    fn aplicacao_view_placement_arm_folds_through_accessor() {
16902        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
16903        // Aplicacao-composition seed must fold through
16904        // [`Caixa::placement`], not the raw
16905        // `self.placement.clone().unwrap_or_default()` field-borrow.
16906        // Structurally: a `Caixa { placement: Some(Placement {
16907        // estrategia: Replicated, clusters: ["rio"], .. default }),
16908        // kind: Aplicacao, .. }` must surface a projected
16909        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
16910        // `placement().clusters()` byte-equal the outer composite's
16911        // authored values (the fold must project the authored
16912        // composite verbatim), a `Caixa { placement:
16913        // Some(Placement::default()), kind: Aplicacao, .. }` must
16914        // surface an [`crate::AplicacaoSpec`] whose `placement()`
16915        // byte-equals [`crate::aplicacao::Placement::default`] (the
16916        // fold's empty-composite arm collapses to the same default
16917        // the author-omitted arm does), and a `Caixa { placement:
16918        // None, kind: Aplicacao, .. }` must surface an
16919        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
16920        // [`crate::aplicacao::Placement::default`] (the "author
16921        // omitted the slot entirely" arm folds through the
16922        // `unwrap_or_default` onto the cluster-default). The triad
16923        // jointly pins the accessor + Aplicacao-composition seed
16924        // composition: any future silent detour that had the accessor
16925        // divert the raw slot away from the seed's fold (an operator-
16926        // resolved overlay's default-fold arm silently differing from
16927        // the raw slot's default-fold arm) would silently split the
16928        // build-time distribution-artifact emission gate from the
16929        // caixa-mesh renderer's Aplicacao-view input at the
16930        // composition boundary.
16931        use crate::aplicacao::{Placement, PlacementStrategy};
16932        let c = caixa_aplicacao_with_placement(Some(Placement {
16933            estrategia: PlacementStrategy::Replicated,
16934            clusters: vec!["rio".into()],
16935            affinity: None,
16936            shard_key: None,
16937        }));
16938        let view = c.aplicacao_view().unwrap();
16939        assert_eq!(
16940            view.placement().estrategia(),
16941            PlacementStrategy::Replicated,
16942            "Caixa::aplicacao_view must fold the authored :placement \
16943             :estrategia scalar through the accessor verbatim onto the \
16944             projected AplicacaoSpec — a future silent detour at the \
16945             seed's fold arm would surface here as a projected-scalar \
16946             drift (got {:?})",
16947            view.placement().estrategia(),
16948        );
16949        assert_eq!(
16950            view.placement().clusters(),
16951            &["rio"],
16952            "Caixa::aplicacao_view must fold the authored :placement \
16953             :clusters list through the accessor verbatim onto the \
16954             projected AplicacaoSpec — a future silent detour at the \
16955             seed's fold arm would surface here as a projected-list \
16956             drift (got {:?})",
16957            view.placement().clusters(),
16958        );
16959        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
16960        let view = c.aplicacao_view().unwrap();
16961        assert_eq!(
16962            view.placement(),
16963            &Placement::default(),
16964            "Caixa::aplicacao_view must fold Some(Placement::default()) \
16965             through the accessor onto Placement::default — the empty- \
16966             composite arm collapses to the same default the author- \
16967             omitted arm does (got {:?})",
16968            view.placement(),
16969        );
16970        let c = caixa_aplicacao_with_placement(None);
16971        let view = c.aplicacao_view().unwrap();
16972        assert_eq!(
16973            view.placement(),
16974            &Placement::default(),
16975            "Caixa::aplicacao_view must fold None through the accessor's \
16976             unwrap_or_default onto Placement::default — the author- \
16977             omitted arm must route through the accessor's None-return \
16978             unchanged (got {:?})",
16979            view.placement(),
16980        );
16981    }
16982
16983    #[test]
16984    fn placement_projects_option_ref_by_borrow() {
16985        // The by-borrow pin: [`Caixa::placement`] returns
16986        // `Option<&Placement>` by borrow — the returned reference
16987        // borrows the underlying `Option<Placement>` storage of the
16988        // `:placement` slot and the accessor must not clone the
16989        // backing composite on every call. Peer of the sibling
16990        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
16991        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
16992        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
16993        // pins on the outer top-level [`Caixa`]
16994        // `Option<&Composite>`-return sub-family — extended here to
16995        // the fourth axis of the same sub-family: the accessor's
16996        // returned reference must borrow from `&self` (the returned
16997        // reference's lifetime is tied to `&self`), and calling the
16998        // accessor twice on the same [`Caixa`] must yield references
16999        // that are pointer-equal (the underlying byte-buffer is the
17000        // storage `Placement`'s allocation, not a fresh copy) as well
17001        // as value-equal (idempotent, no side effects on `&self`).
17002        //
17003        // Pins against a future silent detour that returned an owned
17004        // `Placement` (which would type-check via the `Clone` impl
17005        // but silently clone on every call), a `&Placement` panic-
17006        // return on the `None` arm (which would collapse the load-
17007        // bearing `Option` presence-bit into a runtime panic), or a
17008        // one-arm-only accessor that returned a saturating composite
17009        // on some sentinel input.
17010        use crate::aplicacao::{Placement, PlacementStrategy};
17011        for placement in [
17012            Some(Placement::default()),
17013            Some(Placement {
17014                estrategia: PlacementStrategy::Sharded,
17015                clusters: vec!["rio".into(), "sao-paulo".into()],
17016                affinity: Some("data-locality".into()),
17017                shard_key: Some("$tenantId".into()),
17018            }),
17019        ] {
17020            let c = caixa_aplicacao_with_placement(placement.clone());
17021            let first = c.placement().unwrap();
17022            let second = c.placement().unwrap();
17023            assert_eq!(
17024                first, second,
17025                "Caixa::placement must be idempotent — two successive \
17026                 calls on the same &self must return the same \
17027                 &Placement",
17028            );
17029            assert!(
17030                std::ptr::eq(first, second),
17031                "Caixa::placement must borrow the underlying \
17032                 Option<Placement> storage — two successive calls \
17033                 must return references with the same backing pointer \
17034                 (a fresh Placement clone would change the pointer on \
17035                 every call)",
17036            );
17037            assert_eq!(
17038                Some(first),
17039                placement.as_ref(),
17040                "Caixa::placement must return :placement verbatim by \
17041                 borrow — got {first:?}, expected {:?}",
17042                placement.as_ref(),
17043            );
17044        }
17045        let c = caixa_aplicacao_with_placement(None);
17046        assert!(
17047            c.placement().is_none(),
17048            "Caixa::placement must return None when :placement is \
17049             absent — the author-omitted arm must project through the \
17050             accessor's Option::None unchanged",
17051        );
17052    }
17053
17054    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
17055
17056    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
17057        use crate::aplicacao::{Membro, WitContract};
17058        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17059        c.kind = CaixaKind::Aplicacao;
17060        c.membros = vec![Membro {
17061            caixa: "a".into(),
17062            versao: "^0.1".into(),
17063        }];
17064        c.contratos = vec![WitContract {
17065            de: "a".into(),
17066            para: "a".into(),
17067            wit: "wasi:http/proxy".into(),
17068            endpoint: Some("/x".into()),
17069            subject: None,
17070            slot: None,
17071        }];
17072        c.entrada = entrada;
17073        c
17074    }
17075
17076    #[test]
17077    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
17078        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
17079        // composite optional-composite-reference-shape pin:
17080        // [`Caixa::entrada`] must return the `:entrada` typed
17081        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
17082        // reference over the same backing storage the raw
17083        // `self.entrada.as_ref()` field access borrows from,
17084        // byte-equal across every representative fixture in the
17085        // accept-set — the author-omitted `None` shape (the
17086        // "cluster-internal Aplicacao" partition every downstream
17087        // Gateway-API emitter treats as "emit no listener + no
17088        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
17089        // (empty `paths` — the resolved-paths fallback the peer
17090        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
17091        // onto the substrate catch-all), and a fully-populated
17092        // multi-path-with-non-default-port fixture (the canonical
17093        // shape a public HTTP Aplicacao carries).
17094        //
17095        // Pins against a future silent detour that returned a fresh-
17096        // cloned [`crate::aplicacao::Entrada`] copy (which would
17097        // type-check via the `Clone` impl but silently break every
17098        // downstream caller that relied on the reference sharing the
17099        // composite's backing identity), a reference to an operator-
17100        // resolved overlay (the future per-cluster
17101        // `:entrada-overrides` slot — its resolution must land at
17102        // exactly this accessor body, not silently divert the raw
17103        // slot away from the peer [`Caixa::declared_mesh_slots`]
17104        // enumerator's presence probe), or an axis-shuffled projection
17105        // (a future detour that swapped `host` and `para` through the
17106        // accessor would silently split the paired
17107        // [`Caixa::aplicacao_view`] seed's forward input from the
17108        // sibling M3 gateway-artifact emitter's projection input).
17109        //
17110        // Fifth and final outer top-level [`Caixa`]
17111        // `Option<&Composite>`-return composite-reference accessor pin
17112        // on the substrate primitive — peer of the sibling
17113        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17114        // (b2bd9d7),
17115        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17116        // (35d8b52),
17117        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17118        // (5d23d29), and
17119        // `placement_returns_placement_option_ref_verbatim_across_permutations`
17120        // (4fb8074) opening tetrad pins on the outer top-level
17121        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17122        // here to the third and final M3 mesh-slot axis so the closed
17123        // outer `Option<&Composite>` sub-family carries the same
17124        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
17125        // accessor discipline across all five arms.
17126        use crate::aplicacao::Entrada;
17127        let fixtures: Vec<Option<Entrada>> = vec![
17128            None,
17129            Some(Entrada {
17130                host: "checkout.quero.cloud".into(),
17131                para: "gateway".into(),
17132                paths: Vec::new(),
17133                port: crate::DEFAULT_SERVICO_PORT,
17134            }),
17135            Some(Entrada {
17136                host: "api.pleme.io".into(),
17137                para: "public-api".into(),
17138                paths: vec!["/v1".into(), "/v2".into()],
17139                port: 8080,
17140            }),
17141        ];
17142        for entrada in fixtures {
17143            let c = caixa_aplicacao_with_entrada(entrada.clone());
17144            assert_eq!(
17145                c.entrada(),
17146                entrada.as_ref(),
17147                "Caixa::entrada must return :entrada verbatim (got \
17148                 {:?}, expected {:?})",
17149                c.entrada(),
17150                entrada.as_ref(),
17151            );
17152            match (c.entrada(), c.entrada.as_ref()) {
17153                (Some(a), Some(b)) => assert!(
17154                    std::ptr::eq(a, b),
17155                    "Caixa::entrada accessor and self.entrada.as_ref() \
17156                     field access must borrow the same backing storage \
17157                     — the accessor is the substrate-primitive typed \
17158                     dispatch every downstream Aplicacao-external- \
17159                     gateway composite consumer must route through, and \
17160                     a reference-identity split would silently break \
17161                     every consumer that relied on the borrow sharing \
17162                     the composite's storage",
17163                ),
17164                (None, None) => {}
17165                _ => panic!(
17166                    "Caixa::entrada presence bit must byte-equal \
17167                     self.entrada.is_some() — a presence-bit drift \
17168                     would silently split the paired \
17169                     Caixa::aplicacao_view Aplicacao-composition seed's \
17170                     traversal head from the peer \
17171                     Caixa::declared_mesh_slots M3 declared-slot \
17172                     enumerator's presence probe",
17173                ),
17174            }
17175            assert_eq!(
17176                c.entrada().is_some(),
17177                c.entrada.is_some(),
17178                "Caixa::entrada().is_some() must byte-equal \
17179                 self.entrada.is_some() — a presence-bit drift would \
17180                 silently split every downstream Option<&Entrada> \
17181                 consumer's partition on the cluster-internal arm",
17182            );
17183        }
17184    }
17185
17186    #[test]
17187    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
17188        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
17189        // presence-probe arm must key off [`Caixa::entrada`], not the
17190        // raw `self.entrada.is_some()` field-probe. Structurally: a
17191        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
17192        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
17193        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
17194        // presence bit is `Some`, so the M3 kind-coherence gate must
17195        // surface the slot as "declared" even when every per-axis
17196        // scalar defers to the substrate catch-all / default port),
17197        // and a `Caixa { entrada: None, .. }` must NOT push the label
17198        // (the "author omitted the slot entirely" partition). The pair
17199        // jointly pins the accessor + declared-slot enumerator
17200        // composition: any future silent detour that had the accessor
17201        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
17202        // `.filter(|e| !e.paths.is_empty())` projection) would silently
17203        // absorb the "declared but empty-paths" arm at the accessor
17204        // boundary and the
17205        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17206        // coherence gate would silently accept a struct-literal
17207        // `Caixa` carrying the drift.
17208        //
17209        // Peer of the sibling
17210        // `declared_servico_slots_limits_arm_routes_through_accessor`
17211        // (b2bd9d7),
17212        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17213        // (35d8b52),
17214        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17215        // (5d23d29), and
17216        // `declared_mesh_slots_placement_arm_routes_through_accessor`
17217        // (4fb8074) composition pins on the sibling `:limits` /
17218        // `:behavior` / `:politicas` / `:placement` outer-
17219        // `Option<&Composite>` arms — same "the enumerator gate must
17220        // route through the substrate-primitive typed dispatch"
17221        // discipline extended onto the third and final M3 mesh-slot
17222        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
17223        // carries the routing invariant on every M3 mesh-slot arm.
17224        use crate::aplicacao::Entrada;
17225        let c = caixa_aplicacao_with_entrada(Some(Entrada {
17226            host: "checkout.quero.cloud".into(),
17227            para: "gateway".into(),
17228            paths: Vec::new(),
17229            port: crate::DEFAULT_SERVICO_PORT,
17230        }));
17231        let slots = c.declared_mesh_slots();
17232        assert!(
17233            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17234            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
17235             `:entrada` is Some (even for empty-paths / default-port) \
17236             — the accessor and the enumerator gate must route through \
17237             the same substrate-primitive typed dispatch on the outer \
17238             :entrada presence bit (got slots={slots:?})",
17239        );
17240        let c = caixa_aplicacao_with_entrada(None);
17241        let slots = c.declared_mesh_slots();
17242        assert!(
17243            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17244            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
17245             when `:entrada` is None — the author-omitted arm must \
17246             route through the accessor's None-return unchanged (got \
17247             slots={slots:?})",
17248        );
17249    }
17250
17251    #[test]
17252    fn aplicacao_view_entrada_arm_folds_through_accessor() {
17253        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
17254        // Aplicacao-composition seed must fold through
17255        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
17256        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
17257        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
17258        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
17259        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
17260        // equals the outer composite's authored value (the fold must
17261        // project the authored composite verbatim), and a `Caixa {
17262        // entrada: None, kind: Aplicacao, .. }` must surface an
17263        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
17264        // "author omitted the slot entirely" arm folds through the
17265        // accessor's `Option::cloned` onto the same `None` presence
17266        // bit — unlike the peer `:politicas` / `:placement` arms
17267        // `:entrada` has no cluster-default fold, the omitted arm
17268        // stays omitted). The pair jointly pins the accessor +
17269        // Aplicacao-composition seed composition: any future silent
17270        // detour that had the accessor divert the raw slot away from
17271        // the seed's fold (an operator-resolved overlay's forward arm
17272        // silently differing from the raw slot's forward arm) would
17273        // silently split the build-time gateway-artifact emission gate
17274        // from the caixa-mesh renderer's Aplicacao-view input at the
17275        // composition boundary.
17276        use crate::aplicacao::Entrada;
17277        let authored = Entrada {
17278            host: "api.pleme.io".into(),
17279            para: "public-api".into(),
17280            paths: vec!["/v1".into()],
17281            port: 8080,
17282        };
17283        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
17284        let view = c.aplicacao_view().unwrap();
17285        assert_eq!(
17286            view.entrada(),
17287            Some(&authored),
17288            "Caixa::aplicacao_view must fold the authored :entrada \
17289             composite through the accessor verbatim onto the \
17290             projected AplicacaoSpec — a future silent detour at the \
17291             seed's fold arm would surface here as a projected- \
17292             composite drift (got {:?})",
17293            view.entrada(),
17294        );
17295        let c = caixa_aplicacao_with_entrada(None);
17296        let view = c.aplicacao_view().unwrap();
17297        assert!(
17298            view.entrada().is_none(),
17299            "Caixa::aplicacao_view must fold None through the \
17300             accessor's Option::cloned onto None — the author- \
17301             omitted arm must route through the accessor's None-return \
17302             unchanged (got {:?})",
17303            view.entrada(),
17304        );
17305    }
17306
17307    #[test]
17308    fn entrada_projects_option_ref_by_borrow() {
17309        // The by-borrow pin: [`Caixa::entrada`] returns
17310        // `Option<&Entrada>` by borrow — the returned reference
17311        // borrows the underlying `Option<Entrada>` storage of the
17312        // `:entrada` slot and the accessor must not clone the backing
17313        // composite on every call. Peer of the sibling
17314        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17315        // `behavior_projects_option_ref_by_borrow` (35d8b52),
17316        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
17317        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
17318        // borrow pins on the outer top-level [`Caixa`]
17319        // `Option<&Composite>`-return sub-family — extended here to
17320        // the fifth and final axis of the same sub-family, closing
17321        // the discipline: the accessor's returned reference must
17322        // borrow from `&self` (the returned reference's lifetime is
17323        // tied to `&self`), and calling the accessor twice on the
17324        // same [`Caixa`] must yield references that are pointer-equal
17325        // (the underlying byte-buffer is the storage `Entrada`'s
17326        // allocation, not a fresh copy) as well as value-equal
17327        // (idempotent, no side effects on `&self`).
17328        //
17329        // Pins against a future silent detour that returned an owned
17330        // `Entrada` (which would type-check via the `Clone` impl but
17331        // silently clone on every call), a `&Entrada` panic-return on
17332        // the `None` arm (which would collapse the load-bearing
17333        // `Option` presence-bit into a runtime panic), or a one-arm-
17334        // only accessor that returned a saturating composite on some
17335        // sentinel input.
17336        use crate::aplicacao::Entrada;
17337        for entrada in [
17338            Some(Entrada {
17339                host: "checkout.quero.cloud".into(),
17340                para: "gateway".into(),
17341                paths: Vec::new(),
17342                port: crate::DEFAULT_SERVICO_PORT,
17343            }),
17344            Some(Entrada {
17345                host: "api.pleme.io".into(),
17346                para: "public-api".into(),
17347                paths: vec!["/v1".into(), "/v2".into()],
17348                port: 8080,
17349            }),
17350        ] {
17351            let c = caixa_aplicacao_with_entrada(entrada.clone());
17352            let first = c.entrada().unwrap();
17353            let second = c.entrada().unwrap();
17354            assert_eq!(
17355                first, second,
17356                "Caixa::entrada must be idempotent — two successive \
17357                 calls on the same &self must return the same &Entrada",
17358            );
17359            assert!(
17360                std::ptr::eq(first, second),
17361                "Caixa::entrada must borrow the underlying \
17362                 Option<Entrada> storage — two successive calls must \
17363                 return references with the same backing pointer (a \
17364                 fresh Entrada clone would change the pointer on every \
17365                 call)",
17366            );
17367            assert_eq!(
17368                Some(first),
17369                entrada.as_ref(),
17370                "Caixa::entrada must return :entrada verbatim by \
17371                 borrow — got {first:?}, expected {:?}",
17372                entrada.as_ref(),
17373            );
17374        }
17375        let c = caixa_aplicacao_with_entrada(None);
17376        assert!(
17377            c.entrada().is_none(),
17378            "Caixa::entrada must return None when :entrada is absent \
17379             — the author-omitted arm must project through the \
17380             accessor's Option::None unchanged",
17381        );
17382    }
17383
17384    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
17385
17386    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
17387        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17388        c.estrategia = estrategia;
17389        c
17390    }
17391
17392    #[test]
17393    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
17394        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
17395        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
17396        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
17397        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
17398        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
17399        // over the same discriminant the raw `self.estrategia` field
17400        // access carries, byte-equal across every representative fixture
17401        // in the accept-set — the author-omitted `None` shape (the
17402        // "defer to [`RestartStrategy::default`] through the
17403        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
17404        // every non-`Supervisor`-kind `defcaixa` carries by
17405        // `#[serde(default)]`), and each of the four closed-set variants
17406        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
17407        // / [`RestartStrategy::RestForOne`] /
17408        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
17409        // partitions on.
17410        //
17411        // Pins against a future silent detour that re-derived the
17412        // strategy from a peer axis (an accidental fallback to
17413        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
17414        // collapse that read the outer `:children` list-length axis into
17415        // the strategy discriminator at the accessor boundary), a
17416        // stale-derive detour that substituted [`RestartStrategy::default`]
17417        // when the outer `Option` held `None` (which would silently
17418        // collapse the load-bearing "author explicitly declared
17419        // `:estrategia OneForOne`" vs "author omitted the slot and
17420        // inherited the default" partition the [`Self::declared_supervisor_slots`]
17421        // presence-probe reads — the enumerator gate would still push
17422        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
17423        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17424        // kind-coherence gate's traversal head from the
17425        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
17426        // composition head), a reference to an operator-resolved overlay
17427        // (the future per-cluster `:estrategia-overrides` slot — its
17428        // resolution must land at exactly this accessor body, not
17429        // silently divert the raw slot away from a second consumer), or
17430        // an axis-remap projection (a future detour that mapped
17431        // `OneForAll` through the accessor onto `OneForOne` would
17432        // silently split every downstream sibling-restart-strategy
17433        // consumer's per-arm fan-out).
17434        //
17435        // First outer top-level [`Caixa`] `Option<Copy>`-return
17436        // supervisor-tree-slot flat-spread accessor pin on the substrate
17437        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
17438        // projection pattern the sibling per-`Caixa` `:max-restarts` /
17439        // `:restart-window` future outer-scalar pins fold on. Peer of
17440        // the inner-altitude
17441        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17442        // (eafb619) pin on the post-composition [`SupervisorSpec`]
17443        // altitude — same "the substrate-primitive accessor must byte-
17444        // equal the raw field access verbatim across every author-
17445        // declared value" discipline extended onto the pre-composition
17446        // outer author-surface [`Caixa`] altitude. Peer of the closed
17447        // outer-`Caixa` `Option<&Composite>` composite-reference family
17448        // the sibling `limits` / `behavior` / `politicas` / `placement` /
17449        // `entrada`
17450        // `..._returns_..._option_ref_verbatim_across_permutations` pins
17451        // already carry on the outer `Option<&Composite>` altitude.
17452        use crate::supervisor::RestartStrategy;
17453        let fixtures: Vec<Option<RestartStrategy>> = vec![
17454            None,
17455            Some(RestartStrategy::OneForOne),
17456            Some(RestartStrategy::OneForAll),
17457            Some(RestartStrategy::RestForOne),
17458            Some(RestartStrategy::SimpleOneForOne),
17459        ];
17460        for estrategia in fixtures {
17461            let c = caixa_with_estrategia(estrategia);
17462            assert_eq!(
17463                c.estrategia(),
17464                estrategia,
17465                "Caixa::estrategia must return :estrategia verbatim (got \
17466                 {:?}, expected {:?})",
17467                c.estrategia(),
17468                estrategia,
17469            );
17470            assert_eq!(
17471                c.estrategia(),
17472                c.estrategia,
17473                "Caixa::estrategia accessor and self.estrategia field \
17474                 access must byte-equal — the accessor is the substrate-\
17475                 primitive typed dispatch every downstream supervisor-\
17476                 tree flat-spread consumer must route through, and a \
17477                 discriminant split would silently break every consumer \
17478                 that relied on the accessor sharing the field's own \
17479                 Option<Copy> shape",
17480            );
17481            assert_eq!(
17482                c.estrategia().is_some(),
17483                c.estrategia.is_some(),
17484                "Caixa::estrategia().is_some() must byte-equal \
17485                 self.estrategia.is_some() — a presence-bit drift would \
17486                 silently split the paired Caixa::declared_supervisor_slots \
17487                 presence-probe arm from the Caixa::supervisor_view \
17488                 unwrap_or_default() fold's composition input",
17489            );
17490        }
17491    }
17492
17493    #[test]
17494    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
17495        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17496        // `:estrategia` presence-probe arm must key off
17497        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
17498        // field-probe. Structurally: every `Caixa { estrategia:
17499        // Some(RestartStrategy::_), .. }` variant must push
17500        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
17501        // (the presence bit is `Some` for every closed-set variant, so
17502        // the M2 supervisor-tree kind-coherence gate must surface the
17503        // slot as "declared" regardless of which variant the author
17504        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
17505        // the label (the "author omitted the slot entirely, deferring
17506        // to [`RestartStrategy::default`] through the supervisor_view
17507        // fold" partition). The pair jointly pins the accessor +
17508        // declared-slot enumerator composition: any future silent detour
17509        // that had the accessor collapse `Some(RestartStrategy::default())`
17510        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
17511        // projection) would silently absorb the "declared but default-
17512        // valued" arm at the accessor boundary and the
17513        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
17514        // coherence gate would silently accept a struct-literal `Caixa`
17515        // carrying the drift.
17516        //
17517        // Peer of the sibling per-`Caixa`
17518        // `declared_servico_slots_limits_arm_routes_through_accessor`
17519        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
17520        // `Option<&LimitsSpec>` composition axis — same "the enumerator
17521        // gate must route through the substrate-primitive typed
17522        // dispatch" discipline extended onto the flat-spread M2
17523        // supervisor-tree `Option<RestartStrategy>`-composition surface,
17524        // opening the outer-`Caixa` supervisor-tree-slot arm of the
17525        // composition-pin family.
17526        use crate::supervisor::RestartStrategy;
17527        for estrategia in [
17528            RestartStrategy::OneForOne,
17529            RestartStrategy::OneForAll,
17530            RestartStrategy::RestForOne,
17531            RestartStrategy::SimpleOneForOne,
17532        ] {
17533            let c = caixa_with_estrategia(Some(estrategia));
17534            let slots = c.declared_supervisor_slots();
17535            assert!(
17536                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17537                "declared_supervisor_slots must push \
17538                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
17539                 Some({estrategia:?}) — the accessor and the enumerator \
17540                 gate must route through the same substrate-primitive \
17541                 typed dispatch on the outer :estrategia presence bit \
17542                 (got slots={slots:?})",
17543            );
17544        }
17545        let c = caixa_with_estrategia(None);
17546        let slots = c.declared_supervisor_slots();
17547        assert!(
17548            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17549            "declared_supervisor_slots must NOT push \
17550             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
17551             — the author-omitted arm must route through the accessor's \
17552             None-return unchanged (got slots={slots:?})",
17553        );
17554    }
17555
17556    #[test]
17557    fn supervisor_view_estrategia_arm_routes_through_accessor() {
17558        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
17559        // [`SupervisorSpec`] construction arm must key off
17560        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
17561        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
17562        // for every `:kind Supervisor` `Caixa` carrying an author-
17563        // declared `Some(RestartStrategy::_)` variant, the composed
17564        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
17565        // outer accessor's declared variant unchanged; and for a
17566        // `:kind Supervisor` `Caixa` carrying `None`, the composed
17567        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
17568        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
17569        // arm the flat-spread `unwrap_or_default()` fold projects to on
17570        // the author-omitted arm — this is the *composition* between the
17571        // outer `Option<RestartStrategy>` accessor's presence-bit
17572        // surface and the inner post-composition non-`Option`
17573        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
17574        // pins the accessor + supervisor_view composition: any future
17575        // silent detour that had the accessor promote `None` to
17576        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
17577        // projection) would silently collapse the two arms into one at
17578        // the accessor boundary and the [`Self::declared_supervisor_slots`]
17579        // presence probe would silently drift from the composition site.
17580        //
17581        // Peer of the sibling M2 supervisor-slot post-composition
17582        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
17583        // pin on the [`SupervisorSpec::validate`] altitude — this pin
17584        // extends that inner-altitude accessor-routing discipline onto
17585        // the pre-composition outer author-surface [`Caixa`] altitude,
17586        // pinning the composition edge between the flat-spread outer
17587        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
17588        // `RestartStrategy` axes.
17589        use crate::CaixaKind;
17590        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17591        for estrategia in [
17592            RestartStrategy::OneForOne,
17593            RestartStrategy::OneForAll,
17594            RestartStrategy::RestForOne,
17595            RestartStrategy::SimpleOneForOne,
17596        ] {
17597            let mut c = caixa_with_estrategia(Some(estrategia));
17598            c.kind = CaixaKind::Supervisor;
17599            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
17600            // shape partition through the [`gen_platform::IsVariant`]
17601            // derive-generated
17602            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
17603            // than the raw `matches!(estrategia, RestartStrategy::
17604            // SimpleOneForOne)` open-coded pattern-match — same closed-
17605            // set-typed-enum arm-discriminator dispatch discipline the
17606            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
17607            // convergence (915a934) extended onto its two paired positive
17608            // / negated `matches!` sites and the peer
17609            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
17610            // predicate convergence (766ec63) extended onto the M3 mesh-
17611            // slot per-`:placement` distribution-strategy discriminator
17612            // axis. See the sibling `supervisor::tests::
17613            // round_trip_all_strategies` and
17614            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17615            // fixtures — the three sites (all test-only,
17616            // acknowledged in 915a934's Prior-commits footnote as the
17617            // outstanding follow-up) now consult one typed dispatch on
17618            // the substrate primitive.
17619            c.children = if estrategia.is_simple_one_for_one() {
17620                Vec::new()
17621            } else {
17622                vec![ChildSpec {
17623                    caixa: "worker".into(),
17624                    versao: "^0.1".into(),
17625                    restart: RestartPolicy::Permanent,
17626                }]
17627            };
17628            let view = c.supervisor_view().expect(
17629                "supervisor_view must materialize a SupervisorSpec for a \
17630                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
17631            );
17632            assert_eq!(
17633                view.estrategia(),
17634                c.estrategia().unwrap(),
17635                "supervisor_view must carry the outer Caixa::estrategia() \
17636                 declared variant onto the composed SupervisorSpec.estrategia \
17637                 field verbatim on the Some arm (got {:?}, expected {:?})",
17638                view.estrategia(),
17639                c.estrategia().unwrap(),
17640            );
17641        }
17642        // The author-omitted arm: outer `None` → composed
17643        // `RestartStrategy::default()` through the flat-spread
17644        // `unwrap_or_default()` fold.
17645        let mut c = caixa_with_estrategia(None);
17646        c.kind = CaixaKind::Supervisor;
17647        // Populate children so the sibling supervisor slots are coherent
17648        // for the [`Self::supervisor_view`] projection; the `:estrategia`
17649        // arm still defers to [`RestartStrategy::default`] on the
17650        // author-omitted arm even when the sibling slots carry values.
17651        c.children = vec![ChildSpec {
17652            caixa: "worker".into(),
17653            versao: "^0.1".into(),
17654            restart: RestartPolicy::Permanent,
17655        }];
17656        let view = c.supervisor_view().expect(
17657            "supervisor_view must materialize a SupervisorSpec for a \
17658             :kind Supervisor Caixa carrying a None `:estrategia` slot",
17659        );
17660        assert_eq!(
17661            view.estrategia(),
17662            RestartStrategy::default(),
17663            "supervisor_view must project the outer Caixa::estrategia() \
17664             None arm onto RestartStrategy::default() through the flat-\
17665             spread unwrap_or_default() fold (got {:?}, expected {:?})",
17666            view.estrategia(),
17667            RestartStrategy::default(),
17668        );
17669        assert!(
17670            c.estrategia().is_none(),
17671            "Caixa::estrategia() must remain None on the author-omitted \
17672             arm — the supervisor_view fold must not mutate the outer \
17673             flat-spread presence bit",
17674        );
17675    }
17676
17677    #[test]
17678    fn estrategia_projects_option_by_copy() {
17679        // The by-`Copy` pin: [`Caixa::estrategia`] returns
17680        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
17681        // the accessor does not borrow `&self` past the call (no
17682        // lifetime on the return type), and calling the accessor twice
17683        // on the same [`Caixa`] must yield discriminant-equal values
17684        // (idempotent, no side effects on `&self`). Peer of the sibling
17685        // outer-`Caixa` `Option<&Composite>` by-borrow
17686        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
17687        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
17688        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
17689        // `placement_projects_option_ref_by_borrow` (4fb8074) /
17690        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
17691        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
17692        // extended here to the outer-`Caixa` `Option<Copy>`-return
17693        // flat-spread axis. The `Copy` discipline replaces the pointer-
17694        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
17695        // `Copy` discriminant is definitionally the same discriminant, so
17696        // the axis reduces to discriminant equality).
17697        //
17698        // Pins against a future silent detour that returned a fresh
17699        // `Option<&RestartStrategy>` (which would type-check but silently
17700        // introduce a borrow of `&self` past the call, collapsing the
17701        // load-bearing "no lifetime on the return type" `Copy` projection
17702        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
17703        // read side effect that flipped the outer discriminant on
17704        // successive calls, or an axis-remap projection that returned a
17705        // different variant than the field storage.
17706        use crate::supervisor::RestartStrategy;
17707        for estrategia in [
17708            Some(RestartStrategy::OneForOne),
17709            Some(RestartStrategy::OneForAll),
17710            Some(RestartStrategy::RestForOne),
17711            Some(RestartStrategy::SimpleOneForOne),
17712        ] {
17713            let c = caixa_with_estrategia(estrategia);
17714            let first = c.estrategia();
17715            let second = c.estrategia();
17716            assert_eq!(
17717                first, second,
17718                "Caixa::estrategia must be idempotent — two successive \
17719                 calls on the same &self must return the same \
17720                 Option<RestartStrategy>",
17721            );
17722            assert_eq!(
17723                first, estrategia,
17724                "Caixa::estrategia must return :estrategia verbatim by \
17725                 Copy — got {first:?}, expected {estrategia:?}",
17726            );
17727        }
17728        let c = caixa_with_estrategia(None);
17729        assert!(
17730            c.estrategia().is_none(),
17731            "Caixa::estrategia must return None when :estrategia is \
17732             absent — the author-omitted arm must project through the \
17733             accessor's Option::None unchanged",
17734        );
17735    }
17736
17737    // ── Caixa::max_restarts / Caixa::restart_window —
17738    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
17739    //    (Option<u32> / Option<&str>) folding on the ed04d3c
17740    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
17741
17742    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
17743        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17744        c.max_restarts = max_restarts;
17745        c
17746    }
17747
17748    fn caixa_supervisor_with_max_restarts_and_window(
17749        max_restarts: Option<u32>,
17750        restart_window: Option<&str>,
17751    ) -> Caixa {
17752        use crate::CaixaKind;
17753        use crate::supervisor::{ChildSpec, RestartPolicy};
17754        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
17755        c.kind = CaixaKind::Supervisor;
17756        c.max_restarts = max_restarts;
17757        c.restart_window = restart_window.map(str::to_string);
17758        c.children = vec![ChildSpec {
17759            caixa: "worker".into(),
17760            versao: "^0.1".into(),
17761            restart: RestartPolicy::Permanent,
17762        }];
17763        c
17764    }
17765
17766    #[test]
17767    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
17768        // Value-shape pin: [`Caixa::max_restarts`] returns the
17769        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
17770        // from the typed slot's own storage, byte-equal across the
17771        // author-omitted `None` arm (the "defer to the
17772        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
17773        // `{intensity, 5, 60}` default" partition every
17774        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
17775        // and each of the representative fixtures in the accept-set —
17776        // `0` (the zero-floor arm the peer
17777        // [`crate::supervisor::SupervisorSpec::validate`]
17778        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
17779        // the post-composition altitude — the accessor must ship the
17780        // raw slot verbatim so struct-literal fixtures continue to
17781        // expose the zero at the accessor boundary), the OTP-canonical
17782        // `5` default (`{intensity, 5, 60}` worker-supervisor from
17783        // Learn You Some Erlang), `1000` (the
17784        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
17785        // upper-bound gate accepts on the boundary), `u32::MAX` (a
17786        // past-the-cap sentinel that the substrate-primitive accessor
17787        // must still ship verbatim). Second outer top-level
17788        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
17789        // pin — folds on the sibling
17790        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
17791        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
17792        // onto the sibling `Option<u32>` restart-budget-count arm.
17793        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
17794        for max_restarts in fixtures {
17795            let c = caixa_with_max_restarts(max_restarts);
17796            assert_eq!(
17797                c.max_restarts(),
17798                max_restarts,
17799                "Caixa::max_restarts must return :max-restarts verbatim \
17800                 (got {:?}, expected {max_restarts:?})",
17801                c.max_restarts(),
17802            );
17803            assert_eq!(
17804                c.max_restarts(),
17805                c.max_restarts,
17806                "Caixa::max_restarts accessor and self.max_restarts \
17807                 field access must byte-equal — a presence-bit or count \
17808                 drift would silently split the paired \
17809                 Caixa::declared_supervisor_slots presence-probe arm \
17810                 from the Caixa::supervisor_view unwrap_or(5) fold's \
17811                 composition input",
17812            );
17813        }
17814    }
17815
17816    #[test]
17817    fn max_restarts_projects_option_by_copy() {
17818        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
17819        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
17820        // borrow `&self` past the call (no lifetime on the return type),
17821        // and calling the accessor twice on the same [`Caixa`] must
17822        // yield equal values (idempotent, no side effects). Peer of the
17823        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
17824        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
17825        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
17826            let c = caixa_with_max_restarts(max_restarts);
17827            let first = c.max_restarts();
17828            let second = c.max_restarts();
17829            assert_eq!(
17830                first, second,
17831                "Caixa::max_restarts must be idempotent — two successive \
17832                 calls on the same &self must return the same Option<u32>",
17833            );
17834            assert_eq!(
17835                first, max_restarts,
17836                "Caixa::max_restarts must return :max-restarts verbatim \
17837                 by Copy — got {first:?}, expected {max_restarts:?}",
17838            );
17839        }
17840    }
17841
17842    #[test]
17843    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
17844        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17845        // `:max-restarts` presence-probe arm must key off
17846        // [`Caixa::max_restarts`], not the raw
17847        // `self.max_restarts.is_some()` field-probe. Structurally: every
17848        // `Caixa { max_restarts: Some(_), .. }` variant must push
17849        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
17850        // list (the presence bit is `Some` for every representative
17851        // count, so the M2 kind-coherence gate must surface the slot as
17852        // "declared"), and a `Caixa { max_restarts: None, .. }` must
17853        // NOT push the label. Peer of the sibling
17854        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
17855        // (ed04d3c) composition pin — same routing-through-accessor
17856        // discipline extended onto the sibling flat-spread `Option<u32>`
17857        // arm.
17858        for max_restarts in [0u32, 5, 1000, u32::MAX] {
17859            let c = caixa_with_max_restarts(Some(max_restarts));
17860            let slots = c.declared_supervisor_slots();
17861            assert!(
17862                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
17863                "declared_supervisor_slots must push \
17864                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
17865                 is Some({max_restarts}) — the accessor and the \
17866                 enumerator gate must route through the same \
17867                 substrate-primitive typed dispatch on the outer \
17868                 :max-restarts presence bit (got slots={slots:?})",
17869            );
17870        }
17871        let c = caixa_with_max_restarts(None);
17872        let slots = c.declared_supervisor_slots();
17873        assert!(
17874            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
17875            "declared_supervisor_slots must NOT push \
17876             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
17877             None — the author-omitted arm must route through the \
17878             accessor's None-return unchanged (got slots={slots:?})",
17879        );
17880    }
17881
17882    #[test]
17883    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
17884        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
17885        // [`SupervisorSpec`] construction arm must key off
17886        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
17887        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
17888        // every `:kind Supervisor` `Caixa` carrying an author-declared
17889        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
17890        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
17891        // carrying `None`, the composed [`SupervisorSpec`]'s
17892        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
17893        // of the sibling
17894        // `supervisor_view_estrategia_arm_routes_through_accessor`
17895        // (ed04d3c) composition pin.
17896        for max_restarts in [1u32, 5, 1000] {
17897            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
17898            let view = c.supervisor_view().expect(
17899                "supervisor_view must materialize a SupervisorSpec for a \
17900                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
17901            );
17902            assert_eq!(
17903                view.max_restarts(),
17904                max_restarts,
17905                "supervisor_view must carry the outer \
17906                 Caixa::max_restarts() Some arm onto the composed \
17907                 SupervisorSpec.max_restarts field verbatim (got {}, \
17908                 expected {max_restarts})",
17909                view.max_restarts(),
17910            );
17911        }
17912        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
17913        let view = c.supervisor_view().expect(
17914            "supervisor_view must materialize a SupervisorSpec for a \
17915             :kind Supervisor Caixa carrying a None :max-restarts",
17916        );
17917        assert_eq!(
17918            view.max_restarts(),
17919            5,
17920            "supervisor_view must project the outer \
17921             Caixa::max_restarts() None arm onto the OTP-canonical \
17922             {{intensity, 5, 60}} default (5) through the flat-spread \
17923             unwrap_or(5) fold (got {})",
17924            view.max_restarts(),
17925        );
17926        assert!(
17927            c.max_restarts().is_none(),
17928            "Caixa::max_restarts() must remain None on the author-\
17929             omitted arm — the supervisor_view fold must not mutate \
17930             the outer flat-spread presence bit",
17931        );
17932    }
17933
17934    #[test]
17935    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
17936        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
17937        // `:estrategia` arm must degrade onto the substrate-canonical
17938        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
17939        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
17940        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
17941        // worker-supervisor default — rather than the transitively-
17942        // derived [`crate::supervisor::RestartStrategy::default`] route
17943        // the prior `.unwrap_or_default()` fold reached for. Prior to the
17944        // lift the composition site carried `.unwrap_or_default()` with
17945        // no compile-time link back to the shared OTP-canonical strategy
17946        // default that the paired [`crate::supervisor::Default for
17947        // RestartStrategy`] impl and the [`crate::supervisor::Default for
17948        // SupervisorSpec`] impl's struct-literal `estrategia` field both
17949        // (now) route through the same lifted constant — so a future
17950        // rebrand of the OTP-canonical strategy default (an OTP
17951        // `rest_for_one` widening once the substrate discovers startup-
17952        // order-coupled child cohorts as the more common worker-
17953        // supervisor shape, a per-cluster overlay the operator pins
17954        // through the MESH-COMPOSITION §III.2 supervision-canary
17955        // `:estrategia-overrides` roadmap slot) would have had to migrate
17956        // the paired `MaxIntensity` + `Period` halves through the lifted
17957        // constants and the `one_for_one` half through a
17958        // `RestartStrategy::default()` route in lockstep or a
17959        // `:kind Supervisor` caixa carrying an author-omitted
17960        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
17961        // whose `estrategia` disagreed with the paired
17962        // `SupervisorSpec::default()` view. Byte-parity against the
17963        // lifted constant closes the split. Peer of the sibling
17964        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
17965        // composition pin on the paired `MaxIntensity` half + the
17966        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
17967        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
17968        // pins on the sibling entry points onto the shared substrate
17969        // constant.
17970        use crate::CaixaKind;
17971        use crate::supervisor::{ChildSpec, RestartPolicy};
17972        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
17973        c.kind = CaixaKind::Supervisor;
17974        c.estrategia = None;
17975        c.children = vec![ChildSpec {
17976            caixa: "worker".into(),
17977            versao: "^0.1".into(),
17978            restart: RestartPolicy::Permanent,
17979        }];
17980        let view = c.supervisor_view().expect(
17981            "supervisor_view must materialize a SupervisorSpec for a \
17982             :kind Supervisor Caixa carrying a None :estrategia",
17983        );
17984        assert_eq!(
17985            view.estrategia(),
17986            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17987            "supervisor_view must degrade the outer \
17988             Caixa::estrategia() None arm onto the lifted \
17989             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
17990             expected {:?})",
17991            view.estrategia(),
17992            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
17993        );
17994    }
17995
17996    #[test]
17997    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
17998        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
17999        // `:max-restarts` arm must degrade onto the substrate-canonical
18000        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
18001        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
18002        // `MaxIntensity` default — rather than a raw `5` literal. Prior
18003        // to the lift the composition site carried an inline
18004        // `.unwrap_or(5)` with no compile-time link back to the shared
18005        // OTP-canonical default that the serde-side
18006        // `#[serde(default = "default_max_restarts")]` wire-format arm
18007        // and the [`Default for crate::supervisor::SupervisorSpec`]
18008        // struct-literal default arm both key off — so a future rebrand
18009        // of the OTP-canonical default (Elixir's `Supervisor` `3`
18010        // default, a per-cluster overlay the operator pins through the
18011        // MESH-COMPOSITION §III.2 supervision-canary
18012        // `:supervisor :max-restarts-overrides` roadmap slot) would
18013        // have had to be threaded through both the serde-side helper
18014        // and this view-construction arm in lockstep or a `:kind
18015        // Supervisor` caixa carrying `:max-restarts ()` would silently
18016        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
18017        // with the same fixture's serde-side `SupervisorSpec` view (an
18018        // author-omitted slot round-tripping through
18019        // `SupervisorSpec::default()` to the lifted constant, then
18020        // splitting to a stale literal past `supervisor_view`).
18021        // Byte-parity against the lifted constant closes the split.
18022        // Peer of the sibling
18023        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
18024        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
18025        // composition pins that close the same routing on the two
18026        // sibling entry points onto the shared substrate constant.
18027        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18028        let view = c.supervisor_view().expect(
18029            "supervisor_view must materialize a SupervisorSpec for a \
18030             :kind Supervisor Caixa carrying a None :max-restarts",
18031        );
18032        assert_eq!(
18033            view.max_restarts(),
18034            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18035            "supervisor_view must degrade the outer \
18036             Caixa::max_restarts() None arm onto the lifted \
18037             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
18038             expected {})",
18039            view.max_restarts(),
18040            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18041        );
18042    }
18043
18044    #[test]
18045    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
18046        // Value-shape pin: [`Caixa::restart_window`] returns the
18047        // `:restart-window` typed `Option<String>` verbatim as an
18048        // `Option<&str>`, borrowed from the typed slot's own storage,
18049        // byte-equal across the author-omitted `None` arm and each of
18050        // the representative fixtures in the accept-set — the canonical
18051        // `"60s"` from `{intensity, 5, 60}`, the sibling
18052        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
18053        // / `"0s"`) the shared codec's positive-set sweep pin covers,
18054        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
18055        // seconds drift the sibling [`Self::validate_restart_window`]
18056        // gate refuses; the accessor must ship the raw slot verbatim
18057        // so struct-literal fixtures continue to expose the drift at
18058        // the accessor boundary). Third outer top-level [`Caixa`]
18059        // supervisor-tree flat-spread pin — extends the sub-family onto
18060        // the sibling `Option<&str>` raw-duration-string arm.
18061        for window in [
18062            None,
18063            Some("60s"),
18064            Some("5m"),
18065            Some("1h"),
18066            Some("500ms"),
18067            Some("1.5s"),
18068            Some(""),
18069        ] {
18070            let c = caixa_with_restart_window(window);
18071            assert_eq!(
18072                c.restart_window(),
18073                window,
18074                "Caixa::restart_window must return :restart-window \
18075                 verbatim as Option<&str> (got {:?}, expected {window:?})",
18076                c.restart_window(),
18077            );
18078            assert_eq!(
18079                c.restart_window(),
18080                c.restart_window.as_deref(),
18081                "Caixa::restart_window accessor and \
18082                 self.restart_window.as_deref() field access must \
18083                 byte-equal — a byte-level drift would silently split \
18084                 the paired Caixa::declared_supervisor_slots \
18085                 presence-probe arm from the \
18086                 Caixa::validate_restart_window shared-codec gate and \
18087                 the Caixa::supervisor_view soft-swallowing fold",
18088            );
18089        }
18090    }
18091
18092    #[test]
18093    fn restart_window_projects_slice_by_borrow() {
18094        // The by-borrow pin: [`Caixa::restart_window`] returns
18095        // `Option<&str>` by borrow — the returned string slice borrows
18096        // the underlying `Option<String>` storage of the `:restart-window`
18097        // slot and the accessor must not clone on every call. Peer of
18098        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
18099        // by-borrow pins on the universal-axis scalar family
18100        // (`licenca_projects_option_ref_by_borrow` /
18101        // `descricao_projects_option_ref_by_borrow` and siblings) —
18102        // extended onto the M2 supervisor-tree flat-spread
18103        // `Option<&str>` raw-duration-string axis.
18104        for window in [None, Some("60s"), Some("5m"), Some("")] {
18105            let c = caixa_with_restart_window(window);
18106            let first = c.restart_window();
18107            let second = c.restart_window();
18108            assert_eq!(
18109                first, second,
18110                "Caixa::restart_window must be idempotent — two \
18111                 successive calls on the same &self must return the \
18112                 same Option<&str>",
18113            );
18114            if let (Some(a), Some(b)) = (first, second) {
18115                assert_eq!(
18116                    a.as_ptr(),
18117                    b.as_ptr(),
18118                    "Caixa::restart_window must borrow the underlying \
18119                     String storage — two successive Some-arm calls must \
18120                     return slices with the same backing pointer (a fresh \
18121                     String clone would change the pointer on every call)",
18122                );
18123            }
18124            assert_eq!(
18125                first, window,
18126                "Caixa::restart_window must return :restart-window \
18127                 verbatim by borrow — got {first:?}, expected {window:?}",
18128            );
18129        }
18130    }
18131
18132    #[test]
18133    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
18134        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18135        // `:restart-window` presence-probe arm must key off
18136        // [`Caixa::restart_window`], not the raw
18137        // `self.restart_window.is_some()` field-probe. Structurally:
18138        // every `Caixa { restart_window: Some(_), .. }` must push
18139        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
18140        // list, and a `Caixa { restart_window: None, .. }` must NOT
18141        // push the label. Peer of the sibling
18142        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
18143        // routing pin.
18144        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
18145            let c = caixa_with_restart_window(Some(window));
18146            let slots = c.declared_supervisor_slots();
18147            assert!(
18148                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18149                "declared_supervisor_slots must push \
18150                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
18151                 `:restart-window` is Some({window:?}) — the accessor \
18152                 and the enumerator gate must route through the same \
18153                 substrate-primitive typed dispatch on the outer \
18154                 :restart-window presence bit (got slots={slots:?})",
18155            );
18156        }
18157        let c = caixa_with_restart_window(None);
18158        let slots = c.declared_supervisor_slots();
18159        assert!(
18160            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18161            "declared_supervisor_slots must NOT push \
18162             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
18163             is None — the author-omitted arm must route through the \
18164             accessor's None-return unchanged (got slots={slots:?})",
18165        );
18166    }
18167
18168    #[test]
18169    fn validate_restart_window_arm_routes_through_accessor() {
18170        // Composition pin: [`Caixa::validate_restart_window`]'s
18171        // shared-codec fold arm must key off [`Caixa::restart_window`],
18172        // not the raw `self.restart_window.as_deref()` field-projection.
18173        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
18174        // express no reset" canonical shape); (2) a canonical `Some`
18175        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
18176        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
18177        // .. })` carrying the offending raw string verbatim. The three
18178        // arms jointly pin that the validator's raw-string binding is
18179        // the accessor's return, not a peer projection — any future
18180        // silent detour that had the accessor collapse `Some("")` to
18181        // `None` would silently absorb the empty-after-trim refusal
18182        // case at the accessor boundary.
18183        caixa_with_restart_window(None)
18184            .validate_restart_window()
18185            .expect("None :restart-window must validate through the accessor");
18186        caixa_with_restart_window(Some("60s"))
18187            .validate_restart_window()
18188            .expect("canonical :restart-window \"60s\" must validate through the accessor");
18189        let err = caixa_with_restart_window(Some("1.5s"))
18190            .validate_restart_window()
18191            .expect_err("fractional-seconds :restart-window must fail through the accessor");
18192        assert!(
18193            matches!(
18194                err,
18195                ManifestError::RestartWindowMalformed { ref restart_window, .. }
18196                    if restart_window == "1.5s"
18197            ),
18198            "validator must carry the offending raw string verbatim \
18199             from the accessor's borrowed &str (got {err:?})",
18200        );
18201    }
18202
18203    #[test]
18204    fn supervisor_view_restart_window_arm_routes_through_accessor() {
18205        // Composition pin: [`Caixa::supervisor_view`]'s
18206        // per-`:restart-window` [`SupervisorSpec`] construction arm
18207        // must key off [`Caixa::restart_window`]'s soft-swallowing
18208        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
18209        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
18210        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
18211        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
18212        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
18213        // (the shared codec's canonical parse); (3) codec-rejected
18214        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
18215        // (the soft-swallow preserving the view's best-effort shape).
18216        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18217        let view = c.supervisor_view().expect("Supervisor kind has a view");
18218        assert_eq!(
18219            view.restart_window(),
18220            None,
18221            "supervisor_view must project outer None :restart-window \
18222             onto None on the composed SupervisorSpec (never-reset \
18223             sentinel) through the accessor's None-return unchanged",
18224        );
18225
18226        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
18227        let view = c.supervisor_view().expect("Supervisor kind has a view");
18228        assert_eq!(
18229            view.restart_window(),
18230            Some(std::time::Duration::from_secs(60)),
18231            "supervisor_view must fold outer Some(\"60s\") through the \
18232             shared duration_codec into Duration::from_secs(60) on the \
18233             composed SupervisorSpec (accessor's Some(&str) → codec \
18234             parse → Some(Duration))",
18235        );
18236
18237        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
18238        let view = c.supervisor_view().expect("Supervisor kind has a view");
18239        assert_eq!(
18240            view.restart_window(),
18241            None,
18242            "supervisor_view must soft-swallow the shared-codec parse \
18243             failure to None (the view's best-effort shape the sibling \
18244             manifest-level validate_restart_window surfaces as \
18245             RestartWindowMalformed); the accessor's raw-string return \
18246             is the single input every downstream consumer keys off",
18247        );
18248    }
18249
18250    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
18251
18252    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
18253        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18254        c.upgrade_from = upgrade_from;
18255        c
18256    }
18257
18258    #[test]
18259    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
18260        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
18261        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
18262        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
18263        // typed `Vec<UpgradeFromEntry>` verbatim as a
18264        // `&[UpgradeFromEntry]` slice-view over the same backing
18265        // buffer the raw `self.upgrade_from.as_slice()` field access
18266        // borrows from, element-equal across every representative
18267        // fixture in the accept-set — `[]` (the "no hot-upgrade path
18268        // declared" arm every `defcaixa` without an `:upgrade-from`
18269        // block carries; `#[serde(default)]` folds an omitted slot
18270        // onto `Vec::new()`), a canonical single-entry `Restart`
18271        // fixture (the shape most Servicos carry — a single prior
18272        // version with the fallback strategy), a canonical multi-
18273        // entry list carrying every typed instruction variant
18274        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
18275        // `Restart`), and a past-the-guard sentinel — a duplicate-
18276        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
18277        // ([`crate::upgrade::validate_upgrade_from`] rejects through
18278        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
18279        // ship the raw slot verbatim so struct-literal fixtures
18280        // continue to expose the duplicate at the accessor boundary).
18281        //
18282        // Pins against a future silent detour that returned an owned
18283        // `Vec<UpgradeFromEntry>` (which would type-check but silently
18284        // clone on every accessor call, breaking the zero-cost
18285        // projection every peer sibling slice accessor carries), a
18286        // `[dup, dup] → [dup]` dedup collapse (which would silently
18287        // absorb the `DuplicateFrom` refusal case at the accessor
18288        // boundary and the [`crate::StandardLayout::verify`] cross-
18289        // entry gate would silently accept a struct-literal `Caixa`
18290        // carrying the drift), a reference to an operator-resolved
18291        // overlay (the future per-cluster `:upgrade-overrides` slot
18292        // — its resolution must land at exactly this accessor body,
18293        // not silently divert the raw slot away from a second
18294        // consumer), or an axis-shuffled projection (a future detour
18295        // that reordered entries through the accessor would silently
18296        // split the paired [`crate::StandardLayout::verify`] per-
18297        // `:upgrade-from` shape gate's traversal input from the peer
18298        // [`crate::render::servico_m2_overlay`] emitter's projection
18299        // input, since the operator's hot-upgrade dispatch matches
18300        // per-`:from` and axis reordering would silently split the
18301        // per-entry script-path existence probe's iteration order
18302        // from the M2 overlay emitter's serialized-entry order).
18303        //
18304        // First outer top-level [`Caixa`] `&[Composite]`-return
18305        // slice accessor pin on the substrate primitive for M2 / M3
18306        // typed-slot vec-carry axes — opens the outer-`Caixa`
18307        // `&[Composite]` composite-slice projection pattern the
18308        // sibling `:children` [`crate::supervisor::ChildSpec`] /
18309        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
18310        // [`crate::aplicacao::WitContract`] future outer-composite-
18311        // slice pins fold on. Peer of the closed outer-`Caixa`
18312        // scalar `Option<&Composite>` composite-reference family the
18313        // sibling `limits` / `behavior` / `politicas` / `placement`
18314        // / `entrada` `..._returns_..._option_ref_verbatim_across_
18315        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
18316        // the "byte-equal, borrow-shared" outer-accessor discipline
18317        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
18318        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18319        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
18320            vec![],
18321            vec![UpgradeFromEntry {
18322                from: "0.0.1".into(),
18323                instructions: vec![UpgradeInstruction::Restart],
18324            }],
18325            vec![
18326                UpgradeFromEntry {
18327                    from: "0.0.1".into(),
18328                    instructions: vec![
18329                        UpgradeInstruction::LoadModule {
18330                            module: "demo".into(),
18331                        },
18332                        UpgradeInstruction::SoftPurge {
18333                            module: "demo".into(),
18334                        },
18335                    ],
18336                },
18337                UpgradeFromEntry {
18338                    from: "0.0.2".into(),
18339                    instructions: vec![
18340                        UpgradeInstruction::StateChange {
18341                            script: "servicos/upgrade.lisp".into(),
18342                        },
18343                        UpgradeInstruction::Purge {
18344                            module: "demo".into(),
18345                        },
18346                        UpgradeInstruction::Restart,
18347                    ],
18348                },
18349            ],
18350            vec![
18351                UpgradeFromEntry {
18352                    from: "0.1.0".into(),
18353                    instructions: vec![UpgradeInstruction::Restart],
18354                },
18355                UpgradeFromEntry {
18356                    from: "0.1.0".into(),
18357                    instructions: vec![UpgradeInstruction::Restart],
18358                },
18359            ],
18360        ];
18361        for upgrade_from in fixtures {
18362            let c = caixa_with_upgrade_from(upgrade_from.clone());
18363            assert_eq!(
18364                c.upgrade_from(),
18365                upgrade_from.as_slice(),
18366                "Caixa::upgrade_from must return :upgrade-from \
18367                 verbatim (got {:?}, expected {upgrade_from:?})",
18368                c.upgrade_from(),
18369            );
18370            assert_eq!(
18371                c.upgrade_from(),
18372                c.upgrade_from.as_slice(),
18373                "Caixa::upgrade_from must element-equal the raw \
18374                 `self.upgrade_from.as_slice()` field access across \
18375                 every value in the Vec<UpgradeFromEntry> accept-set",
18376            );
18377            assert_eq!(
18378                c.upgrade_from().is_empty(),
18379                c.upgrade_from.is_empty(),
18380                "Caixa::upgrade_from().is_empty() must byte-equal \
18381                 self.upgrade_from.is_empty() — a presence-bit drift \
18382                 would silently split the paired \
18383                 Caixa::declared_servico_slots M2 declared-slot \
18384                 enumerator's presence probe from the peer \
18385                 crate::render::servico_m2_overlay M2 overlay \
18386                 emitter's presence gate",
18387            );
18388        }
18389    }
18390
18391    #[test]
18392    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
18393        // Composition pin: [`Caixa::declared_servico_slots`]'s
18394        // `:upgrade-from` presence-probe arm must key off
18395        // [`Caixa::upgrade_from`], not the raw
18396        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
18397        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
18398        // instructions: vec![Restart] }], .. }` must push
18399        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
18400        // (the presence bit is non-empty, so the M2 kind-coherence
18401        // gate must surface the slot as "declared"), and a `Caixa {
18402        // upgrade_from: vec![], .. }` must NOT push the label (the
18403        // "author omitted the slot entirely" arm — the empty-slice
18404        // partition the serde-default folds onto). The pair jointly
18405        // pins the accessor + declared-slot enumerator composition:
18406        // any future silent detour that had the accessor collapse
18407        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
18408        // is_empty())` projection) would silently absorb the
18409        // "declared but degenerate" arm at the accessor boundary and
18410        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
18411        // coherence gate would silently accept a struct-literal
18412        // `Caixa` carrying the drift.
18413        //
18414        // Peer of the sibling
18415        // `declared_servico_slots_limits_arm_routes_through_accessor`
18416        // (b2bd9d7) and
18417        // `declared_servico_slots_behavior_arm_routes_through_accessor`
18418        // (35d8b52) composition pins on the sibling `:limits` /
18419        // `:behavior` outer-`Option<&Composite>` arms — same "the
18420        // enumerator gate must route through the substrate-primitive
18421        // typed dispatch" discipline extended onto the third M2
18422        // Servico-runtime slot axis, closing the enumerator's routing
18423        // invariant on every M2 arm.
18424        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18425        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18426            from: "0.0.1".into(),
18427            instructions: vec![UpgradeInstruction::Restart],
18428        }]);
18429        let slots = c.declared_servico_slots();
18430        assert!(
18431            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18432            "declared_servico_slots must push \
18433             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18434             non-empty — the accessor and the enumerator gate must \
18435             route through the same substrate-primitive typed \
18436             dispatch on the outer :upgrade-from presence bit (got \
18437             slots={slots:?})",
18438        );
18439        let c = caixa_with_upgrade_from(vec![]);
18440        let slots = c.declared_servico_slots();
18441        assert!(
18442            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18443            "declared_servico_slots must NOT push \
18444             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18445             empty — the author-omitted arm must route through the \
18446             accessor's empty-slice return unchanged (got \
18447             slots={slots:?})",
18448        );
18449    }
18450
18451    #[test]
18452    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
18453        // Composition pin: [`crate::render::servico_m2_overlay`]'s
18454        // per-`:upgrade-from` M2 overlay emit arm must key off
18455        // [`Caixa::upgrade_from`], not the raw
18456        // `!caixa.upgrade_from.is_empty()` presence gate + the
18457        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
18458        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
18459        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
18460        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
18461        // sequence in the overlay (the emitter fans onto the serde
18462        // slice-serialization), and a `Caixa { upgrade_from: vec![],
18463        // .. }` must omit the key entirely (the empty-slice
18464        // partition — the `!.is_empty()` outer gate elides the key
18465        // when the author omitted the slot). The pair jointly pins
18466        // the accessor + M2 overlay emitter composition: any future
18467        // silent detour that had the accessor return a fresh-cloned
18468        // `Vec<UpgradeFromEntry>` copy would silently break the
18469        // reference-identity pin the peer per-entry
18470        // `serde_yaml::to_value(caixa.upgrade_from())` projection
18471        // reads from — the projection would clone once per accessor
18472        // call instead of borrowing the storage buffer verbatim.
18473        //
18474        // Peer of the sibling
18475        // `servico_m2_overlay_limits_arm_routes_through_accessor`
18476        // (b2bd9d7) and
18477        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
18478        // (35d8b52) composition pins on the sibling `:limits` /
18479        // `:behavior` outer-`Option<&Composite>` arms — same "the
18480        // M2 overlay emitter must route through the substrate-
18481        // primitive typed dispatch" discipline extended onto the
18482        // third M2 Servico-runtime slot axis, closing the overlay
18483        // emitter's routing invariant on every M2 arm.
18484        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
18485        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18486        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18487            from: "0.0.1".into(),
18488            instructions: vec![UpgradeInstruction::Restart],
18489        }]);
18490        let overlay = servico_m2_overlay(&c).unwrap();
18491        assert!(
18492            overlay.contains_key(M2_KEY_UPGRADE_FROM),
18493            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
18494             `:upgrade-from` is non-empty — the accessor and the M2 \
18495             overlay emitter must route through the same substrate- \
18496             primitive typed dispatch on the outer :upgrade-from \
18497             slice (got overlay={overlay:?})",
18498        );
18499        let c = caixa_with_upgrade_from(vec![]);
18500        let overlay = servico_m2_overlay(&c).unwrap();
18501        assert!(
18502            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
18503            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
18504             `:upgrade-from` is empty — the empty-slice partition \
18505             must route through the accessor's empty-slice return \
18506             unchanged (got overlay={overlay:?})",
18507        );
18508    }
18509
18510    #[test]
18511    fn upgrade_from_projects_slice_by_borrow() {
18512        // The by-borrow pin: [`Caixa::upgrade_from`] returns
18513        // `&[UpgradeFromEntry]` by borrow — the returned slice
18514        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
18515        // the `:upgrade-from` slot and the accessor must not clone
18516        // the backing `Vec` on every call. Peer of the sibling
18517        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
18518        // (`autores_projects_slice_by_borrow` b5d813f,
18519        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18520        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18521        // `exe_projects_slice_by_borrow` 65d9527,
18522        // `servicos_projects_slice_by_borrow` 611f78b,
18523        // `deps_projects_slice_by_borrow` ad34b4e,
18524        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
18525        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
18526        // axes — extended here to the first outer-`Caixa`
18527        // composite-element `&[Composite]` axis: the accessor's
18528        // returned slice must borrow from `&self` (the returned
18529        // reference's lifetime is tied to `&self`), and calling the
18530        // accessor twice on the same [`Caixa`] must yield slices
18531        // that are pointer-equal (the underlying byte-buffer is the
18532        // storage `Vec`'s allocation, not a fresh copy) as well as
18533        // value-equal (idempotent, no side effects on `&self`).
18534        //
18535        // Pins against a future silent detour that returned an owned
18536        // `Vec<UpgradeFromEntry>` (which would type-check but
18537        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
18538        // return (which would leak the backing `Vec`'s
18539        // grow/push/reserve surface no downstream consumer reaches
18540        // for), or a one-arm-only accessor that returned a
18541        // saturating value on some sentinel input.
18542        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18543        for upgrade_from in [
18544            vec![],
18545            vec![UpgradeFromEntry {
18546                from: "0.0.1".into(),
18547                instructions: vec![UpgradeInstruction::Restart],
18548            }],
18549            vec![
18550                UpgradeFromEntry {
18551                    from: "0.0.1".into(),
18552                    instructions: vec![UpgradeInstruction::Restart],
18553                },
18554                UpgradeFromEntry {
18555                    from: "0.0.2".into(),
18556                    instructions: vec![UpgradeInstruction::SoftPurge {
18557                        module: "demo".into(),
18558                    }],
18559                },
18560            ],
18561        ] {
18562            let c = caixa_with_upgrade_from(upgrade_from.clone());
18563            let first = c.upgrade_from();
18564            let second = c.upgrade_from();
18565            assert_eq!(
18566                first, second,
18567                "Caixa::upgrade_from must be idempotent — two \
18568                 successive calls on the same &self must return the \
18569                 same &[UpgradeFromEntry]",
18570            );
18571            assert_eq!(
18572                first.as_ptr(),
18573                second.as_ptr(),
18574                "Caixa::upgrade_from must borrow the underlying \
18575                 Vec<UpgradeFromEntry> storage — two successive calls \
18576                 must return slices with the same backing pointer (a \
18577                 fresh Vec<UpgradeFromEntry> clone would change the \
18578                 pointer on every call)",
18579            );
18580            assert_eq!(
18581                first,
18582                upgrade_from.as_slice(),
18583                "Caixa::upgrade_from must return :upgrade-from \
18584                 verbatim by borrow — got {first:?}, expected \
18585                 {upgrade_from:?}",
18586            );
18587        }
18588    }
18589
18590    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
18591
18592    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
18593        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18594        c.children = children;
18595        c
18596    }
18597
18598    #[test]
18599    fn children_returns_children_slice_verbatim_across_permutations() {
18600        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
18601        // outer-composite `&[ChildSpec]`-return slice-shape pin:
18602        // [`Caixa::children`] must return the `:children` typed
18603        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
18604        // the same backing buffer the raw `self.children.as_slice()`
18605        // field access borrows from, element-equal across every
18606        // representative fixture in the accept-set — `[]` (the "no
18607        // static children declared" arm every non-`Supervisor`-kind
18608        // `defcaixa` carries by `#[serde(default)]` and every
18609        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
18610        // a canonical single-child `Permanent` fixture (the shape
18611        // most `OneForOne` supervisors carry — a single long-running
18612        // worker child), a canonical multi-child list carrying every
18613        // typed restart-policy variant (`Permanent` / `Transient` /
18614        // `Temporary`), and a past-the-guard sentinel — a duplicate
18615        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
18616        // ([`crate::SupervisorSpec::validate`] rejects through
18617        // `DuplicateChildNome { nome: "w" }` but the accessor must
18618        // ship the raw slot verbatim so struct-literal fixtures
18619        // continue to expose the duplicate at the accessor boundary).
18620        //
18621        // Pins against a future silent detour that returned an owned
18622        // `Vec<ChildSpec>` (which would type-check but silently clone
18623        // on every accessor call, breaking the zero-cost projection
18624        // every peer sibling slice accessor carries), a `[dup, dup] →
18625        // [dup]` dedup collapse (which would silently absorb the
18626        // `DuplicateChildNome` refusal case at the accessor boundary
18627        // and the [`crate::StandardLayout::verify`] cross-child gate
18628        // would silently accept a struct-literal `Caixa` carrying the
18629        // drift), a reference to an operator-resolved overlay (the
18630        // future per-cluster `:children-overrides` slot — its
18631        // resolution must land at exactly this accessor body, not
18632        // silently divert the raw slot away from a second consumer),
18633        // or an axis-shuffled projection (a future detour that
18634        // reordered children through the accessor would silently
18635        // split the paired [`crate::StandardLayout::verify`] per-
18636        // supervisor gate's traversal input from the peer
18637        // [`Self::supervisor_view`] fold-in path's clone-order input,
18638        // since the OTP `RestForOne` restart strategy dispatches on
18639        // declared child order and axis reordering would silently
18640        // split the operator's per-cluster restart-fan-out order
18641        // from the caixa.lisp source-order).
18642        //
18643        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
18644        // accessor pin on the substrate primitive for M2 / M3 typed-
18645        // slot vec-carry axes — folds on the outer-`Caixa`
18646        // `&[Composite]` composite-slice sub-family the sibling
18647        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18648        // (2a1f907) pin opened, peer at the outer altitude of the
18649        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
18650        // (bc92bce) accessor on the same OTP-supervisor static-child-
18651        // list axis.
18652        use crate::supervisor::{ChildSpec, RestartPolicy};
18653        let fixtures: Vec<Vec<ChildSpec>> = vec![
18654            vec![],
18655            vec![ChildSpec {
18656                caixa: "worker".into(),
18657                versao: "^0.1".into(),
18658                restart: RestartPolicy::Permanent,
18659            }],
18660            vec![
18661                ChildSpec {
18662                    caixa: "worker-a".into(),
18663                    versao: "^0.1".into(),
18664                    restart: RestartPolicy::Permanent,
18665                },
18666                ChildSpec {
18667                    caixa: "worker-b".into(),
18668                    versao: "^0.1".into(),
18669                    restart: RestartPolicy::Transient,
18670                },
18671                ChildSpec {
18672                    caixa: "worker-c".into(),
18673                    versao: "^0.1".into(),
18674                    restart: RestartPolicy::Temporary,
18675                },
18676            ],
18677            vec![
18678                ChildSpec {
18679                    caixa: "w".into(),
18680                    versao: "^0.1".into(),
18681                    restart: RestartPolicy::Permanent,
18682                },
18683                ChildSpec {
18684                    caixa: "w".into(),
18685                    versao: "^0.1".into(),
18686                    restart: RestartPolicy::Permanent,
18687                },
18688            ],
18689        ];
18690        for children in fixtures {
18691            let c = caixa_with_children(children.clone());
18692            assert_eq!(
18693                c.children(),
18694                children.as_slice(),
18695                "Caixa::children must return :children verbatim \
18696                 (got {:?}, expected {children:?})",
18697                c.children(),
18698            );
18699            assert_eq!(
18700                c.children(),
18701                c.children.as_slice(),
18702                "Caixa::children must element-equal the raw \
18703                 `self.children.as_slice()` field access across \
18704                 every value in the Vec<ChildSpec> accept-set",
18705            );
18706            assert_eq!(
18707                c.children().is_empty(),
18708                c.children.is_empty(),
18709                "Caixa::children().is_empty() must byte-equal \
18710                 self.children.is_empty() — a presence-bit drift \
18711                 would silently split the paired \
18712                 Caixa::declared_supervisor_slots supervisor-tree \
18713                 declared-slot enumerator's presence probe from the \
18714                 peer Caixa::supervisor_view typed-view composer's \
18715                 fold-in path",
18716            );
18717        }
18718    }
18719
18720    #[test]
18721    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
18722        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18723        // `:children` presence-probe arm must key off
18724        // [`Caixa::children`], not the raw
18725        // `!self.children.is_empty()` field-probe. Structurally: a
18726        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
18727        // "^0.1", restart: Permanent }], .. }` must push
18728        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
18729        // (the presence bit is non-empty, so the supervisor-tree
18730        // kind-coherence gate must surface the slot as "declared"),
18731        // and a `Caixa { children: vec![], .. }` must NOT push the
18732        // label (the "author omitted the slot entirely" arm — the
18733        // empty-slice partition the serde-default folds onto). The
18734        // pair jointly pins the accessor + declared-slot enumerator
18735        // composition: any future silent detour that had the accessor
18736        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
18737        // "__reserved__")` projection) would silently absorb the
18738        // "declared but degenerate" arm at the accessor boundary and
18739        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
18740        // kind-coherence gate would silently accept a struct-literal
18741        // `Caixa` carrying the drift.
18742        //
18743        // Peer of the sibling
18744        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18745        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
18746        // same "the enumerator gate must route through the substrate-
18747        // primitive typed dispatch" discipline extended onto the
18748        // supervisor-tree `:children` composite-slice arm.
18749        use crate::supervisor::{ChildSpec, RestartPolicy};
18750        let c = caixa_with_children(vec![ChildSpec {
18751            caixa: "w".into(),
18752            versao: "^0.1".into(),
18753            restart: RestartPolicy::Permanent,
18754        }]);
18755        let slots = c.declared_supervisor_slots();
18756        assert!(
18757            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
18758            "declared_supervisor_slots must push \
18759             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
18760             non-empty — the accessor and the enumerator gate must \
18761             route through the same substrate-primitive typed \
18762             dispatch on the outer :children presence bit (got \
18763             slots={slots:?})",
18764        );
18765        let c = caixa_with_children(vec![]);
18766        let slots = c.declared_supervisor_slots();
18767        assert!(
18768            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
18769            "declared_supervisor_slots must NOT push \
18770             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
18771             empty — the author-omitted arm must route through the \
18772             accessor's empty-slice return unchanged (got \
18773             slots={slots:?})",
18774        );
18775    }
18776
18777    #[test]
18778    fn supervisor_view_children_arm_routes_through_accessor() {
18779        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
18780        // fold-in arm must key off [`Caixa::children`], not the raw
18781        // `self.children.clone()` field-clone. Structurally: a `Caixa {
18782        // kind: Supervisor, estrategia: Some(OneForOne), children:
18783        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
18784        // per-child list through the accessor into the typed
18785        // [`SupervisorSpec`] view's `children` field verbatim — every
18786        // entry the accessor surfaces must land in the view's
18787        // `children` slot in the same order. The pair jointly pins the
18788        // accessor + view-composer composition: any future silent
18789        // detour that had the accessor return a fresh-cloned
18790        // `Vec<ChildSpec>` copy would silently break the reference-
18791        // identity pin the peer `supervisor_view` fold-in path reads
18792        // from — the fold would clone once more per accessor call
18793        // instead of borrowing the storage buffer verbatim once.
18794        //
18795        // Peer of the sibling
18796        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
18797        // family) composition pin on the peer kind-gate arm — same
18798        // "the view composer must route through the substrate-
18799        // primitive typed dispatch" discipline extended onto the
18800        // per-`:children` fold-in arm, closing the supervisor-view
18801        // composer's routing invariant on the composite-slice input.
18802        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
18803        let mut c = caixa_with_children(vec![
18804            ChildSpec {
18805                caixa: "worker-a".into(),
18806                versao: "^0.1".into(),
18807                restart: RestartPolicy::Permanent,
18808            },
18809            ChildSpec {
18810                caixa: "worker-b".into(),
18811                versao: "^0.1".into(),
18812                restart: RestartPolicy::Transient,
18813            },
18814        ]);
18815        c.kind = crate::CaixaKind::Supervisor;
18816        c.estrategia = Some(RestartStrategy::OneForOne);
18817        let view = c
18818            .supervisor_view()
18819            .expect("Supervisor kind must produce a supervisor_view");
18820        assert_eq!(
18821            view.children(),
18822            c.children(),
18823            "supervisor_view must fold Caixa::children verbatim into \
18824             SupervisorSpec::children — the accessor and the view \
18825             composer must route through the same substrate-primitive \
18826             typed dispatch on the outer :children slice (got view \
18827             children={:?}, expected {:?})",
18828            view.children(),
18829            c.children(),
18830        );
18831    }
18832
18833    #[test]
18834    fn children_projects_slice_by_borrow() {
18835        // The by-borrow pin: [`Caixa::children`] returns
18836        // `&[ChildSpec]` by borrow — the returned slice borrows the
18837        // underlying `Vec<ChildSpec>` storage of the `:children` slot
18838        // and the accessor must not clone the backing `Vec` on every
18839        // call. Peer of the sibling outer top-level [`Caixa`]
18840        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
18841        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
18842        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18843        // `exe_projects_slice_by_borrow` 65d9527,
18844        // `servicos_projects_slice_by_borrow` 611f78b,
18845        // `deps_projects_slice_by_borrow` ad34b4e,
18846        // `deps_dev_projects_slice_by_borrow` f7fd81e,
18847        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
18848        // sibling outer top-level [`Caixa`] scalar-element and
18849        // composite-element `&[T]` axes — folds on the outer-`Caixa`
18850        // composite-element `&[Composite]` axis: the accessor's
18851        // returned slice must borrow from `&self` (the returned
18852        // reference's lifetime is tied to `&self`), and calling the
18853        // accessor twice on the same [`Caixa`] must yield slices
18854        // that are pointer-equal (the underlying byte-buffer is the
18855        // storage `Vec`'s allocation, not a fresh copy) as well as
18856        // value-equal (idempotent, no side effects on `&self`).
18857        //
18858        // Pins against a future silent detour that returned an owned
18859        // `Vec<ChildSpec>` (which would type-check but silently clone
18860        // on every call), a `&Vec<ChildSpec>` return (which would leak
18861        // the backing `Vec`'s grow/push/reserve surface no downstream
18862        // consumer reaches for), or a one-arm-only accessor that
18863        // returned a saturating value on some sentinel input.
18864        use crate::supervisor::{ChildSpec, RestartPolicy};
18865        for children in [
18866            vec![],
18867            vec![ChildSpec {
18868                caixa: "w".into(),
18869                versao: "^0.1".into(),
18870                restart: RestartPolicy::Permanent,
18871            }],
18872            vec![
18873                ChildSpec {
18874                    caixa: "worker-a".into(),
18875                    versao: "^0.1".into(),
18876                    restart: RestartPolicy::Permanent,
18877                },
18878                ChildSpec {
18879                    caixa: "worker-b".into(),
18880                    versao: "^0.1".into(),
18881                    restart: RestartPolicy::Transient,
18882                },
18883            ],
18884        ] {
18885            let c = caixa_with_children(children.clone());
18886            let first = c.children();
18887            let second = c.children();
18888            assert_eq!(
18889                first, second,
18890                "Caixa::children must be idempotent — two successive \
18891                 calls on the same &self must return the same \
18892                 &[ChildSpec]",
18893            );
18894            assert_eq!(
18895                first.as_ptr(),
18896                second.as_ptr(),
18897                "Caixa::children must borrow the underlying \
18898                 Vec<ChildSpec> storage — two successive calls must \
18899                 return slices with the same backing pointer (a fresh \
18900                 Vec<ChildSpec> clone would change the pointer on \
18901                 every call)",
18902            );
18903            assert_eq!(
18904                first,
18905                children.as_slice(),
18906                "Caixa::children must return :children verbatim by \
18907                 borrow — got {first:?}, expected {children:?}",
18908            );
18909        }
18910    }
18911
18912    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
18913
18914    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
18915        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18916        c.kind = CaixaKind::Aplicacao;
18917        c.membros = membros;
18918        c
18919    }
18920
18921    #[test]
18922    fn membros_returns_membros_slice_verbatim_across_permutations() {
18923        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
18924        // composite `&[Membro]`-return slice-shape pin:
18925        // [`Caixa::membros`] must return the `:membros` typed
18926        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
18927        // same backing buffer the raw `self.membros.as_slice()` field
18928        // access borrows from, element-equal across every
18929        // representative fixture in the accept-set — `[]` (the "no
18930        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
18931        // carries by `#[serde(default)]` and every partially-authored
18932        // Aplicacao carries before the
18933        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
18934        // canonical single-member fixture (the shape a minimal
18935        // Aplicacao carries — one Servico wrapping one contained
18936        // computation), a canonical multi-member list carrying three
18937        // distinct entries (the canonical checkout-shape Aplicacao —
18938        // cart / pricing / auth — every canonical example carries), and
18939        // a past-the-guard sentinel — a duplicate `:caixa`
18940        // `[("cart", ...), ("cart", ...)]` entry pair
18941        // ([`crate::AplicacaoSpec::validate`] rejects through
18942        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
18943        // the raw slot verbatim so struct-literal fixtures continue to
18944        // expose the duplicate at the accessor boundary).
18945        //
18946        // Pins against a future silent detour that returned an owned
18947        // `Vec<Membro>` (which would type-check but silently clone on
18948        // every accessor call, breaking the zero-cost projection every
18949        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
18950        // dedup collapse (which would silently absorb the
18951        // `DuplicateMembro` refusal case at the accessor boundary and
18952        // the [`crate::StandardLayout::verify`] cross-member gate would
18953        // silently accept a struct-literal `Caixa` carrying the drift),
18954        // a reference to an operator-resolved overlay (the future per-
18955        // cluster `:membros-overrides` slot — its resolution must land
18956        // at exactly this accessor body, not silently divert the raw
18957        // slot away from a second consumer), or an axis-shuffled
18958        // projection (a future detour that reordered members through
18959        // the accessor would silently split the paired
18960        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
18961        // traversal input from the peer [`Self::aplicacao_view`] fold-
18962        // in path's clone-order input, since the canonical `:contratos`
18963        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
18964        // read the member set through the same slice).
18965        //
18966        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
18967        // accessor pin on the substrate primitive for M2 / M3 typed-
18968        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
18969        // arm of the `&[Composite]` composite-slice sub-family the
18970        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18971        // (2a1f907) and
18972        // `children_returns_children_slice_verbatim_across_permutations`
18973        // (c17b51e) pins opened, peer at the outer altitude of the
18974        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
18975        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
18976        // list axis.
18977        use crate::aplicacao::Membro;
18978        let fixtures: Vec<Vec<Membro>> = vec![
18979            vec![],
18980            vec![Membro {
18981                caixa: "cart".into(),
18982                versao: "^0.1".into(),
18983            }],
18984            vec![
18985                Membro {
18986                    caixa: "cart".into(),
18987                    versao: "^0.1".into(),
18988                },
18989                Membro {
18990                    caixa: "pricing".into(),
18991                    versao: "^0.2".into(),
18992                },
18993                Membro {
18994                    caixa: "auth".into(),
18995                    versao: "^1.0".into(),
18996                },
18997            ],
18998            vec![
18999                Membro {
19000                    caixa: "cart".into(),
19001                    versao: "^0.1".into(),
19002                },
19003                Membro {
19004                    caixa: "cart".into(),
19005                    versao: "^0.1".into(),
19006                },
19007            ],
19008        ];
19009        for membros in fixtures {
19010            let c = caixa_aplicacao_with_membros(membros.clone());
19011            assert_eq!(
19012                c.membros(),
19013                membros.as_slice(),
19014                "Caixa::membros must return :membros verbatim \
19015                 (got {:?}, expected {membros:?})",
19016                c.membros(),
19017            );
19018            assert_eq!(
19019                c.membros(),
19020                c.membros.as_slice(),
19021                "Caixa::membros must element-equal the raw \
19022                 `self.membros.as_slice()` field access across every \
19023                 value in the Vec<Membro> accept-set",
19024            );
19025            assert_eq!(
19026                c.membros().is_empty(),
19027                c.membros.is_empty(),
19028                "Caixa::membros().is_empty() must byte-equal \
19029                 self.membros.is_empty() — a presence-bit drift would \
19030                 silently split the paired Caixa::declared_mesh_slots \
19031                 mesh declared-slot enumerator's presence probe from \
19032                 the peer Caixa::aplicacao_view typed-view composer's \
19033                 fold-in path",
19034            );
19035        }
19036    }
19037
19038    #[test]
19039    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
19040        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
19041        // presence-probe arm must key off [`Caixa::membros`], not the
19042        // raw `!self.membros.is_empty()` field-probe. Structurally: a
19043        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
19044        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
19045        // declared-slot list (the presence bit is non-empty, so the
19046        // mesh kind-coherence gate must surface the slot as
19047        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
19048        // push the label (the "author omitted the slot entirely" arm
19049        // — the empty-slice partition the serde-default folds onto).
19050        // The pair jointly pins the accessor + declared-slot
19051        // enumerator composition: any future silent detour that had
19052        // the accessor collapse `[Membro { .. }]` to `[]` (a
19053        // `.filter(|m| m.nome() != "__reserved__")` projection) would
19054        // silently absorb the "declared but degenerate" arm at the
19055        // accessor boundary and the
19056        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19057        // coherence gate would silently accept a struct-literal
19058        // `Caixa` carrying the drift.
19059        //
19060        // Peer of the sibling
19061        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19062        // (2a1f907) and
19063        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19064        // (c17b51e) composition pins on the M2 `:upgrade-from` /
19065        // `:children` composite-slice arms — same "the enumerator gate
19066        // must route through the substrate-primitive typed dispatch"
19067        // discipline extended onto the M3 `:membros` composite-slice
19068        // arm, opening the M3 arm of the declared-slot enumerator's
19069        // routing invariant.
19070        use crate::aplicacao::Membro;
19071        let c = caixa_aplicacao_with_membros(vec![Membro {
19072            caixa: "cart".into(),
19073            versao: "^0.1".into(),
19074        }]);
19075        let slots = c.declared_mesh_slots();
19076        assert!(
19077            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19078            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
19079             `:membros` is non-empty — the accessor and the enumerator \
19080             gate must route through the same substrate-primitive \
19081             typed dispatch on the outer :membros presence bit (got \
19082             slots={slots:?})",
19083        );
19084        let c = caixa_aplicacao_with_membros(vec![]);
19085        let slots = c.declared_mesh_slots();
19086        assert!(
19087            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19088            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
19089             when `:membros` is empty — the author-omitted arm must \
19090             route through the accessor's empty-slice return unchanged \
19091             (got slots={slots:?})",
19092        );
19093    }
19094
19095    #[test]
19096    fn aplicacao_view_membros_arm_routes_through_accessor() {
19097        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
19098        // fold-in arm must key off [`Caixa::membros`], not the raw
19099        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
19100        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
19101        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
19102        // member list through the accessor into the typed
19103        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
19104        // every entry the accessor surfaces must land in the view's
19105        // `membros` slot in the same order. The pair jointly pins the
19106        // accessor + view-composer composition: any future silent
19107        // detour that had the accessor return a fresh-cloned
19108        // `Vec<Membro>` copy would silently break the reference-
19109        // identity pin the peer `aplicacao_view` fold-in path reads
19110        // from — the fold would clone once more per accessor call
19111        // instead of borrowing the storage buffer verbatim once.
19112        //
19113        // Peer of the sibling
19114        // `aplicacao_view_politicas_arm_folds_through_accessor`
19115        // (5d23d29) /
19116        // `aplicacao_view_placement_arm_folds_through_accessor`
19117        // (4fb8074) /
19118        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
19119        // composition pins on the M3 `:politicas` / `:placement` /
19120        // `:entrada` outer-`Option<&Composite>` arms — extended here to
19121        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
19122        // closing the aplicacao-view composer's routing invariant on
19123        // the composite-slice input.
19124        use crate::aplicacao::Membro;
19125        let c = caixa_aplicacao_with_membros(vec![
19126            Membro {
19127                caixa: "cart".into(),
19128                versao: "^0.1".into(),
19129            },
19130            Membro {
19131                caixa: "pricing".into(),
19132                versao: "^0.2".into(),
19133            },
19134        ]);
19135        let view = c
19136            .aplicacao_view()
19137            .expect("Aplicacao kind must produce an aplicacao_view");
19138        assert_eq!(
19139            view.membros(),
19140            c.membros(),
19141            "aplicacao_view must fold Caixa::membros verbatim into \
19142             AplicacaoSpec::membros — the accessor and the view \
19143             composer must route through the same substrate-primitive \
19144             typed dispatch on the outer :membros slice (got view \
19145             membros={:?}, expected {:?})",
19146            view.membros(),
19147            c.membros(),
19148        );
19149    }
19150
19151    #[test]
19152    fn membros_projects_slice_by_borrow() {
19153        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
19154        // borrow — the returned slice borrows the underlying
19155        // `Vec<Membro>` storage of the `:membros` slot and the
19156        // accessor must not clone the backing `Vec` on every call.
19157        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19158        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19159        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19160        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19161        // `exe_projects_slice_by_borrow` 65d9527,
19162        // `servicos_projects_slice_by_borrow` 611f78b,
19163        // `deps_projects_slice_by_borrow` ad34b4e,
19164        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19165        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19166        // `children_projects_slice_by_borrow` c17b51e) on the sibling
19167        // outer top-level [`Caixa`] scalar-element and composite-
19168        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
19169        // slot composite-element `&[Composite]` axis: the accessor's
19170        // returned slice must borrow from `&self` (the returned
19171        // reference's lifetime is tied to `&self`), and calling the
19172        // accessor twice on the same [`Caixa`] must yield slices that
19173        // are pointer-equal (the underlying byte-buffer is the storage
19174        // `Vec`'s allocation, not a fresh copy) as well as value-equal
19175        // (idempotent, no side effects on `&self`).
19176        //
19177        // Pins against a future silent detour that returned an owned
19178        // `Vec<Membro>` (which would type-check but silently clone on
19179        // every call), a `&Vec<Membro>` return (which would leak the
19180        // backing `Vec`'s grow/push/reserve surface no downstream
19181        // consumer reaches for), or a one-arm-only accessor that
19182        // returned a saturating value on some sentinel input.
19183        use crate::aplicacao::Membro;
19184        for membros in [
19185            vec![],
19186            vec![Membro {
19187                caixa: "cart".into(),
19188                versao: "^0.1".into(),
19189            }],
19190            vec![
19191                Membro {
19192                    caixa: "cart".into(),
19193                    versao: "^0.1".into(),
19194                },
19195                Membro {
19196                    caixa: "pricing".into(),
19197                    versao: "^0.2".into(),
19198                },
19199            ],
19200        ] {
19201            let c = caixa_aplicacao_with_membros(membros.clone());
19202            let first = c.membros();
19203            let second = c.membros();
19204            assert_eq!(
19205                first, second,
19206                "Caixa::membros must be idempotent — two successive \
19207                 calls on the same &self must return the same &[Membro]",
19208            );
19209            assert_eq!(
19210                first.as_ptr(),
19211                second.as_ptr(),
19212                "Caixa::membros must borrow the underlying Vec<Membro> \
19213                 storage — two successive calls must return slices with \
19214                 the same backing pointer (a fresh Vec<Membro> clone \
19215                 would change the pointer on every call)",
19216            );
19217            assert_eq!(
19218                first,
19219                membros.as_slice(),
19220                "Caixa::membros must return :membros verbatim by borrow \
19221                 — got {first:?}, expected {membros:?}",
19222            );
19223        }
19224    }
19225
19226    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
19227
19228    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
19229        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19230        c.kind = CaixaKind::Aplicacao;
19231        c.contratos = contratos;
19232        c
19233    }
19234
19235    fn contrato_http_for_test(
19236        de: &str,
19237        para: &str,
19238        endpoint: &str,
19239    ) -> crate::aplicacao::WitContract {
19240        crate::aplicacao::WitContract {
19241            de: de.into(),
19242            para: para.into(),
19243            wit: "wasi:http/proxy".into(),
19244            endpoint: Some(endpoint.into()),
19245            subject: None,
19246            slot: None,
19247        }
19248    }
19249
19250    #[test]
19251    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
19252        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
19253        // composite `&[WitContract]`-return slice-shape pin:
19254        // [`Caixa::contratos`] must return the `:contratos` typed
19255        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
19256        // over the same backing buffer the raw
19257        // `self.contratos.as_slice()` field access borrows from,
19258        // element-equal across every representative fixture in the
19259        // accept-set — `[]` (the "no contracts declared" arm every
19260        // non-`Aplicacao`-kind `defcaixa` carries by
19261        // `#[serde(default)]` and every leaf-Aplicacao with a single
19262        // member carries), a canonical single-edge fixture (the
19263        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
19264        // edge), and a canonical multi-edge fixture with three distinct
19265        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
19266        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
19267        //
19268        // Pins against a future silent detour that returned an owned
19269        // `Vec<WitContract>` (which would type-check but silently clone
19270        // on every accessor call, breaking the zero-cost projection
19271        // every peer sibling slice accessor carries), an axis-shuffled
19272        // projection (a future detour that reordered edges through the
19273        // accessor would silently split the paired
19274        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19275        // traversal input from the peer [`Self::aplicacao_view`] fold-
19276        // in path's clone-order input, since every canonical
19277        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
19278        // seed dispatch reads the edge set through the same slice),
19279        // or a reference to an operator-resolved overlay (the future
19280        // per-cluster `:contratos-overrides` slot — its resolution
19281        // must land at exactly this accessor body, not silently divert
19282        // the raw slot away from a second consumer).
19283        //
19284        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
19285        // accessor pin on the substrate primitive for M2 / M3 typed-
19286        // slot vec-carry axes — closes the outer-`Caixa`
19287        // `&[Composite]` composite-slice sub-family the sibling M2
19288        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19289        // (2a1f907) and
19290        // `children_returns_children_slice_verbatim_across_permutations`
19291        // (c17b51e) pins opened and the M3
19292        // `membros_returns_membros_slice_verbatim_across_permutations`
19293        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
19294        // slot arm of the composite-slice sub-family. Peer at the outer
19295        // altitude of the closed inner-
19296        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
19297        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
19298        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
19299            vec![],
19300            vec![contrato_http_for_test("cart", "catalog", "/items")],
19301            vec![
19302                contrato_http_for_test("cart", "catalog", "/items"),
19303                contrato_http_for_test("cart", "pricing", "/price"),
19304                contrato_http_for_test("cart", "auth", "/whoami"),
19305            ],
19306        ];
19307        for contratos in fixtures {
19308            let c = caixa_aplicacao_with_contratos(contratos.clone());
19309            assert_eq!(
19310                c.contratos(),
19311                contratos.as_slice(),
19312                "Caixa::contratos must return :contratos verbatim \
19313                 (got {:?}, expected {contratos:?})",
19314                c.contratos(),
19315            );
19316            assert_eq!(
19317                c.contratos(),
19318                c.contratos.as_slice(),
19319                "Caixa::contratos must element-equal the raw \
19320                 `self.contratos.as_slice()` field access across every \
19321                 value in the Vec<WitContract> accept-set",
19322            );
19323            assert_eq!(
19324                c.contratos().is_empty(),
19325                c.contratos.is_empty(),
19326                "Caixa::contratos().is_empty() must byte-equal \
19327                 self.contratos.is_empty() — a presence-bit drift would \
19328                 silently split the paired Caixa::declared_mesh_slots \
19329                 mesh declared-slot enumerator's presence probe from \
19330                 the peer Caixa::aplicacao_view typed-view composer's \
19331                 fold-in path",
19332            );
19333        }
19334    }
19335
19336    #[test]
19337    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
19338        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
19339        // presence-probe arm must key off [`Caixa::contratos`], not the
19340        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
19341        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
19342        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
19343        // presence bit is non-empty, so the mesh kind-coherence gate
19344        // must surface the slot as "declared"), and a `Caixa {
19345        // contratos: vec![], .. }` must NOT push the label (the "author
19346        // omitted the slot entirely" arm — the empty-slice partition
19347        // the serde-default folds onto). The pair jointly pins the
19348        // accessor + declared-slot enumerator composition: any future
19349        // silent detour that had the accessor collapse
19350        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
19351        // "__reserved__")` projection) would silently absorb the
19352        // "declared but degenerate" arm at the accessor boundary and
19353        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19354        // coherence gate would silently accept a struct-literal
19355        // `Caixa` carrying the drift.
19356        //
19357        // Peer of the sibling
19358        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19359        // (2a1f907),
19360        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19361        // (c17b51e), and
19362        // `declared_mesh_slots_membros_arm_routes_through_accessor`
19363        // (0f26987) composition pins on the M2 `:upgrade-from` /
19364        // `:children` / M3 `:membros` composite-slice arms — same "the
19365        // enumerator gate must route through the substrate-primitive
19366        // typed dispatch" discipline extended onto the M3 `:contratos`
19367        // composite-slice arm, closing the M3 mesh-slot arm of the
19368        // declared-slot enumerator's routing invariant on the
19369        // composite-slice inputs.
19370        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
19371            "cart", "catalog", "/items",
19372        )]);
19373        let slots = c.declared_mesh_slots();
19374        assert!(
19375            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19376            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
19377             `:contratos` is non-empty — the accessor and the enumerator \
19378             gate must route through the same substrate-primitive \
19379             typed dispatch on the outer :contratos presence bit (got \
19380             slots={slots:?})",
19381        );
19382        let c = caixa_aplicacao_with_contratos(vec![]);
19383        let slots = c.declared_mesh_slots();
19384        assert!(
19385            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19386            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
19387             when `:contratos` is empty — the author-omitted arm must \
19388             route through the accessor's empty-slice return unchanged \
19389             (got slots={slots:?})",
19390        );
19391    }
19392
19393    #[test]
19394    fn aplicacao_view_contratos_arm_routes_through_accessor() {
19395        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
19396        // fold-in arm must key off [`Caixa::contratos`], not the raw
19397        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
19398        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
19399        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
19400        // per-edge list through the accessor into the typed
19401        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
19402        // every entry the accessor surfaces must land in the view's
19403        // `contratos` slot in the same order. The pair jointly pins
19404        // the accessor + view-composer composition: a future silent
19405        // detour that had the accessor shuffle or drop an edge would
19406        // silently split the paired declared-slot enumerator's
19407        // presence bit from the typed-view composer's edge-list, a
19408        // two-consumer split at the enumerator and the view composer
19409        // far from the source `caixa.lisp`.
19410        //
19411        // Peer of the sibling
19412        // `aplicacao_view_membros_arm_routes_through_accessor`
19413        // (0f26987) composition pin on the M3 `:membros` outer-
19414        // `&[Composite]` composite-slice arm, closing the aplicacao-
19415        // view composer's routing invariant on the composite-slice
19416        // inputs at the outer altitude.
19417        let c = caixa_aplicacao_with_contratos(vec![
19418            contrato_http_for_test("cart", "catalog", "/items"),
19419            contrato_http_for_test("cart", "pricing", "/price"),
19420        ]);
19421        let view = c
19422            .aplicacao_view()
19423            .expect("Aplicacao kind must produce an aplicacao_view");
19424        assert_eq!(
19425            view.contratos(),
19426            c.contratos(),
19427            "aplicacao_view must fold Caixa::contratos verbatim into \
19428             AplicacaoSpec::contratos — the accessor and the view \
19429             composer must route through the same substrate-primitive \
19430             typed dispatch on the outer :contratos slice (got view \
19431             contratos={:?}, expected {:?})",
19432            view.contratos(),
19433            c.contratos(),
19434        );
19435    }
19436
19437    #[test]
19438    fn contratos_projects_slice_by_borrow() {
19439        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
19440        // by borrow — the returned slice borrows the underlying
19441        // `Vec<WitContract>` storage of the `:contratos` slot and the
19442        // accessor must not clone the backing `Vec` on every call.
19443        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19444        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19445        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19446        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19447        // `exe_projects_slice_by_borrow` 65d9527,
19448        // `servicos_projects_slice_by_borrow` 611f78b,
19449        // `deps_projects_slice_by_borrow` ad34b4e,
19450        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19451        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19452        // `children_projects_slice_by_borrow` c17b51e,
19453        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
19454        // outer top-level [`Caixa`] scalar-element and composite-
19455        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
19456        // composite-element `&[Composite]` axis on the by-borrow pin:
19457        // the accessor's returned slice must borrow from `&self` (the
19458        // returned reference's lifetime is tied to `&self`), and
19459        // calling the accessor twice on the same [`Caixa`] must yield
19460        // slices that are pointer-equal (the underlying byte-buffer is
19461        // the storage `Vec`'s allocation, not a fresh copy) as well as
19462        // value-equal (idempotent, no side effects on `&self`).
19463        //
19464        // Pins against a future silent detour that returned an owned
19465        // `Vec<WitContract>` (which would type-check but silently clone
19466        // on every call), a `&Vec<WitContract>` return (which would
19467        // leak the backing `Vec`'s grow/push/reserve surface no
19468        // downstream consumer reaches for), or a one-arm-only accessor
19469        // that returned a saturating value on some sentinel input.
19470        for contratos in [
19471            vec![],
19472            vec![contrato_http_for_test("cart", "catalog", "/items")],
19473            vec![
19474                contrato_http_for_test("cart", "catalog", "/items"),
19475                contrato_http_for_test("cart", "pricing", "/price"),
19476            ],
19477        ] {
19478            let c = caixa_aplicacao_with_contratos(contratos.clone());
19479            let first = c.contratos();
19480            let second = c.contratos();
19481            assert_eq!(
19482                first, second,
19483                "Caixa::contratos must be idempotent — two successive \
19484                 calls on the same &self must return the same \
19485                 &[WitContract]",
19486            );
19487            assert_eq!(
19488                first.as_ptr(),
19489                second.as_ptr(),
19490                "Caixa::contratos must borrow the underlying \
19491                 Vec<WitContract> storage — two successive calls must \
19492                 return slices with the same backing pointer (a fresh \
19493                 Vec<WitContract> clone would change the pointer on \
19494                 every call)",
19495            );
19496            assert_eq!(
19497                first,
19498                contratos.as_slice(),
19499                "Caixa::contratos must return :contratos verbatim by \
19500                 borrow — got {first:?}, expected {contratos:?}",
19501            );
19502        }
19503    }
19504
19505    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
19506
19507    #[test]
19508    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
19509        // Load-bearing invariant: every multi-word top-level [`Caixa`]
19510        // serde-derived JSON key routes through a lifted `&'static str`
19511        // const. The Rust field names are `snake_case`
19512        // (`deps_dev` / `upgrade_from` / `max_restarts` /
19513        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
19514        // "camelCase")]` derive attribute maps each to the camelCase
19515        // byte-string the [`Caixa::to_lisp`] round-trip's
19516        // `serde_json::to_value(self)` step lands under before
19517        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
19518        // to the kebab-case `:deps-dev` / `:upgrade-from` /
19519        // `:max-restarts` / `:restart-window` author surface. Serialize
19520        // a fully-populated [`Caixa`] and pin that each canonical
19521        // byte-sequence appears verbatim in the JSON — a future
19522        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
19523        // verbatim-field-name flip at the derive attribute (any of
19524        // which would silently break every [`Caixa::to_lisp`]
19525        // round-trip and the future M4 operator-side manifest ingest's
19526        // `Value::get(<key>)` navigation) surfaces here as a build-time
19527        // test failure at `manifest.rs`, not as an apply-time
19528        // `.get(<stale-canonical-const>)` returning `None` far from the
19529        // derive-attr drift's commit. Same discipline the sibling
19530        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
19531        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
19532        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
19533        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
19534        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
19535        // [`UpgradeFromEntry`] per-entry axes — extended here to the
19536        // enclosing M0 [`Caixa`] top-level axis so the last of the four
19537        // multi-word top-level [`Caixa`] serde-derived JSON keys
19538        // (`depsDev`) joins the substrate's "one canonical byte-string
19539        // per typed serialized-key axis" discipline.
19540        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19541        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
19542        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19543        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
19544        c.upgrade_from = vec![UpgradeFromEntry {
19545            from: "0.0.1".into(),
19546            instructions: vec![UpgradeInstruction::Restart],
19547        }];
19548        c.estrategia = Some(RestartStrategy::OneForOne);
19549        c.max_restarts = Some(3);
19550        c.restart_window = Some("60s".into());
19551        c.children = vec![ChildSpec {
19552            caixa: "child".into(),
19553            versao: "^0.1".into(),
19554            restart: RestartPolicy::Permanent,
19555        }];
19556        let json = serde_json::to_string(&c).unwrap();
19557        for key in [
19558            crate::render::CAIXA_KEY_DEPS_DEV,
19559            crate::render::M2_KEY_UPGRADE_FROM,
19560            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19561            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19562        ] {
19563            let quoted = format!("\"{key}\"");
19564            assert!(
19565                json.contains(&quoted),
19566                "serialized Caixa must carry the lifted top-level \
19567                 multi-word byte-sequence {quoted} verbatim in the JSON \
19568                 emission (got: {json})",
19569            );
19570        }
19571    }
19572
19573    #[test]
19574    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
19575        // Cross-axis drift-detection pin: a future collapse of the four
19576        // canonical [`Caixa`] top-level multi-word byte-strings onto the
19577        // same value (e.g. an accidental copy-paste flip of
19578        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
19579        // `"upgradeFrom"`) would silently reroute every downstream
19580        // `Value::get(<key>)` probe on one axis onto the sibling axis's
19581        // top-level entry and pass every propagation-probe test that
19582        // expected only the stale axis's value. Peer of the sibling
19583        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
19584        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
19585        let all = [
19586            crate::render::CAIXA_KEY_DEPS_DEV,
19587            crate::render::M2_KEY_UPGRADE_FROM,
19588            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19589            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19590        ];
19591        for (i, a) in all.iter().enumerate() {
19592            for b in all.iter().skip(i + 1) {
19593                assert_ne!(
19594                    a, b,
19595                    "Caixa top-level multi-word key consts must be \
19596                     pairwise-distinct canonical byte-sequences — got \
19597                     `{a}` == `{b}`",
19598                );
19599            }
19600        }
19601    }
19602
19603    #[test]
19604    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
19605        // Shape-pin: every [`Caixa`] top-level multi-word key const must
19606        // be a lowerCamelCase byte-sequence (no `snake_case`
19607        // underscores, no `kebab-case` hyphens, no leading colon, no
19608        // `PascalCase` leading capital, no whitespace / dots) — the
19609        // canonical shape the `#[serde(rename_all = "camelCase")]`
19610        // derive produces on [`Caixa`]. A future flip to a
19611        // non-camelCase attribute at the derive surfaces both here
19612        // (this test fails on the stale-constant shape) and at
19613        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19614        // (that test fails on the mismatch between const and derive).
19615        // Peer with `membro_key_consts_are_lower_camel_case_shape`
19616        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
19617        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
19618        for key in [
19619            crate::render::CAIXA_KEY_DEPS_DEV,
19620            crate::render::M2_KEY_UPGRADE_FROM,
19621            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19622            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19623        ] {
19624            assert!(
19625                !key.is_empty(),
19626                "Caixa top-level multi-word key const must be non-empty \
19627                 (got {key:?})"
19628            );
19629            let first = key.chars().next().unwrap();
19630            assert!(
19631                first.is_ascii_lowercase(),
19632                "Caixa top-level multi-word key const must lead with an \
19633                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
19634            );
19635            assert!(
19636                key.chars().all(|c| c.is_ascii_alphanumeric()),
19637                "Caixa top-level multi-word key const must be \
19638                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
19639                 whitespace (got {key:?})",
19640            );
19641        }
19642    }
19643
19644    #[test]
19645    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
19646        // Scalar-value pin: the byte-string the
19647        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
19648        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
19649        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
19650        // → `depsTest` matching a hypothetical per-test-target
19651        // vocabulary flip) lands as an edit to exactly one const AND
19652        // one derive attribute — the sibling
19653        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19654        // pin already ties the const to the derive attribute, so a
19655        // rebrand that touches only one side of the pair fails at
19656        // caixa-core build time. Same "scalar-value pin per const"
19657        // discipline the sibling
19658        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
19659        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
19660        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
19661        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
19662    }
19663
19664    #[test]
19665    fn caixa_key_deps_pins_canonical_byte_string() {
19666        // Scalar-value pin: the byte-string the
19667        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
19668        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
19669        // on the two-list dep-graph serialized-key axis — the sibling
19670        // pin covers the multi-word `deps_dev → depsDev` camelCase
19671        // arm, this pin covers the single-word `deps → deps` no-op arm
19672        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
19673        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
19674        // axis and the emitted JSON key equals the source-side field
19675        // name byte-for-byte). A future [`crate::Caixa::deps`] field
19676        // rename (`deps` → `dependencies` matching Cargo's verbatim
19677        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
19678        // hypothetical per-runtime-target vocabulary flip) OR an added
19679        // `#[serde(rename = "…")]` explicit override lands as an edit
19680        // to exactly one const AND one derive-attr / field name — the
19681        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
19682        // pin ties the const to the emitted JSON key, so a rebrand
19683        // that touches only one side of the pair fails at caixa-core
19684        // build time.
19685        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
19686    }
19687
19688    #[test]
19689    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
19690        // Load-bearing invariant on the single-word `deps` top-level
19691        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
19692        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
19693        // `serde_json::to_value(self)` step emits. Serialize a
19694        // populated [`Caixa`] whose `:deps` slot carries at least one
19695        // entry (the `#[serde(default)]` attribute on the field emits
19696        // an empty `[]` even without members, but a non-empty vec
19697        // additionally covers the codec's per-`Dep`-entry emission
19698        // path) and pin that `"deps"` appears verbatim in the JSON
19699        // emission — a future accidental `rename_all = "snake_case"` /
19700        // `"kebab-case"` flip at the derive attribute (or an added
19701        // `#[serde(rename = "…")]` explicit override on the field, or
19702        // a Rust field rename) would break every [`Caixa::to_lisp`]
19703        // round-trip and the future M4 operator-side manifest ingest's
19704        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
19705        // build-time test failure at `manifest.rs`, not as an
19706        // apply-time `.get(<stale-canonical-const>)` returning `None`
19707        // far from the drift's commit. Peer of the sibling
19708        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19709        // multi-word pin on the same M0 [`Caixa`] top-level
19710        // serialized-key axis, extended here to the single-word arm
19711        // the multi-word test's `rename_all = "camelCase"` sweep can't
19712        // reach (single-word `deps → deps` is a no-op the multi-word
19713        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
19714        // `\"restartWindow\"` byte-scan can never observe).
19715        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19716        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
19717        let json = serde_json::to_string(&c).unwrap();
19718        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
19719        assert!(
19720            json.contains(&quoted),
19721            "serialized Caixa must carry the lifted top-level `deps` \
19722             byte-sequence {quoted} verbatim in the JSON emission (got: \
19723             {json})",
19724        );
19725    }
19726
19727    #[test]
19728    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
19729        // Cross-axis drift-detection pin on the two-list dep-graph
19730        // renderer-side wire-key axis: a future collapse of the
19731        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
19732        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
19733        // same value (e.g. an accidental copy-paste flip of
19734        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
19735        // reroute every downstream `Value::get(<key>)` probe on one
19736        // axis onto the sibling axis's dep-list and pass every
19737        // propagation-probe test that expected only the stale axis's
19738        // value — a dev-only dep would land in the runtime closure at
19739        // publish time, or a runtime dep would be excluded from the
19740        // published lacre. Peer of the sibling four-way distinct pin
19741        // on the top-level multi-word tetrad
19742        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
19743        // and the two-way pin on the sibling
19744        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
19745        // author-facing arm (4da6fba's test), extended here to the
19746        // renderer-side wire-key arm of the same two-list dep-graph
19747        // axis so both halves of the "one canonical byte-string per
19748        // typed axis per (author, wire)" grid carry the same
19749        // distinct-ness discipline.
19750        assert_ne!(
19751            crate::render::CAIXA_KEY_DEPS,
19752            crate::render::CAIXA_KEY_DEPS_DEV,
19753            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
19754             canonical byte-sequences on the two-list dep-graph \
19755             renderer-side wire-key axis"
19756        );
19757    }
19758
19759    // ── DepList / Caixa::push_dep pin ────────────────────────────────
19760    //
19761    // The compounding pin: the two-arm closed-set typed enum
19762    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
19763    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
19764    // consumer of the top-level manifest's dep-mutation surface reads
19765    // through, and the typed dispatch [`Caixa::push_dep`] on the
19766    // substrate primitive folds the "select list → check within-list
19767    // dup → push" cascade onto one method call. Prior to this landing
19768    // the two axes lived across two `&'static str` constants
19769    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
19770    // set type carrying the pair; the `feira add` mutation site's
19771    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
19772    // caixa.deps }` dispatch expressed no compile-time link back to
19773    // the substrate primitive, and a future third dep-list axis would
19774    // have silently split at every open-coded mutation site.
19775
19776    #[test]
19777    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
19778        // Every arm returns the same `&'static str` the substrate's
19779        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
19780        // constants carry. A future rebrand on either constant reaches
19781        // the enum through one edit; a regression to inline literals
19782        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
19783        // quotes from the wire-format constants every consumer routes
19784        // through and this pin flags it at build time.
19785        assert_eq!(
19786            crate::dep::DepList::Prod.as_str(),
19787            crate::render::DEP_AUTHOR_KEY_DEPS
19788        );
19789        assert_eq!(
19790            crate::dep::DepList::Dev.as_str(),
19791            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
19792        );
19793    }
19794
19795    #[test]
19796    fn dep_list_display_routes_through_as_str() {
19797        // Same as-str-through-Display convergence discipline the
19798        // sibling closed-set typed enums carry — a `format!("{list}")`
19799        // call must land byte-for-byte on the accessor's return so a
19800        // future consumer that formats the enum for a diagnostic line
19801        // reaches the same wire-format constant the wire-format
19802        // producers do.
19803        assert_eq!(
19804            format!("{}", crate::dep::DepList::Prod),
19805            crate::dep::DepList::Prod.as_str()
19806        );
19807        assert_eq!(
19808            format!("{}", crate::dep::DepList::Dev),
19809            crate::dep::DepList::Dev.as_str()
19810        );
19811    }
19812
19813    #[test]
19814    fn dep_list_all_enumerates_every_variant_once() {
19815        // Exhaustive-iteration pin — every arm appears exactly once in
19816        // `ALL`, matching the closed set the compiler enforces on the
19817        // sibling `match self` arms. A future variant addition that
19818        // extends only one method's match without extending `ALL`
19819        // would silently drop the new arm from every consumer that
19820        // iterates the slice.
19821        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
19822        assert!(variants.contains(&crate::dep::DepList::Prod));
19823        assert!(variants.contains(&crate::dep::DepList::Dev));
19824        assert_eq!(variants.len(), 2);
19825    }
19826
19827    #[test]
19828    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
19829        // Reverse projection on the two-list dep-graph axis: the
19830        // author-surface wire tag the sibling `as_str` emitter walks
19831        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
19832        // `Some(DepList::Prod)`. A regression that hand-rolled the
19833        // per-arm match without routing through the lifted
19834        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
19835        // future wire-tag rebrand and this pin flags it at build time.
19836        assert_eq!(
19837            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
19838            Some(crate::dep::DepList::Prod)
19839        );
19840    }
19841
19842    #[test]
19843    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
19844        // Peer of the `Prod`-arm pin on the dev-only axis: the
19845        // author-surface wire tag the sibling `as_str` emitter walks
19846        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
19847        // back to `Some(DepList::Dev)`. Same drift-detection posture
19848        // as the peer arm — the sibling method `match` arms are
19849        // compiler-checked exhaustive so a future variant addition
19850        // trips at build time.
19851        assert_eq!(
19852            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
19853            Some(crate::dep::DepList::Dev)
19854        );
19855    }
19856
19857    #[test]
19858    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
19859        // Every input outside the closed-set arm-string set the
19860        // sibling `as_str` emitter walks lands on the terminal `None`
19861        // fallback — no silent-accept surface. Sweeps a set of
19862        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
19863        // rebrand candidates, foreign wire tags, empty string) so a
19864        // future variant addition that widened one wire form without
19865        // extending the emitter's arm-set would trip the sibling
19866        // round-trip pin below rather than silently accepting the new
19867        // form here.
19868        for candidate in [
19869            "",
19870            "deps",
19871            "deps-dev",
19872            ":deps ",
19873            ":Deps",
19874            ":DEPS",
19875            ":build-dep",
19876            ":tool-dep",
19877            "prod",
19878            "dev",
19879        ] {
19880            assert_eq!(
19881                crate::dep::DepList::from_wire(candidate),
19882                None,
19883                "from_wire({candidate:?}) must return None; every input outside \
19884                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
19885                 the sibling as_str emitter walks lands on the terminal fallback",
19886            );
19887        }
19888    }
19889
19890    #[test]
19891    fn dep_list_round_trips_through_as_str_and_from_wire() {
19892        // Load-bearing round-trip pin: every arm the `ALL` iteration
19893        // exposes survives the `as_str` → `from_wire` composition
19894        // byte-for-byte. Same discipline the sibling closed-set enums
19895        // carry — `CaixaKind` /
19896        // `RestartStrategy` / `RestartPolicy` /
19897        // `PlacementStrategy` — extended onto the two-list dep-graph
19898        // axis. A future variant addition that extends `ALL` +
19899        // `as_str` without extending `from_wire` (or vice versa)
19900        // trips at build time on this iteration because the compiler
19901        // enforces exhaustiveness on the sibling `match self` arms.
19902        for &list in crate::dep::DepList::ALL {
19903            assert_eq!(
19904                crate::dep::DepList::from_wire(list.as_str()),
19905                Some(list),
19906                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
19907                 a silent split between the forward emitter and the reverse parser \
19908                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
19909            );
19910        }
19911    }
19912
19913    #[test]
19914    fn push_dep_routes_to_deps_slot_on_prod_arm() {
19915        // The `Prod` arm dispatches to the runtime-closure `:deps`
19916        // slot every downstream lacre-pipeline consumer resolves at
19917        // build time. A future arm that regressed to inline `&mut
19918        // self.deps_dev` on the `Prod` path would silently reroute
19919        // every runtime dep into the dev-only closure at publish time
19920        // — this pin refuses that regression.
19921        let src = Caixa::template("host");
19922        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19923        let before_deps = caixa.deps().len();
19924        let before_deps_dev = caixa.deps_dev().len();
19925        let dep = Dep {
19926            nome: "caixa-teia".to_string(),
19927            versao: "^0.1".to_string(),
19928            fonte: None,
19929            opcional: false,
19930            caracteristicas: Vec::new(),
19931        };
19932        caixa
19933            .push_dep(crate::dep::DepList::Prod, dep)
19934            .expect("first push into :deps succeeds");
19935        assert_eq!(caixa.deps().len(), before_deps + 1);
19936        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
19937        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
19938    }
19939
19940    #[test]
19941    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
19942        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
19943        // must dispatch to the dev-only-closure `:deps-dev` slot every
19944        // downstream test-facing artifact resolver reads. A future
19945        // regression that inverted the two arms would silently route
19946        // every dev-only dep into the runtime closure at publish time
19947        // and this pin catches it before the drift ships.
19948        let src = Caixa::template("host");
19949        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19950        let dep = Dep {
19951            nome: "tatara-check".to_string(),
19952            versao: "*".to_string(),
19953            fonte: None,
19954            opcional: false,
19955            caracteristicas: Vec::new(),
19956        };
19957        caixa
19958            .push_dep(crate::dep::DepList::Dev, dep)
19959            .expect("first push into :deps-dev succeeds");
19960        assert!(caixa.deps().is_empty());
19961        assert_eq!(caixa.deps_dev().len(), 1);
19962        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
19963    }
19964
19965    #[test]
19966    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
19967        // Within-list dup check routes through the canonical
19968        // [`DepError::DuplicateNome`] carrier — the substrate's typed
19969        // diagnostic for the same axis [`Caixa::validate_deps`]'s
19970        // parse-time [`crate::render::insert_first_seen`] walk raises
19971        // on. Prior to the lift the mutation site's inline
19972        // `bail!("dep '{}' already declared", …)` string-diagnostic
19973        // path expressed no through-line back to the typed error;
19974        // routing every dep-list refusal through one carrier means an
19975        // author reading a `feira add` refusal and a `feira build`
19976        // refusal reaches for the same corrective surface without
19977        // switching diagnostic idioms.
19978        let src = Caixa::template("host");
19979        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
19980        let dep = Dep {
19981            nome: "caixa-teia".to_string(),
19982            versao: "^0.1".to_string(),
19983            fonte: None,
19984            opcional: false,
19985            caracteristicas: Vec::new(),
19986        };
19987        caixa
19988            .push_dep(crate::dep::DepList::Prod, dep.clone())
19989            .expect("first push succeeds");
19990        let dup = Dep {
19991            nome: "caixa-teia".to_string(),
19992            versao: "^0.2".to_string(),
19993            fonte: None,
19994            opcional: false,
19995            caracteristicas: Vec::new(),
19996        };
19997        let err = caixa
19998            .push_dep(crate::dep::DepList::Prod, dup)
19999            .expect_err("second push with same :nome refuses");
20000        assert_eq!(
20001            err,
20002            DepError::DuplicateNome {
20003                nome: "caixa-teia".to_string(),
20004                list: crate::render::DEP_AUTHOR_KEY_DEPS,
20005            }
20006        );
20007        // The refused mutation must not corrupt the target list —
20008        // exactly one entry lives past the refusal, matching the
20009        // canonical single-source-of-truth invariant `Caixa::deps()`
20010        // carries.
20011        assert_eq!(caixa.deps().len(), 1);
20012    }
20013
20014    #[test]
20015    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
20016        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
20017        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
20018        // `list` payload so a future author reading the refusal grep's
20019        // for the correct `:deps-dev` block in their `caixa.lisp`,
20020        // not the sibling `:deps` block the runtime closure resolves.
20021        let src = Caixa::template("host");
20022        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20023        let dep = Dep {
20024            nome: "tatara-check".to_string(),
20025            versao: "*".to_string(),
20026            fonte: None,
20027            opcional: false,
20028            caracteristicas: Vec::new(),
20029        };
20030        caixa
20031            .push_dep(crate::dep::DepList::Dev, dep.clone())
20032            .expect("first push succeeds");
20033        let err = caixa
20034            .push_dep(crate::dep::DepList::Dev, dep)
20035            .expect_err("second push with same :nome refuses");
20036        assert!(matches!(
20037            err,
20038            DepError::DuplicateNome {
20039                ref nome,
20040                list,
20041            } if nome == "tatara-check"
20042                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20043        ));
20044    }
20045
20046    #[test]
20047    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
20048        // The within-list dup check is scoped to the target arm — a
20049        // caixa may legitimately carry the same `:nome` under both
20050        // `:deps` and `:deps-dev` (though the substrate's peer
20051        // [`crate::Caixa::validate_deps`] walk still refuses the
20052        // shape at parse time; the mutation-site refusal is scoped to
20053        // the mutation-site's list to match the peer parse-time
20054        // per-list [`crate::render::insert_first_seen`] discipline).
20055        // The two arms hold independent seen-sets.
20056        let src = Caixa::template("host");
20057        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20058        let dep_prod = Dep {
20059            nome: "shared".to_string(),
20060            versao: "^0.1".to_string(),
20061            fonte: None,
20062            opcional: false,
20063            caracteristicas: Vec::new(),
20064        };
20065        let dep_dev = Dep {
20066            nome: "shared".to_string(),
20067            versao: "*".to_string(),
20068            fonte: None,
20069            opcional: false,
20070            caracteristicas: Vec::new(),
20071        };
20072        caixa
20073            .push_dep(crate::dep::DepList::Prod, dep_prod)
20074            .expect("push into :deps succeeds");
20075        caixa
20076            .push_dep(crate::dep::DepList::Dev, dep_dev)
20077            .expect("push same :nome into :deps-dev succeeds");
20078        assert_eq!(caixa.deps().len(), 1);
20079        assert_eq!(caixa.deps_dev().len(), 1);
20080    }
20081
20082    #[test]
20083    fn deps_of_prod_returns_the_deps_slot_verbatim() {
20084        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
20085        // accessor must project onto the runtime-closure `:deps` slot —
20086        // element-equal and length-equal to the sibling per-slot
20087        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
20088        // A future arm that regressed to `self.deps_dev()` on the `Prod`
20089        // path would silently reroute every downstream typed-dispatch
20090        // walker (the [`Caixa::validate_deps`] per-list
20091        // [`crate::render::insert_first_seen`] dedup walk, any future
20092        // per-axis-parametrised consumer) into the sibling dev-only
20093        // closure and this pin refuses that regression.
20094        let src = Caixa::template("host");
20095        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20096        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20097        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
20098        let dep = Dep {
20099            nome: "caixa-teia".to_string(),
20100            versao: "^0.1".to_string(),
20101            fonte: None,
20102            opcional: false,
20103            caracteristicas: Vec::new(),
20104        };
20105        caixa
20106            .push_dep(crate::dep::DepList::Prod, dep.clone())
20107            .expect("push into :deps succeeds");
20108        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20109        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
20110        assert_eq!(
20111            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
20112            "caixa-teia"
20113        );
20114    }
20115
20116    #[test]
20117    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
20118        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
20119        // [`Caixa::deps_of`] must project onto the dev-only-closure
20120        // `:deps-dev` slot, element-equal and length-equal to the
20121        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
20122        // future regression that inverted the two arms would silently
20123        // route every dev-list walker onto the runtime closure and this
20124        // pin catches it before the drift ships.
20125        let src = Caixa::template("host");
20126        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20127        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20128        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
20129        let dep = Dep {
20130            nome: "tatara-check".to_string(),
20131            versao: "*".to_string(),
20132            fonte: None,
20133            opcional: false,
20134            caracteristicas: Vec::new(),
20135        };
20136        caixa
20137            .push_dep(crate::dep::DepList::Dev, dep)
20138            .expect("push into :deps-dev succeeds");
20139        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20140        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
20141        assert_eq!(
20142            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
20143            "tatara-check"
20144        );
20145    }
20146
20147    #[test]
20148    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
20149        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
20150        // [`Caixa::deps_of`] must land on the same two-slot partition the
20151        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
20152        // expose — the canonical dispatch a future per-axis-parametrised
20153        // walker (a future `feira app graph` per-list dep summary, a
20154        // future M4 per-cluster dev-closure-audit overlay the CR
20155        // materializer resolves per-CR) reads through. Prior to the
20156        // lift the two-block iteration lived open-coded at every walker,
20157        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
20158        // §I) would have had to grow a third block at every consumer.
20159        // A regression that dropped the `Dev` arm from `ALL` would flip
20160        // the collected pairs to `[(":deps", &[])]` alone and this pin
20161        // refuses that shape.
20162        let src = Caixa::template("host");
20163        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20164        let prod_dep = Dep {
20165            nome: "caixa-teia".to_string(),
20166            versao: "^0.1".to_string(),
20167            fonte: None,
20168            opcional: false,
20169            caracteristicas: Vec::new(),
20170        };
20171        let dev_dep = Dep {
20172            nome: "tatara-check".to_string(),
20173            versao: "*".to_string(),
20174            fonte: None,
20175            opcional: false,
20176            caracteristicas: Vec::new(),
20177        };
20178        caixa
20179            .push_dep(crate::dep::DepList::Prod, prod_dep)
20180            .expect("push into :deps succeeds");
20181        caixa
20182            .push_dep(crate::dep::DepList::Dev, dev_dep)
20183            .expect("push into :deps-dev succeeds");
20184        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
20185            .iter()
20186            .map(|&list| {
20187                let slice = caixa.deps_of(list);
20188                (list.as_str(), slice.len(), slice[0].nome())
20189            })
20190            .collect();
20191        assert_eq!(
20192            collected,
20193            vec![
20194                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
20195                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
20196            ]
20197        );
20198    }
20199
20200    #[test]
20201    fn caixa_deps_of_is_const_fn() {
20202        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
20203        // `const`-eval-surface posture. The typed-dispatch read
20204        // accessor forwards through the sibling `pub const fn`
20205        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
20206        // accessors on the two [`crate::dep::DepList`] enum arms —
20207        // every operator in the body is already `const`-callable
20208        // (`DepList` is a plain `#[derive(Copy)]` closed-set
20209        // discriminator so the `match` arms are const-evaluable, and
20210        // each arm dispatches through the sibling `pub const fn`
20211        // slice accessor). Any future accidental downgrade to
20212        // non-`const` fails the `deps_of_via_const_fn` wrapper below
20213        // at caixa-core build time with E0015 (`cannot call non-const
20214        // method`), strictly stronger than a runtime `assert!` and
20215        // side-stepping the destructor-in-const restriction the
20216        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
20217        // rule out on the direct-`const _: () = assert!(...)`
20218        // residence.
20219        //
20220        // Peer of the sibling outer-`Caixa` accessor family pins
20221        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
20222        // on the `&[String]` universal-axis surface,
20223        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
20224        // on the outer `&[T]` composite-slice surface,
20225        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
20226        // on the outer `Option<&Composite>` surface) — this pin
20227        // extends the `const`-eval-surface discipline onto the outer-
20228        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
20229        // dep-list axis, closing the outer-`Caixa` accessor family's
20230        // last unlifted `pub fn` on the read side.
20231        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
20232            c.deps_of(list)
20233        }
20234        let src = Caixa::template("host");
20235        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20236        // Empty-list arm: both `Prod` and `Dev` degenerate to the
20237        // empty slice with no silent `None` collapse — the
20238        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
20239        // that omits the slot lands on.
20240        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
20241        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
20242        assert_eq!(
20243            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20244            caixa.deps()
20245        );
20246        assert_eq!(
20247            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20248            caixa.deps_dev()
20249        );
20250        // Populated arms: each list carries its own entry, and the
20251        // wrapper / direct dispatches agree byte-for-byte on the
20252        // slice-view under both non-empty arms.
20253        let prod_dep = Dep {
20254            nome: "caixa-teia".to_string(),
20255            versao: "^0.1".to_string(),
20256            fonte: None,
20257            opcional: false,
20258            caracteristicas: Vec::new(),
20259        };
20260        let dev_dep = Dep {
20261            nome: "tatara-check".to_string(),
20262            versao: "*".to_string(),
20263            fonte: None,
20264            opcional: false,
20265            caracteristicas: Vec::new(),
20266        };
20267        caixa
20268            .push_dep(crate::dep::DepList::Prod, prod_dep)
20269            .expect("push into :deps succeeds");
20270        caixa
20271            .push_dep(crate::dep::DepList::Dev, dev_dep)
20272            .expect("push into :deps-dev succeeds");
20273        assert_eq!(
20274            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20275            caixa.deps()
20276        );
20277        assert_eq!(
20278            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20279            caixa.deps_dev()
20280        );
20281        assert_eq!(
20282            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
20283            "caixa-teia"
20284        );
20285        assert_eq!(
20286            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
20287            "tatara-check"
20288        );
20289    }
20290
20291    #[test]
20292    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
20293        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
20294        // must route its per-list [`crate::render::insert_first_seen`]
20295        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
20296        // rather than the pre-lift open-coded two-block iteration over
20297        // `self.deps()` + `self.deps_dev()`. A regression that dropped
20298        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
20299        // stop refusing within-list dups on the sibling arm; a
20300        // regression that flipped the arm-to-list-key mapping
20301        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
20302        // diagnostic surface. Both drifts surface here through a paired
20303        // duplicate-name refusal per arm plus an offending-list-key
20304        // check on the emitted [`DepError::DuplicateNome`] carrier.
20305        for &list in crate::dep::DepList::ALL {
20306            let src = Caixa::template("host");
20307            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20308            let dup = Dep {
20309                nome: "twin".to_string(),
20310                versao: "^0.1".to_string(),
20311                fonte: None,
20312                opcional: false,
20313                caracteristicas: Vec::new(),
20314            };
20315            match list {
20316                crate::dep::DepList::Prod => {
20317                    caixa.deps.push(dup.clone());
20318                    caixa.deps.push(dup);
20319                }
20320                crate::dep::DepList::Dev => {
20321                    caixa.deps_dev.push(dup.clone());
20322                    caixa.deps_dev.push(dup);
20323                }
20324            }
20325            let err = caixa
20326                .validate_deps()
20327                .expect_err("within-list duplicate :nome must refuse");
20328            assert_eq!(
20329                err,
20330                DepError::DuplicateNome {
20331                    nome: "twin".to_string(),
20332                    list: list.as_str(),
20333                },
20334                "validate_deps on {list} arm must emit \
20335                 DepError::DuplicateNome carrying the arm's own \
20336                 as_str() diagnostic — the arm-to-list-key mapping \
20337                 flowed through DepList::ALL + Caixa::deps_of"
20338            );
20339        }
20340    }
20341
20342    #[test]
20343    fn caixa_licenca_default_pins_canonical_mit_byte() {
20344        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
20345        // canonical SPDX-`"MIT"` byte today, the same license expression
20346        // every peer substrate-side consumer of the author-omitted
20347        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
20348        // `caixa-helm/src/lib.rs`, the future M4
20349        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
20350        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
20351        // crate's [`Caixa::validate_licenca`] docstring roadmap already
20352        // names as the second consumer) fills into its per-consumer
20353        // README/annotation emit site. Pin the literal here (peer with the
20354        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
20355        // [`crate::version::DEFAULT_GIT_REMOTE`] /
20356        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
20357        // on the sibling lifted-constant surfaces) so a future
20358        // substrate-side license-fallback rebrand surfaces here as a
20359        // coordinated edit-point: the sibling caixa-helm
20360        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
20361        // pinning test already pins the equality at the renderer-emit
20362        // axis; this pin closes the second coordinate of the pair by
20363        // anchoring the lifted constant's current byte to the canonical
20364        // CAIXA-SDLC §I license scaffold's documented shape.
20365        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
20366    }
20367
20368    // ── Caixa::validate_upgrade_from — compound per-Caixa entry gate on ──
20369    // ── the M2 `:upgrade-from` slot: folds the three top-level        ──
20370    // ── `crate::upgrade` validators (per-entry + cross-entry           ──
20371    // ── duplicate-`:from`, cross-slot `:from < :versao` precedence,   ──
20372    // ── cross-slot `:state-change` ↔ `:on-state-change` composition)  ──
20373    // ── onto one substrate primitive. Byte-for-byte equivalent to the ──
20374    // ── pre-fold three-block cascade at                               ──
20375    // ── `crate::layout::StandardLayout::verify` under the same        ──
20376    // ── canonical dispatch order.                                     ──
20377
20378    #[test]
20379    fn validate_upgrade_from_folds_per_entry_arm_matches_gate() {
20380        // Fail-before-pass-after per-arm equivalence pin on the
20381        // per-entry + cross-entry axis: a fixture whose `:upgrade-from`
20382        // carries a per-entry-invalid `:from` (git-tag shape `"v0.1.0"`,
20383        // which `semver::Version::parse` rejects) surfaces the same
20384        // [`crate::UpgradeError`] through the compound gate
20385        // [`Caixa::validate_upgrade_from`] and the standalone per-entry
20386        // gate [`crate::upgrade::validate_upgrade_from`] on the same
20387        // [`Caixa::upgrade_from`] slice. Pins the fold — a silent
20388        // regression that de-folded the per-entry arm would surface here
20389        // as a mismatch between the two dispatches. Sibling in shape to
20390        // the peer per-slot-≡-standalone equivalence pins the
20391        // [`crate::AplicacaoSpec::validate_contratos`] /
20392        // [`crate::MeshPolicy::validate`] /
20393        // [`crate::SupervisorSpec::validate_children`] compound gates
20394        // each carry on their axes.
20395        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20396        c.upgrade_from = vec![crate::UpgradeFromEntry {
20397            from: "v0.1.0".into(),
20398            instructions: vec![crate::UpgradeInstruction::Restart],
20399        }];
20400        let via_method = c.validate_upgrade_from().unwrap_err();
20401        let via_standalone = crate::upgrade::validate_upgrade_from(c.upgrade_from()).unwrap_err();
20402        assert_eq!(
20403            via_method, via_standalone,
20404            "Caixa::validate_upgrade_from must surface the per-entry \
20405             axis's diagnostic byte-equal to the standalone \
20406             `crate::upgrade::validate_upgrade_from` on the same \
20407             upgrade_from() slice"
20408        );
20409        assert!(
20410            matches!(
20411                via_method,
20412                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.1.0"
20413            ),
20414            "expected FromInvalid on the git-tag-shape `:from`, got {via_method:?}"
20415        );
20416    }
20417
20418    #[test]
20419    fn validate_upgrade_from_folds_versao_arm_matches_gate() {
20420        // Per-arm equivalence pin on the cross-slot `:from ↔ :versao`
20421        // precedence axis: a fixture with a well-formed `:from` (so the
20422        // per-entry arm passes) whose parsed semver is >= the caixa's
20423        // `:versao` under SemVer-2 precedence surfaces the same
20424        // [`crate::UpgradeError::FromNotBeforeVersao`] through both the
20425        // compound gate and the standalone
20426        // [`crate::upgrade::validate_upgrade_from_against_versao`] gate
20427        // keyed off the same `(upgrade_from, versao)` pair. Pins the
20428        // fold's second arm — reaching this arm through the compound
20429        // gate requires the per-entry arm to pass first, which itself
20430        // pins the per-arm cross-arm ordering.
20431        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20432        c.versao = "0.1.0".into();
20433        c.upgrade_from = vec![crate::UpgradeFromEntry {
20434            from: "0.2.0".into(),
20435            instructions: vec![crate::UpgradeInstruction::Restart],
20436        }];
20437        let via_method = c.validate_upgrade_from().unwrap_err();
20438        let via_standalone =
20439            crate::upgrade::validate_upgrade_from_against_versao(c.upgrade_from(), c.versao())
20440                .unwrap_err();
20441        assert_eq!(
20442            via_method, via_standalone,
20443            "Caixa::validate_upgrade_from must surface the \
20444             `:from >= :versao` diagnostic byte-equal to the standalone \
20445             `crate::upgrade::validate_upgrade_from_against_versao` on \
20446             the same (upgrade_from, versao) pair"
20447        );
20448        assert!(
20449            matches!(
20450                via_method,
20451                crate::UpgradeError::FromNotBeforeVersao { ref from, ref versao }
20452                    if from == "0.2.0" && versao == "0.1.0"
20453            ),
20454            "expected FromNotBeforeVersao carrying the offending pair, got {via_method:?}"
20455        );
20456    }
20457
20458    #[test]
20459    fn validate_upgrade_from_folds_behavior_arm_matches_gate() {
20460        // Per-arm equivalence pin on the cross-slot `:state-change ↔
20461        // :on-state-change` composition axis: a fixture with a
20462        // well-formed `:from` strictly less than `:versao` (so the
20463        // per-entry and versao arms both pass) whose `:instructions`
20464        // list carries a `(:state-change …)` instruction with no
20465        // `:behavior :on-state-change` callback declared surfaces the
20466        // same [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
20467        // through both the compound gate and the standalone
20468        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
20469        // gate keyed off the same `(upgrade_from, behavior)` pair.
20470        // Reaching this arm through the compound gate requires both
20471        // prior arms to pass first — the ordering pin below pins the
20472        // per-arm dispatch order explicitly.
20473        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20474        c.versao = "0.2.0".into();
20475        c.behavior = None;
20476        c.upgrade_from = vec![crate::UpgradeFromEntry {
20477            from: "0.1.0".into(),
20478            instructions: vec![
20479                crate::UpgradeInstruction::LoadModule {
20480                    module: "demo".into(),
20481                },
20482                crate::UpgradeInstruction::StateChange {
20483                    script: std::path::PathBuf::from("lib/m.lisp"),
20484                },
20485                crate::UpgradeInstruction::SoftPurge {
20486                    module: "demo-old".into(),
20487                },
20488            ],
20489        }];
20490        let via_method = c.validate_upgrade_from().unwrap_err();
20491        let via_standalone =
20492            crate::upgrade::validate_upgrade_from_against_behavior(c.upgrade_from(), c.behavior())
20493                .unwrap_err();
20494        assert_eq!(
20495            via_method, via_standalone,
20496            "Caixa::validate_upgrade_from must surface the \
20497             `:state-change` ↔ `:on-state-change` composition \
20498             diagnostic byte-equal to the standalone \
20499             `crate::upgrade::validate_upgrade_from_against_behavior` \
20500             on the same (upgrade_from, behavior) pair"
20501        );
20502        assert!(
20503            matches!(
20504                via_method,
20505                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback {
20506                    ref from,
20507                    ref script,
20508                } if from == "0.1.0" && script == &std::path::PathBuf::from("lib/m.lisp")
20509            ),
20510            "expected StateChangeWithoutOnStateChangeCallback carrying \
20511             the offending (from, script) pair, got {via_method:?}"
20512        );
20513    }
20514
20515    #[test]
20516    fn validate_upgrade_from_per_entry_arm_fires_before_versao_arm() {
20517        // Cross-arm ordering pin between the first two arms of the
20518        // fold: a fixture carrying BOTH a per-entry-invalid `:from`
20519        // (`"v0.0.5"` — git-tag shape rejected by
20520        // [`crate::upgrade::validate_upgrade_from`]) AND a would-be
20521        // versao-precedence violation on a second entry (`"0.2.0" >=
20522        // :versao "0.1.0"`) surfaces the per-entry diagnostic first
20523        // through the compound gate. Sanity assertion: the second
20524        // entry alone under the same `:versao` trips the versao arm
20525        // on its own via the standalone
20526        // [`crate::upgrade::validate_upgrade_from_against_versao`], so
20527        // the per-entry-first surfacing is a real ordering property,
20528        // not a case where the versao arm silently accepts the
20529        // fixture. Pins the pre-fold layout wire-up's canonical
20530        // dispatch order (per-entry → versao → behavior) as a
20531        // property of the substrate primitive rather than a
20532        // convention of the layout call site.
20533        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20534        c.versao = "0.1.0".into();
20535        c.upgrade_from = vec![
20536            crate::UpgradeFromEntry {
20537                from: "v0.0.5".into(),
20538                instructions: vec![crate::UpgradeInstruction::Restart],
20539            },
20540            crate::UpgradeFromEntry {
20541                from: "0.2.0".into(),
20542                instructions: vec![crate::UpgradeInstruction::Restart],
20543            },
20544        ];
20545        let err = c.validate_upgrade_from().unwrap_err();
20546        assert!(
20547            matches!(
20548                err,
20549                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.0.5"
20550            ),
20551            "per-entry arm must fire before versao arm — expected \
20552             FromInvalid on `v0.0.5`, got {err:?}"
20553        );
20554        // Sanity: the versao-violating second entry alone under the
20555        // same `:versao` trips the versao arm on its own — proves the
20556        // per-entry-first surfacing above is a real ordering property.
20557        let sanity = crate::upgrade::validate_upgrade_from_against_versao(
20558            &[crate::UpgradeFromEntry {
20559                from: "0.2.0".into(),
20560                instructions: vec![crate::UpgradeInstruction::Restart],
20561            }],
20562            "0.1.0",
20563        )
20564        .unwrap_err();
20565        assert!(
20566            matches!(sanity, crate::UpgradeError::FromNotBeforeVersao { .. }),
20567            "sanity: the versao-violating fixture alone must trip the \
20568             versao arm — got {sanity:?}"
20569        );
20570    }
20571
20572    #[test]
20573    fn validate_upgrade_from_versao_arm_fires_before_behavior_arm() {
20574        // Cross-arm ordering pin between the second and third arms of
20575        // the fold: a fixture carrying BOTH a versao-precedence
20576        // violation (`:from "0.2.0" >= :versao "0.1.0"`) AND a
20577        // would-be missing-callback violation (a `(:state-change …)`
20578        // instruction with no `:behavior :on-state-change`) surfaces
20579        // the versao diagnostic first through the compound gate.
20580        // Sanity assertion: the missing-callback fixture alone (with
20581        // the versao-precedence violation removed by bumping
20582        // `:versao` past `:from`) trips the behavior arm on its own
20583        // via the standalone
20584        // [`crate::upgrade::validate_upgrade_from_against_behavior`],
20585        // so the versao-first surfacing is a real ordering property,
20586        // not a case where the behavior arm silently accepts the
20587        // fixture.
20588        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20589        c.versao = "0.1.0".into();
20590        c.behavior = None;
20591        c.upgrade_from = vec![crate::UpgradeFromEntry {
20592            from: "0.2.0".into(),
20593            instructions: vec![
20594                crate::UpgradeInstruction::LoadModule {
20595                    module: "demo".into(),
20596                },
20597                crate::UpgradeInstruction::StateChange {
20598                    script: std::path::PathBuf::from("lib/m.lisp"),
20599                },
20600            ],
20601        }];
20602        let err = c.validate_upgrade_from().unwrap_err();
20603        assert!(
20604            matches!(
20605                err,
20606                crate::UpgradeError::FromNotBeforeVersao { ref from, .. } if from == "0.2.0"
20607            ),
20608            "versao arm must fire before behavior arm — expected \
20609             FromNotBeforeVersao on `0.2.0`, got {err:?}"
20610        );
20611        // Sanity: the same instructions under a `:versao` that
20612        // accepts the `:from` (so the versao arm passes) trips the
20613        // behavior arm — proves the versao-first surfacing above is a
20614        // real ordering property.
20615        let sanity = crate::upgrade::validate_upgrade_from_against_behavior(
20616            &[crate::UpgradeFromEntry {
20617                from: "0.2.0".into(),
20618                instructions: vec![
20619                    crate::UpgradeInstruction::LoadModule {
20620                        module: "demo".into(),
20621                    },
20622                    crate::UpgradeInstruction::StateChange {
20623                        script: std::path::PathBuf::from("lib/m.lisp"),
20624                    },
20625                ],
20626            }],
20627            None,
20628        )
20629        .unwrap_err();
20630        assert!(
20631            matches!(
20632                sanity,
20633                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback { .. }
20634            ),
20635            "sanity: the missing-callback fixture alone must trip the \
20636             behavior arm — got {sanity:?}"
20637        );
20638    }
20639
20640    #[test]
20641    fn validate_upgrade_from_accepts_clean_fixture() {
20642        // Positive control: a well-formed `:upgrade-from` (single entry
20643        // with `:from` strictly less than `:versao`, no
20644        // `:state-change` instruction so the behavior arm is vacuous)
20645        // passes the compound gate cleanly. A future tightening of any
20646        // one arm's accepted set surfaces here as a test failure
20647        // first. Mirrors the peer `validate_versao_accepts_canonical_forms`
20648        // positive-control posture on the sibling per-Caixa gate.
20649        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20650        c.versao = "0.2.0".into();
20651        c.upgrade_from = vec![crate::UpgradeFromEntry {
20652            from: "0.1.0".into(),
20653            instructions: vec![crate::UpgradeInstruction::Restart],
20654        }];
20655        c.validate_upgrade_from()
20656            .expect("clean fixture must pass the compound `:upgrade-from` gate");
20657    }
20658
20659    #[test]
20660    fn validate_upgrade_from_accepts_empty_upgrade_from() {
20661        // Positive control on the empty-list arm: a caixa without any
20662        // `:upgrade-from` block (the default `Vec::new()`
20663        // `#[serde(default)]` folds an omitted slot onto) passes the
20664        // compound gate cleanly regardless of `:versao` or `:behavior`
20665        // — each of the three standalone validators is vacuous on the
20666        // empty entry list. Pins the identity element of the fold on
20667        // the empty-slot side.
20668        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20669        assert!(
20670            c.upgrade_from().is_empty(),
20671            "template caixa must carry an empty :upgrade-from — got {:?}",
20672            c.upgrade_from()
20673        );
20674        c.validate_upgrade_from()
20675            .expect("empty :upgrade-from must pass the compound gate cleanly");
20676    }
20677
20678    // ── Caixa::validate_limits — compound per-Caixa entry gate on   ──
20679    // ── the M2 `:limits` slot: folds the                            ──
20680    // ── [`crate::LimitsSpec::validate`] four-axis cascade on the    ──
20681    // ── present-slot arm and the `Option::None` identity element on ──
20682    // ── the absent-slot arm onto one substrate primitive.           ──
20683    // ── Byte-for-byte equivalent to the pre-fold                    ──
20684    // ── `if let Some(l) = caixa.limits() { l.validate() }`          ──
20685    // ── unwrap-and-dispatch pattern at                              ──
20686    // ── `crate::layout::StandardLayout::verify` (`layout.rs`).      ──
20687
20688    #[test]
20689    fn validate_limits_folds_arm_matches_gate() {
20690        // Fail-before-pass-after per-arm equivalence pin on the
20691        // present-slot arm: a fixture whose `:limits` carries a
20692        // zero-floor-violating `:fuel` (`Some(0)`, which
20693        // [`crate::LimitsSpec::validate`] rejects through
20694        // [`crate::LimitsError::FuelZero`]) surfaces the same
20695        // [`crate::LimitsError`] byte-equal through both the compound
20696        // gate [`Caixa::validate_limits`] and the standalone
20697        // [`crate::LimitsSpec::validate`] gate on the same `LimitsSpec`
20698        // value. Pins the fold — a silent regression that de-folded
20699        // the present-slot arm would surface here as a mismatch
20700        // between the two dispatches. Sibling in shape to the peer
20701        // per-arm equivalence pins the
20702        // [`crate::AplicacaoSpec::validate_contratos`] /
20703        // [`crate::MeshPolicy::validate`] /
20704        // [`crate::SupervisorSpec::validate_children`] /
20705        // [`Caixa::validate_upgrade_from`] compound gates each carry
20706        // on their axes.
20707        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20708        let l = crate::LimitsSpec {
20709            memory: None,
20710            fuel: Some(0),
20711            wall_clock: None,
20712            cpu: None,
20713        };
20714        c.limits = Some(l);
20715        let via_method = c.validate_limits().unwrap_err();
20716        let via_standalone = l.validate().unwrap_err();
20717        assert_eq!(
20718            via_method, via_standalone,
20719            "Caixa::validate_limits must surface the present-slot \
20720             arm's diagnostic byte-equal to the standalone \
20721             `LimitsSpec::validate` on the same `LimitsSpec` value"
20722        );
20723        assert!(
20724            matches!(via_method, crate::LimitsError::FuelZero),
20725            "expected FuelZero on the zero-floor-violating `:fuel`, \
20726             got {via_method:?}"
20727        );
20728    }
20729
20730    #[test]
20731    fn validate_limits_accepts_none() {
20732        // Positive control on the absent-slot arm (the fold's identity
20733        // element): a caixa without any `:limits` block (the
20734        // canonical "no bound declared — engine-default applies"
20735        // author shape [`crate::LimitsSpec::is_empty`]'s per-axis
20736        // `None` cascade reads, and the shape the [`Caixa::template`]
20737        // scaffold emits by construction) passes the compound gate
20738        // cleanly, regardless of any per-axis defect a subsequent
20739        // `Some(_)` binding would surface. Pins the identity element
20740        // of the fold on the absent-slot side, matching the peer
20741        // `validate_upgrade_from_accepts_empty_upgrade_from` positive-
20742        // control posture on the sibling M2 slot.
20743        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20744        assert!(
20745            c.limits().is_none(),
20746            "template caixa must carry an absent :limits — got {:?}",
20747            c.limits()
20748        );
20749        c.validate_limits()
20750            .expect("absent :limits must pass the compound gate cleanly");
20751    }
20752
20753    #[test]
20754    fn validate_limits_accepts_clean_fixture() {
20755        // Positive control on the present-slot arm: a caixa whose
20756        // `:limits` is `Some(LimitsSpec::default())` (all four axes
20757        // `None` — every axis absent under the outer `Some(_)`
20758        // binding, so every present-slot arm on
20759        // [`crate::LimitsSpec::validate`] is vacuous) passes the
20760        // compound gate cleanly. A future tightening of any one axis
20761        // that surfaces a diagnostic on the all-`None` `LimitsSpec`
20762        // would land here as a test failure first. Pins the
20763        // present-slot arm's accept-shape on the canonical
20764        // "declared-but-empty" author fixture the
20765        // `limits_round_trip_via_json` peer already round-trips
20766        // (`caixa-core/src/manifest.rs:6971`).
20767        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20768        c.limits = Some(crate::LimitsSpec::default());
20769        c.validate_limits()
20770            .expect("Some(LimitsSpec::default()) must pass the compound gate cleanly");
20771    }
20772
20773    // ── Caixa::validate_behavior — compound per-Caixa entry gate on ──
20774    // ── the M2 `:behavior` slot's pure value-shape surface: folds   ──
20775    // ── the [`crate::BehaviorSpec::validate`] six-slot cascade on   ──
20776    // ── the present-slot arm and the `Option::None` identity        ──
20777    // ── element on the absent-slot arm onto one substrate primitive.──
20778    // ── Byte-for-byte equivalent to the pre-fold                    ──
20779    // ── `if let Some(b) = caixa.behavior() { b.validate() }`        ──
20780    // ── unwrap-and-dispatch pattern at                              ──
20781    // ── `crate::layout::StandardLayout::verify` (`layout.rs`). The  ──
20782    // ── on-disk callback-path existence walk stays open-coded at    ──
20783    // ── the layout altitude because it needs the                    ──
20784    // ── [`crate::layout::LayoutInvariants::exists`] filesystem       ──
20785    // ── oracle the pure typed-shape surface has no reference to —   ──
20786    // ── mirror of the peer M2 `:upgrade-from` per-instruction       ──
20787    // ── script-path existence probe that stayed at the layout       ──
20788    // ── altitude after the [`Caixa::validate_upgrade_from`] lift    ──
20789    // ── (d6801df) for the same reason.                              ──
20790
20791    #[test]
20792    fn validate_behavior_folds_arm_matches_gate() {
20793        // Fail-before-pass-after per-arm equivalence pin on the
20794        // present-slot arm: a fixture whose `:behavior` carries an
20795        // absolute-path `:on-init` (`"/etc/passwd"`, which
20796        // [`crate::BehaviorSpec::validate`] rejects through
20797        // [`crate::BehaviorError::AbsolutePath`]) surfaces the same
20798        // [`crate::BehaviorError`] byte-equal through both the
20799        // compound gate [`Caixa::validate_behavior`] and the standalone
20800        // [`crate::BehaviorSpec::validate`] gate on the same
20801        // `BehaviorSpec` value. Pins the fold — a silent regression
20802        // that de-folded the present-slot arm would surface here as a
20803        // mismatch between the two dispatches. Sibling in shape to the
20804        // peer per-arm equivalence pins the
20805        // [`Caixa::validate_limits`] (baa4688),
20806        // [`Caixa::validate_upgrade_from`] (d6801df),
20807        // [`crate::MeshPolicy::validate`],
20808        // [`crate::AplicacaoSpec::validate_contratos`], and
20809        // [`crate::SupervisorSpec::validate_children`] compound gates
20810        // each carry on their axes.
20811        use crate::BehaviorSpec;
20812        use std::path::PathBuf;
20813        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20814        let b = BehaviorSpec {
20815            on_init: Some(PathBuf::from("/etc/passwd")),
20816            ..Default::default()
20817        };
20818        c.behavior = Some(b.clone());
20819        let via_method = c.validate_behavior().unwrap_err();
20820        let via_standalone = b.validate().unwrap_err();
20821        assert_eq!(
20822            via_method, via_standalone,
20823            "Caixa::validate_behavior must surface the present-slot \
20824             arm's diagnostic byte-equal to the standalone \
20825             `BehaviorSpec::validate` on the same `BehaviorSpec` value"
20826        );
20827        assert!(
20828            matches!(via_method, crate::BehaviorError::AbsolutePath { .. }),
20829            "expected AbsolutePath on the absolute `:on-init` path, \
20830             got {via_method:?}"
20831        );
20832    }
20833
20834    #[test]
20835    fn validate_behavior_accepts_none() {
20836        // Positive control on the absent-slot arm (the fold's identity
20837        // element): a caixa without any `:behavior` block (the
20838        // canonical "no callback declared — the runtime falls back to
20839        // the wasm-engine's default per arm" author shape
20840        // [`crate::BehaviorSpec::is_empty`]'s per-slot `None` cascade
20841        // reads, and the shape the [`Caixa::template`] scaffold emits
20842        // by construction) passes the compound gate cleanly,
20843        // regardless of any per-slot defect a subsequent `Some(_)`
20844        // binding would surface. Pins the identity element of the fold
20845        // on the absent-slot side, matching the peer
20846        // `validate_limits_accepts_none` (baa4688) and
20847        // `validate_upgrade_from_accepts_empty_upgrade_from` (d6801df)
20848        // positive-control postures on the sibling M2 slots.
20849        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20850        assert!(
20851            c.behavior().is_none(),
20852            "template caixa must carry an absent :behavior — got {:?}",
20853            c.behavior()
20854        );
20855        c.validate_behavior()
20856            .expect("absent :behavior must pass the compound gate cleanly");
20857    }
20858
20859    #[test]
20860    fn validate_behavior_accepts_clean_fixture() {
20861        // Positive control on the present-slot arm: a caixa whose
20862        // `:behavior` is `Some(BehaviorSpec::default())` (all six
20863        // slots `None` — every slot absent under the outer `Some(_)`
20864        // binding, so every present-slot arm on
20865        // [`crate::BehaviorSpec::validate`] is vacuous) passes the
20866        // compound gate cleanly. A future tightening of any one arm
20867        // that surfaces a diagnostic on the all-`None` `BehaviorSpec`
20868        // would land here as a test failure first. Pins the
20869        // present-slot arm's accept-shape on the canonical
20870        // "declared-but-empty" author fixture the sibling
20871        // `empty_behavior_round_trip` peer already round-trips
20872        // (`caixa-core/src/behavior.rs` tests). Mirror of the peer
20873        // `validate_limits_accepts_clean_fixture` (baa4688)
20874        // positive-control posture on the sibling M2 `:limits` slot.
20875        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20876        c.behavior = Some(crate::BehaviorSpec::default());
20877        c.validate_behavior()
20878            .expect("Some(BehaviorSpec::default()) must pass the compound gate cleanly");
20879    }
20880
20881    // ── Caixa::validate_deps — compound per-Caixa entry gate on the ──
20882    // ── dep-graph axis: folds the two standalone validators         ──
20883    // ── (per-entry + within-list duplicate walk that this method    ──
20884    // ── opened on, cross-slot self-edge via                         ──
20885    // ── `crate::dep::validate_no_self_dep`) onto one substrate      ──
20886    // ── primitive. Byte-for-byte equivalent to the pre-fold         ──
20887    // ── two-block cascade at                                        ──
20888    // ── `crate::layout::StandardLayout::verify` under the same      ──
20889    // ── canonical dispatch order (per-entry → self-edge).           ──
20890
20891    #[test]
20892    fn validate_deps_folds_per_entry_arm_matches_gate() {
20893        // Fail-before-pass-after per-arm equivalence pin on the
20894        // per-entry + within-list duplicate axis: a fixture whose
20895        // `:deps` carries a per-entry-invalid `:versao` (`"^bad"`,
20896        // which [`crate::parse_requirement`] rejects) surfaces the
20897        // same [`crate::DepError`] through the compound gate
20898        // [`Caixa::validate_deps`] and the standalone per-entry walk
20899        // ([`Dep::validate`]) on the offending entry. Pins the
20900        // fold — a silent regression that de-folded the per-entry arm
20901        // would surface here as a mismatch between the two
20902        // dispatches. Sibling in shape to the peer
20903        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
20904        // per-arm equivalence pin (d6801df) on the M2
20905        // `:upgrade-from` compound gate's per-entry arm, extended
20906        // here onto the universal-axis `:deps` compound gate's
20907        // per-entry arm.
20908        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20909        c.deps = vec![Dep::simple("d", "^bad")];
20910        let via_method = c.validate_deps().unwrap_err();
20911        let via_standalone = c.deps()[0].validate().unwrap_err();
20912        assert_eq!(
20913            via_method, via_standalone,
20914            "Caixa::validate_deps must surface the per-entry arm's \
20915             diagnostic byte-equal to the standalone \
20916             `Dep::validate` on the same offending entry",
20917        );
20918        assert!(
20919            matches!(
20920                via_method,
20921                DepError::VersaoInvalid { ref nome, .. } if nome == "d"
20922            ),
20923            "expected VersaoInvalid on the malformed :versao, got {via_method:?}",
20924        );
20925    }
20926
20927    #[test]
20928    fn validate_deps_folds_self_edge_arm_matches_gate() {
20929        // Per-arm equivalence pin on the cross-slot self-edge axis:
20930        // a fixture whose `:deps` lists the caixa's own `:nome`
20931        // (a self-dep, which
20932        // [`crate::dep::validate_no_self_dep`] rejects as a
20933        // structurally-invalid one-node cycle in the lacre closure's
20934        // dep-graph) surfaces the same [`crate::DepError::DepIsSelf`]
20935        // through both the compound gate and the standalone
20936        // [`crate::dep::validate_no_self_dep`] gate keyed off the
20937        // same `(deps, deps_dev, nome)` triple. Pins the fold's
20938        // second arm — reaching this arm through the compound gate
20939        // requires the per-entry + within-list duplicate walk to
20940        // pass first, which itself pins one cross-arm ordering step.
20941        // Sibling in shape to the peer
20942        // `validate_upgrade_from_folds_versao_arm_matches_gate` /
20943        // `_folds_behavior_arm_matches_gate` cross-slot equivalence
20944        // pins (d6801df) on the M2 `:upgrade-from` compound gate's
20945        // cross-slot arms.
20946        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20947        c.deps = vec![Dep::simple("demo", "^0.1")];
20948        let via_method = c.validate_deps().unwrap_err();
20949        let via_standalone =
20950            crate::dep::validate_no_self_dep(c.deps(), c.deps_dev(), c.nome()).unwrap_err();
20951        assert_eq!(
20952            via_method, via_standalone,
20953            "Caixa::validate_deps must surface the cross-slot \
20954             self-edge diagnostic byte-equal to the standalone \
20955             `crate::dep::validate_no_self_dep` on the same \
20956             (deps, deps_dev, nome) triple",
20957        );
20958        assert!(
20959            matches!(
20960                via_method,
20961                DepError::DepIsSelf { ref nome, list }
20962                    if nome == "demo" && list == crate::render::DEP_AUTHOR_KEY_DEPS
20963            ),
20964            "expected DepIsSelf carrying (nome=\"demo\", list=\":deps\"), got {via_method:?}",
20965        );
20966    }
20967
20968    #[test]
20969    fn validate_deps_per_entry_arm_fires_before_self_edge_arm() {
20970        // Cross-arm ordering pin between the two arms of the fold:
20971        // a fixture carrying BOTH a per-entry-invalid `:versao`
20972        // (`"^bad"` — [`crate::parse_requirement`] rejects the
20973        // requirement grammar) on a non-self-dep entry AND a
20974        // would-be self-edge violation on a second entry (the
20975        // caixa's own `:nome` "demo") surfaces the per-entry
20976        // diagnostic first through the compound gate. Sanity
20977        // assertion: the second entry alone under the same parent
20978        // `:nome` trips the self-edge arm on its own via the
20979        // standalone [`crate::dep::validate_no_self_dep`], so the
20980        // per-entry-first surfacing is a real ordering property,
20981        // not a case where the self-edge arm silently accepts the
20982        // fixture. Pins the pre-fold layout wire-up's canonical
20983        // dispatch order (per-entry + within-list duplicate →
20984        // self-edge) as a property of the substrate primitive
20985        // rather than a convention of the layout call site. Sibling
20986        // in shape to
20987        // `validate_upgrade_from_per_entry_arm_fires_before_versao_arm`
20988        // (d6801df) on the M2 `:upgrade-from` compound gate's
20989        // per-arm ordering property.
20990        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20991        c.deps = vec![
20992            Dep::simple("orquestra", "^bad"),
20993            Dep::simple("demo", "^0.1"),
20994        ];
20995        let err = c.validate_deps().unwrap_err();
20996        assert!(
20997            matches!(
20998                err,
20999                DepError::VersaoInvalid { ref nome, .. } if nome == "orquestra"
21000            ),
21001            "per-entry arm must fire before self-edge arm — expected \
21002             VersaoInvalid on \"orquestra\", got {err:?}",
21003        );
21004        // Sanity: the self-referential entry alone under the same
21005        // parent `:nome` trips the self-edge arm on its own — proves
21006        // the per-entry-first surfacing above is a real ordering
21007        // property, not a case where the self-edge arm silently
21008        // accepts the fixture.
21009        let sanity = crate::dep::validate_no_self_dep(&[Dep::simple("demo", "^0.1")], &[], "demo")
21010            .unwrap_err();
21011        assert!(
21012            matches!(sanity, DepError::DepIsSelf { ref nome, .. } if nome == "demo"),
21013            "sanity: the self-referential entry alone must trip the \
21014             self-edge arm — got {sanity:?}",
21015        );
21016    }
21017
21018    #[test]
21019    fn validate_deps_accepts_clean_fixture() {
21020        // Positive control: a well-formed dep-graph (one `:deps`
21021        // entry naming a non-self DNS-1123 nome + Cargo-shaped
21022        // requirement, one `:deps-dev` entry on a distinct non-self
21023        // nome) passes the compound gate cleanly. A future
21024        // tightening of either arm's accepted set surfaces here as
21025        // a test failure first. Mirrors the peer
21026        // `validate_upgrade_from_accepts_clean_fixture` positive-
21027        // control posture on the sibling per-Caixa compound gate.
21028        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21029        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
21030        c.deps_dev = vec![Dep::simple("caixa-lint", "^0.2")];
21031        c.validate_deps()
21032            .expect("clean fixture must pass the compound `:deps` gate");
21033    }
21034
21035    #[test]
21036    fn validate_deps_accepts_empty_deps_lists() {
21037        // Positive control on the empty-list arm: a caixa without
21038        // any `:deps` or `:deps-dev` entries (the default
21039        // `Vec::new()` `#[serde(default)]` folds an omitted slot
21040        // onto) passes the compound gate cleanly regardless of
21041        // `:nome` — both the per-entry walk and the self-edge walk
21042        // are vacuous on the empty entry list. Pins the identity
21043        // element of the fold on the empty-slot side, peer with the
21044        // `validate_upgrade_from_accepts_empty_upgrade_from` empty-
21045        // arm positive control (d6801df) on the sibling
21046        // `:upgrade-from` compound gate.
21047        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21048        assert!(
21049            c.deps().is_empty(),
21050            "template caixa must carry an empty :deps — got {:?}",
21051            c.deps(),
21052        );
21053        assert!(
21054            c.deps_dev().is_empty(),
21055            "template caixa must carry an empty :deps-dev — got {:?}",
21056            c.deps_dev(),
21057        );
21058        c.validate_deps()
21059            .expect("empty :deps / :deps-dev must pass the compound gate cleanly");
21060    }
21061
21062    // ── Caixa::validate_aplicacao_shape — compound per-Caixa gate ────────
21063
21064    /// Build a minimal well-formed Aplicacao fixture on top of the
21065    /// canonical template. Every arm of the compound gate then patches
21066    /// exactly one axis away from clean so its per-arm diagnostic
21067    /// surfaces without collateral noise from a peer slot.
21068    fn aplicacao_fixture(nome: &str) -> Caixa {
21069        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
21070        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21071        c.kind = CaixaKind::Aplicacao;
21072        c.bibliotecas = vec![];
21073        c.membros = vec![
21074            Membro {
21075                caixa: "checkout".into(),
21076                versao: "^0.1".into(),
21077            },
21078            Membro {
21079                caixa: "cart".into(),
21080                versao: "^0.1".into(),
21081            },
21082        ];
21083        // `:placement` defaults to `Replicated` with an empty
21084        // `:clusters` list which
21085        // [`crate::AplicacaoSpec::validate_placement`] refuses; every
21086        // per-strategy variant needs at least one named cluster (per
21087        // MESH-COMPOSITION §II.1). Pin a single-cluster `SingleNode`
21088        // placement so the typed-shape cascade passes cleanly and the
21089        // per-arm fixtures below can each patch exactly one axis.
21090        c.placement = Some(Placement {
21091            estrategia: PlacementStrategy::SingleNode,
21092            clusters: vec!["rio".into()],
21093            shard_key: None,
21094            affinity: None,
21095        });
21096        c
21097    }
21098
21099    #[test]
21100    fn validate_aplicacao_shape_folds_view_arm_matches_gate() {
21101        // Fail-before-pass-after per-arm equivalence pin on the
21102        // typed-shape cascade arm: a fixture whose typed
21103        // [`crate::AplicacaoSpec`] view fails
21104        // [`crate::AplicacaoSpec::validate`] (here — empty `:membros`,
21105        // which [`crate::AplicacaoSpec::validate_membros`] rejects as
21106        // [`crate::AplicacaoError::NoMembros`] at the first per-slot
21107        // gate) surfaces the same [`crate::AplicacaoError`] diagnostic
21108        // through both the compound gate
21109        // [`Caixa::validate_aplicacao_shape`] and the standalone
21110        // [`crate::AplicacaoSpec::validate`] on the same folded view.
21111        // Pins the fold — a silent regression that de-folded the
21112        // typed-shape arm would surface here as a mismatch between the
21113        // two dispatches. Sibling in shape to the peer
21114        // `validate_deps_folds_per_entry_arm_matches_gate` (b5dd55e) /
21115        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21116        // (d6801df) per-arm equivalence pins on the sibling per-slot
21117        // compound gates.
21118        let mut c = aplicacao_fixture("demo");
21119        c.membros = vec![];
21120        let via_method = c.validate_aplicacao_shape().unwrap_err();
21121        let via_standalone = c.aplicacao_view().unwrap().validate().unwrap_err();
21122        assert_eq!(
21123            via_method, via_standalone,
21124            "Caixa::validate_aplicacao_shape must surface the typed-\
21125             shape arm's diagnostic byte-equal to the standalone \
21126             `AplicacaoSpec::validate` on the same folded view",
21127        );
21128        assert!(
21129            matches!(via_method, crate::AplicacaoError::NoMembros),
21130            "expected NoMembros on the empty :membros, got {via_method:?}",
21131        );
21132    }
21133
21134    #[test]
21135    fn validate_aplicacao_shape_folds_self_membership_arm_matches_gate() {
21136        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21137        // fixture whose `:membros` names the Aplicacao's own `:nome`
21138        // (which [`crate::aplicacao::validate_no_self_membership`]
21139        // rejects as [`crate::AplicacaoError::MembroIsSelfAplicacao`],
21140        // a one-node lacre-closure recursion in the Aplicacao's
21141        // mesh-graph) surfaces the same
21142        // [`crate::AplicacaoError::MembroIsSelfAplicacao`] through both
21143        // the compound gate and the standalone
21144        // [`crate::aplicacao::validate_no_self_membership`] keyed off
21145        // the same `(membros, nome)` pair. Pins the fold's second arm
21146        // — reaching this arm through the compound gate requires the
21147        // typed-shape cascade to pass first, which itself pins one
21148        // cross-arm ordering step. Sibling in shape to the peer
21149        // `validate_deps_folds_self_edge_arm_matches_gate` (b5dd55e)
21150        // cross-slot equivalence pin on the sibling per-slot compound
21151        // gate.
21152        use crate::aplicacao::Membro;
21153        let mut c = aplicacao_fixture("demo");
21154        c.membros = vec![Membro {
21155            caixa: "demo".into(),
21156            versao: "^0.1".into(),
21157        }];
21158        let via_method = c.validate_aplicacao_shape().unwrap_err();
21159        let via_standalone =
21160            crate::aplicacao::validate_no_self_membership(c.membros(), c.nome()).unwrap_err();
21161        assert_eq!(
21162            via_method, via_standalone,
21163            "Caixa::validate_aplicacao_shape must surface the cross-\
21164             slot self-edge diagnostic byte-equal to the standalone \
21165             `aplicacao::validate_no_self_membership` on the same \
21166             (membros, nome) pair",
21167        );
21168        assert!(
21169            matches!(
21170                via_method,
21171                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa } if caixa == "demo"
21172            ),
21173            "expected MembroIsSelfAplicacao carrying (caixa=\"demo\"), \
21174             got {via_method:?}",
21175        );
21176    }
21177
21178    #[test]
21179    fn validate_aplicacao_shape_view_arm_fires_before_self_membership_arm() {
21180        // Cross-arm ordering pin between the two arms of the fold: a
21181        // fixture carrying BOTH a typed-shape violation (a `:contratos`
21182        // edge whose `:para` is not a declared member — rejected by
21183        // [`crate::AplicacaoSpec::validate_contratos`] as
21184        // [`crate::AplicacaoError::ContratoMemberMissing`]) AND a
21185        // would-be self-edge violation (a `:membros` entry naming the
21186        // caixa's own `:nome`) surfaces the typed-shape diagnostic
21187        // first through the compound gate. Sanity assertion: the
21188        // self-referential `:membros` entry alone under the same
21189        // parent `:nome` trips the self-edge arm on its own via the
21190        // standalone [`crate::aplicacao::validate_no_self_membership`],
21191        // so the typed-shape-first surfacing is a real ordering
21192        // property, not a case where the self-edge arm silently
21193        // accepts the fixture. Pins the pre-fold layout wire-up's
21194        // canonical dispatch order (typed-shape cascade → cross-slot
21195        // self-edge) as a property of the substrate primitive rather
21196        // than a convention of the layout call site. Sibling in shape
21197        // to `validate_deps_per_entry_arm_fires_before_self_edge_arm`
21198        // (b5dd55e) on the sibling per-slot compound gate's per-arm
21199        // ordering property.
21200        use crate::aplicacao::{Membro, WitContract};
21201        let mut c = aplicacao_fixture("demo");
21202        c.membros = vec![Membro {
21203            caixa: "demo".into(),
21204            versao: "^0.1".into(),
21205        }];
21206        c.contratos = vec![WitContract {
21207            de: "demo".into(),
21208            para: "orphan".into(),
21209            wit: "wasi:http/proxy".into(),
21210            endpoint: Some("/x".into()),
21211            subject: None,
21212            slot: None,
21213        }];
21214        let err = c.validate_aplicacao_shape().unwrap_err();
21215        assert!(
21216            matches!(
21217                err,
21218                crate::AplicacaoError::ContratoMemberMissing { ref caixa }
21219                    if caixa == "orphan"
21220            ),
21221            "typed-shape arm must fire before self-edge arm — expected \
21222             ContratoMemberMissing on \"orphan\", got {err:?}",
21223        );
21224        // Sanity: the self-referential `:membros` entry alone under
21225        // the same parent `:nome` trips the self-edge arm on its own
21226        // — proves the typed-shape-first surfacing above is a real
21227        // ordering property, not a case where the self-edge arm
21228        // silently accepts the fixture.
21229        let sanity = crate::aplicacao::validate_no_self_membership(
21230            &[Membro {
21231                caixa: "demo".into(),
21232                versao: "^0.1".into(),
21233            }],
21234            "demo",
21235        )
21236        .unwrap_err();
21237        assert!(
21238            matches!(
21239                sanity,
21240                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa }
21241                    if caixa == "demo"
21242            ),
21243            "sanity: the self-referential :membros entry alone must \
21244             trip the self-edge arm — got {sanity:?}",
21245        );
21246    }
21247
21248    #[test]
21249    fn validate_aplicacao_shape_accepts_non_aplicacao_kind() {
21250        // Positive control on the identity-element arm: every non-
21251        // Aplicacao kind passes the compound gate trivially — the
21252        // paired [`Caixa::aplicacao_view`] accessor returns `None`
21253        // off the Aplicacao arm (by construction, keyed on
21254        // `caixa.kind().is_aplicacao()`), so the fold short-circuits
21255        // to `Ok(())` without touching the mesh slots. Pins the
21256        // identity element on every non-Aplicacao kind — a future
21257        // refactor that made the mesh-slot cascade fire on the wrong
21258        // kind (say, on a `Servico` whose mesh slots happen to be
21259        // populated in a mis-authored manifest, which the peer
21260        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
21261        // coherence gate would refuse upstream anyway) surfaces here
21262        // as a test failure first. Peer with the
21263        // `validate_limits_accepts_none` / `validate_behavior_accepts_none`
21264        // identity-element pins on the sibling M2 `Option`-shaped
21265        // per-Caixa compound gates.
21266        for kind in [
21267            CaixaKind::Biblioteca,
21268            CaixaKind::Binario,
21269            CaixaKind::Servico,
21270            CaixaKind::Supervisor,
21271            CaixaKind::Acao,
21272        ] {
21273            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21274            c.kind = kind;
21275            assert!(
21276                c.aplicacao_view().is_none(),
21277                "aplicacao_view must return None off the Aplicacao arm \
21278                 for kind {kind:?}",
21279            );
21280            c.validate_aplicacao_shape().expect(
21281                "non-Aplicacao kinds must pass the compound gate as the fold's identity element",
21282            );
21283        }
21284    }
21285
21286    #[test]
21287    fn validate_aplicacao_shape_accepts_clean_fixture() {
21288        // Positive control: a well-formed Aplicacao (two DNS-1123
21289        // members with valid semver constraints, no `:contratos` /
21290        // `:entrada` / `:placement` / `:politicas` set — every
21291        // per-slot gate accepts the vacuous / omitted arm) passes the
21292        // compound gate cleanly. A future tightening of either arm's
21293        // accepted set surfaces here as a test failure first. Mirrors
21294        // the peer `validate_deps_accepts_clean_fixture` (b5dd55e) /
21295        // `validate_upgrade_from_accepts_clean_fixture` (d6801df)
21296        // positive-control postures on the sibling per-Caixa
21297        // compound gates.
21298        let c = aplicacao_fixture("demo");
21299        c.validate_aplicacao_shape()
21300            .expect("clean Aplicacao fixture must pass the compound gate");
21301    }
21302
21303    // ── Caixa::validate_supervisor_shape — compound per-Caixa gate ───────
21304
21305    /// Build a minimal well-formed Supervisor fixture on top of the
21306    /// canonical template. Every arm of the compound gate then patches
21307    /// exactly one axis away from clean so its per-arm diagnostic
21308    /// surfaces without collateral noise from a peer slot. Peer of
21309    /// [`aplicacao_fixture`] on the sibling per-Aplicacao compound
21310    /// gate's pin family.
21311    fn supervisor_fixture(nome: &str) -> Caixa {
21312        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
21313        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21314        c.kind = CaixaKind::Supervisor;
21315        // Supervisors don't run code — clear the biblioteca slot the
21316        // template seeds so the fold's per-arm diagnostics surface
21317        // without the peer `SupervisorOwnsCode` kind-coherence gate
21318        // firing upstream at the layout altitude.
21319        c.bibliotecas = vec![];
21320        // `:estrategia` defaults to `OneForOne` at the typed view level,
21321        // and `OneForOne` requires at least one `:children` entry — pin
21322        // a single-child `Permanent` worker so the typed-shape cascade
21323        // passes cleanly and the per-arm fixtures below can each patch
21324        // exactly one axis.
21325        c.estrategia = Some(RestartStrategy::OneForOne);
21326        c.children = vec![ChildSpec {
21327            caixa: "worker".into(),
21328            versao: "^0.1".into(),
21329            restart: RestartPolicy::Permanent,
21330        }];
21331        c
21332    }
21333
21334    #[test]
21335    fn validate_supervisor_shape_folds_view_arm_matches_gate() {
21336        // Fail-before-pass-after per-arm equivalence pin on the
21337        // typed-shape cascade arm: a fixture whose typed
21338        // [`crate::SupervisorSpec`] view fails
21339        // [`crate::SupervisorSpec::validate`] (here — a duplicate
21340        // `:children` `:caixa` entry, which
21341        // [`crate::SupervisorSpec::validate`]'s set-not-multiset gate
21342        // rejects as [`crate::SupervisorError::DuplicateChildCaixa`])
21343        // surfaces the same [`crate::SupervisorError`] diagnostic
21344        // through both the compound gate
21345        // [`Caixa::validate_supervisor_shape`] and the standalone
21346        // [`crate::SupervisorSpec::validate`] on the same folded view.
21347        // Pins the fold — a silent regression that de-folded the
21348        // typed-shape arm would surface here as a mismatch between the
21349        // two dispatches. Sibling in shape to the peer
21350        // `validate_aplicacao_shape_folds_view_arm_matches_gate`
21351        // (949a7a0) on the sibling per-Aplicacao compound gate.
21352        use crate::supervisor::{ChildSpec, RestartPolicy};
21353        let mut c = supervisor_fixture("demo");
21354        c.children = vec![
21355            ChildSpec {
21356                caixa: "worker".into(),
21357                versao: "^0.1".into(),
21358                restart: RestartPolicy::Permanent,
21359            },
21360            ChildSpec {
21361                caixa: "worker".into(),
21362                versao: "^0.1".into(),
21363                restart: RestartPolicy::Permanent,
21364            },
21365        ];
21366        let via_method = c.validate_supervisor_shape().unwrap_err();
21367        let via_standalone = c.supervisor_view().unwrap().validate().unwrap_err();
21368        assert_eq!(
21369            via_method, via_standalone,
21370            "Caixa::validate_supervisor_shape must surface the typed-\
21371             shape arm's diagnostic byte-equal to the standalone \
21372             `SupervisorSpec::validate` on the same folded view",
21373        );
21374        assert!(
21375            matches!(
21376                via_method,
21377                crate::SupervisorError::DuplicateChildCaixa { ref caixa }
21378                    if caixa == "worker"
21379            ),
21380            "expected DuplicateChildCaixa on the duplicate 'worker' \
21381             child, got {via_method:?}",
21382        );
21383    }
21384
21385    #[test]
21386    fn validate_supervisor_shape_folds_self_supervision_arm_matches_gate() {
21387        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21388        // fixture whose `:children :caixa` names the Supervisor's own
21389        // `:nome` (which
21390        // [`crate::supervisor::validate_no_self_supervision`] rejects
21391        // as [`crate::SupervisorError::ChildSupervisesSelf`], a
21392        // one-node reconciliation cycle in the supervisor's
21393        // supervision-tree) surfaces the same
21394        // [`crate::SupervisorError::ChildSupervisesSelf`] through both
21395        // the compound gate and the standalone
21396        // [`crate::supervisor::validate_no_self_supervision`] keyed
21397        // off the same `(children, nome)` pair. Pins the fold's
21398        // second arm — reaching this arm through the compound gate
21399        // requires the typed-shape cascade to pass first, which itself
21400        // pins one cross-arm ordering step. Sibling in shape to the
21401        // peer
21402        // `validate_aplicacao_shape_folds_self_membership_arm_matches_gate`
21403        // (949a7a0) cross-slot equivalence pin on the sibling
21404        // per-Aplicacao compound gate.
21405        use crate::supervisor::{ChildSpec, RestartPolicy};
21406        let mut c = supervisor_fixture("demo");
21407        c.children = vec![ChildSpec {
21408            caixa: "demo".into(),
21409            versao: "^0.1".into(),
21410            restart: RestartPolicy::Permanent,
21411        }];
21412        let via_method = c.validate_supervisor_shape().unwrap_err();
21413        let via_standalone =
21414            crate::supervisor::validate_no_self_supervision(c.children(), c.nome()).unwrap_err();
21415        assert_eq!(
21416            via_method, via_standalone,
21417            "Caixa::validate_supervisor_shape must surface the cross-\
21418             slot self-edge diagnostic byte-equal to the standalone \
21419             `supervisor::validate_no_self_supervision` on the same \
21420             (children, nome) pair",
21421        );
21422        assert!(
21423            matches!(
21424                via_method,
21425                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21426            ),
21427            "expected ChildSupervisesSelf carrying (caixa=\"demo\"), \
21428             got {via_method:?}",
21429        );
21430    }
21431
21432    #[test]
21433    fn validate_supervisor_shape_view_arm_fires_before_self_supervision_arm() {
21434        // Cross-arm ordering pin between the two arms of the fold: a
21435        // fixture carrying BOTH a typed-shape violation (a per-child
21436        // empty `:caixa` name — rejected by
21437        // [`crate::SupervisorSpec::validate`] as
21438        // [`crate::SupervisorError::EmptyChildName`]) AND a would-be
21439        // self-edge violation (a `:children` entry naming the
21440        // supervisor's own `:nome`) surfaces the typed-shape
21441        // diagnostic first through the compound gate. Sanity
21442        // assertion: the self-referential `:children` entry alone
21443        // under the same parent `:nome` trips the self-edge arm on
21444        // its own via the standalone
21445        // [`crate::supervisor::validate_no_self_supervision`], so the
21446        // typed-shape-first surfacing is a real ordering property, not
21447        // a case where the self-edge arm silently accepts the fixture.
21448        // Pins the pre-fold layout wire-up's canonical dispatch order
21449        // (typed-shape cascade → cross-slot self-edge) as a property
21450        // of the substrate primitive rather than a convention of the
21451        // layout call site. Sibling in shape to
21452        // `validate_aplicacao_shape_view_arm_fires_before_self_membership_arm`
21453        // (949a7a0) on the sibling per-Aplicacao compound gate.
21454        use crate::supervisor::{ChildSpec, RestartPolicy};
21455        let mut c = supervisor_fixture("demo");
21456        c.children = vec![
21457            ChildSpec {
21458                caixa: String::new(),
21459                versao: "^0.1".into(),
21460                restart: RestartPolicy::Permanent,
21461            },
21462            ChildSpec {
21463                caixa: "demo".into(),
21464                versao: "^0.1".into(),
21465                restart: RestartPolicy::Permanent,
21466            },
21467        ];
21468        let err = c.validate_supervisor_shape().unwrap_err();
21469        assert!(
21470            matches!(err, crate::SupervisorError::EmptyChildName),
21471            "typed-shape arm must fire before self-edge arm — expected \
21472             EmptyChildName on the empty :caixa child, got {err:?}",
21473        );
21474        // Sanity: the self-referential `:children` entry alone under
21475        // the same parent `:nome` trips the self-edge arm on its own
21476        // — proves the typed-shape-first surfacing above is a real
21477        // ordering property, not a case where the self-edge arm
21478        // silently accepts the fixture.
21479        let sanity = crate::supervisor::validate_no_self_supervision(
21480            &[ChildSpec {
21481                caixa: "demo".into(),
21482                versao: "^0.1".into(),
21483                restart: RestartPolicy::Permanent,
21484            }],
21485            "demo",
21486        )
21487        .unwrap_err();
21488        assert!(
21489            matches!(
21490                sanity,
21491                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21492            ),
21493            "sanity: the self-referential :children entry alone must \
21494             trip the self-edge arm — got {sanity:?}",
21495        );
21496    }
21497
21498    #[test]
21499    fn validate_supervisor_shape_accepts_non_supervisor_kind() {
21500        // Positive control on the identity-element arm: every non-
21501        // Supervisor kind passes the compound gate trivially — the
21502        // paired [`Caixa::supervisor_view`] accessor returns `None`
21503        // off the Supervisor arm (by construction, keyed on
21504        // `caixa.kind().is_supervisor()`), so the fold short-circuits
21505        // to `Ok(())` without touching the supervision-tree slots.
21506        // Pins the identity element on every non-Supervisor kind — a
21507        // future refactor that made the supervision-tree cascade fire
21508        // on the wrong kind (say, on a `Servico` whose supervision
21509        // slots happen to be populated in a mis-authored manifest,
21510        // which the peer
21511        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
21512        // kind-coherence gate would refuse upstream anyway) surfaces
21513        // here as a test failure first. Peer with the
21514        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
21515        // (949a7a0) / `validate_limits_accepts_none` /
21516        // `validate_behavior_accepts_none` identity-element pins on
21517        // the sibling per-Caixa compound gates.
21518        for kind in [
21519            CaixaKind::Biblioteca,
21520            CaixaKind::Binario,
21521            CaixaKind::Servico,
21522            CaixaKind::Aplicacao,
21523            CaixaKind::Acao,
21524        ] {
21525            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21526            c.kind = kind;
21527            assert!(
21528                c.supervisor_view().is_none(),
21529                "supervisor_view must return None off the Supervisor \
21530                 arm for kind {kind:?}",
21531            );
21532            c.validate_supervisor_shape().expect(
21533                "non-Supervisor kinds must pass the compound gate as the fold's identity element",
21534            );
21535        }
21536    }
21537
21538    #[test]
21539    fn validate_supervisor_shape_accepts_clean_fixture() {
21540        // Positive control: a well-formed Supervisor (single
21541        // DNS-1123-valid `Permanent` worker child under the
21542        // `OneForOne` strategy — the OTP MaxIntensity/Period defaults
21543        // accept the vacuous `:max-restarts` / `:restart-window`
21544        // arms) passes the compound gate cleanly. A future tightening
21545        // of either arm's accepted set surfaces here as a test
21546        // failure first. Mirrors the peer
21547        // `validate_aplicacao_shape_accepts_clean_fixture` (949a7a0)
21548        // positive-control posture on the sibling per-Caixa compound
21549        // gate.
21550        let c = supervisor_fixture("demo");
21551        c.validate_supervisor_shape()
21552            .expect("clean Supervisor fixture must pass the compound gate");
21553    }
21554
21555    // ── Caixa::validate_acao_shape — compound per-Caixa gate ─────────────
21556
21557    /// Build a minimal well-formed `:kind Acao` fixture with a valid
21558    /// two-node acyclic `:ci` slot. Every arm of the compound gate
21559    /// then patches exactly one axis away from clean so its per-arm
21560    /// diagnostic surfaces without collateral noise from a peer slot.
21561    /// Peer of [`supervisor_fixture`] / [`aplicacao_fixture`] on the
21562    /// sibling per-kind compound gates' pin families.
21563    fn acao_fixture(nome: &str) -> Caixa {
21564        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21565        c.kind = CaixaKind::Acao;
21566        // Acaos don't run code — clear the biblioteca slot the template
21567        // seeds so the compound gate's per-arm diagnostics surface
21568        // without the peer `AcaoOwnsCode` kind-coherence gate firing
21569        // upstream at the layout altitude.
21570        c.bibliotecas = vec![];
21571        c.ci = Some(canteiro_types::CiRun {
21572            workspace: "pleme-io".into(),
21573            repo: "caixa".into(),
21574            nodes: vec![
21575                canteiro_types::CiNode::new(
21576                    "build",
21577                    canteiro_types::EnvClass::None,
21578                    canteiro_types::ActionRef {
21579                        name: "build".into(),
21580                        command: "true".into(),
21581                        args: vec![],
21582                    },
21583                    vec![],
21584                ),
21585                canteiro_types::CiNode::new(
21586                    "test",
21587                    canteiro_types::EnvClass::None,
21588                    canteiro_types::ActionRef {
21589                        name: "test".into(),
21590                        command: "true".into(),
21591                        args: vec![],
21592                    },
21593                    vec!["build".into()],
21594                ),
21595            ],
21596        });
21597        c
21598    }
21599
21600    #[test]
21601    fn validate_acao_shape_folds_decompose_arm_matches_gate() {
21602        // Fail-before-pass-after per-arm equivalence pin on the
21603        // decompose axis: a fixture whose `:ci` slot fails
21604        // [`canteiro_types::decompose`] (here — a minimal two-node
21605        // cycle `a → b → a`, which the sibling
21606        // [`crate::render::decompose_ci`] wraps as
21607        // [`crate::CiDecomposeFailure`] carrying
21608        // [`canteiro_types::DecomposeError::Cycle`]) surfaces the same
21609        // [`crate::CiDecomposeFailure`] diagnostic through both the
21610        // compound gate [`Caixa::validate_acao_shape`] and the
21611        // standalone [`crate::render::decompose_ci`] on the same
21612        // `(caixa, ci)` fixture. Pins the fold — a silent regression
21613        // that de-folded the decompose arm would surface here as a
21614        // mismatch between the two dispatches. Sibling in shape to the
21615        // peer `validate_supervisor_shape_folds_view_arm_matches_gate`
21616        // / `validate_aplicacao_shape_folds_view_arm_matches_gate` on
21617        // the sibling per-kind compound gates.
21618        //
21619        // [`crate::CiDecomposeFailure`] does not derive `PartialEq`
21620        // (its `#[source]` carrier [`canteiro_types::DecomposeError`]
21621        // does, but the wrapper deliberately does not), so the two
21622        // dispatches are compared through their field pair
21623        // (`nome` + `source`) rather than through `assert_eq!` on the
21624        // wrapper itself — every field on the wrapper is thereby
21625        // pinned byte-equal without depending on an implementation
21626        // detail of `CiDecomposeFailure`'s derive set.
21627        let mut c = acao_fixture("demo");
21628        c.ci = Some(canteiro_types::CiRun {
21629            workspace: "pleme-io".into(),
21630            repo: "caixa".into(),
21631            nodes: vec![
21632                canteiro_types::CiNode::new(
21633                    "a",
21634                    canteiro_types::EnvClass::None,
21635                    canteiro_types::ActionRef {
21636                        name: "a".into(),
21637                        command: "true".into(),
21638                        args: vec![],
21639                    },
21640                    vec!["b".into()],
21641                ),
21642                canteiro_types::CiNode::new(
21643                    "b",
21644                    canteiro_types::EnvClass::None,
21645                    canteiro_types::ActionRef {
21646                        name: "b".into(),
21647                        command: "true".into(),
21648                        args: vec![],
21649                    },
21650                    vec!["a".into()],
21651                ),
21652            ],
21653        });
21654        let via_method = c.validate_acao_shape().unwrap_err();
21655        let via_standalone =
21656            crate::render::decompose_ci(&c, c.ci().expect("fixture has a :ci")).unwrap_err();
21657        assert_eq!(
21658            via_method.nome, via_standalone.nome,
21659            "Caixa::validate_acao_shape must surface the decompose \
21660             failure's `nome` byte-equal to the standalone \
21661             `decompose_ci` on the same (caixa, ci) fixture",
21662        );
21663        assert_eq!(
21664            via_method.source, via_standalone.source,
21665            "Caixa::validate_acao_shape must surface the decompose \
21666             failure's `source` byte-equal to the standalone \
21667             `decompose_ci` on the same (caixa, ci) fixture",
21668        );
21669        assert_eq!(
21670            via_method.source,
21671            canteiro_types::DecomposeError::Cycle,
21672            "expected the two-node cycle `a → b → a` to surface as \
21673             DecomposeError::Cycle, got {source:?}",
21674            source = via_method.source,
21675        );
21676    }
21677
21678    #[test]
21679    fn validate_acao_shape_folds_duplicate_node_arm_matches_gate() {
21680        // Per-arm equivalence pin on the `DuplicateNode` decompose
21681        // arm — the sibling of `Cycle` on the substrate's
21682        // `canteiro_types::DecomposeError` enumeration. A fixture
21683        // whose `:ci` slot carries two nodes sharing one name
21684        // surfaces the same [`crate::CiDecomposeFailure`] through
21685        // both dispatches, pinned by field pair. The three
21686        // decompose arms (`DuplicateNode` / `UnknownDep` / `Cycle`)
21687        // together enumerate every failure mode
21688        // [`canteiro_types::decompose`] refuses, so the per-arm
21689        // pins collectively cover the whole decompose axis.
21690        let mut c = acao_fixture("demo");
21691        c.ci = Some(canteiro_types::CiRun {
21692            workspace: "pleme-io".into(),
21693            repo: "caixa".into(),
21694            nodes: vec![
21695                canteiro_types::CiNode::new(
21696                    "twin",
21697                    canteiro_types::EnvClass::None,
21698                    canteiro_types::ActionRef {
21699                        name: "twin".into(),
21700                        command: "true".into(),
21701                        args: vec![],
21702                    },
21703                    vec![],
21704                ),
21705                canteiro_types::CiNode::new(
21706                    "twin",
21707                    canteiro_types::EnvClass::None,
21708                    canteiro_types::ActionRef {
21709                        name: "twin".into(),
21710                        command: "true".into(),
21711                        args: vec![],
21712                    },
21713                    vec![],
21714                ),
21715            ],
21716        });
21717        let via_method = c.validate_acao_shape().unwrap_err();
21718        assert_eq!(
21719            via_method.source,
21720            canteiro_types::DecomposeError::DuplicateNode("twin".into()),
21721            "expected DuplicateNode on the two-\"twin\"-name fixture, \
21722             got {source:?}",
21723            source = via_method.source,
21724        );
21725    }
21726
21727    #[test]
21728    fn validate_acao_shape_folds_unknown_dep_arm_matches_gate() {
21729        // Per-arm equivalence pin on the `UnknownDep` decompose arm —
21730        // the third and last arm on `canteiro_types::DecomposeError`
21731        // after `Cycle` and `DuplicateNode`. A fixture whose `:ci`
21732        // slot names a `deps` entry no declared node satisfies
21733        // surfaces the same [`crate::CiDecomposeFailure`] through
21734        // both dispatches. Pins the third decompose arm at the
21735        // compound gate.
21736        let mut c = acao_fixture("demo");
21737        c.ci = Some(canteiro_types::CiRun {
21738            workspace: "pleme-io".into(),
21739            repo: "caixa".into(),
21740            nodes: vec![canteiro_types::CiNode::new(
21741                "orphan",
21742                canteiro_types::EnvClass::None,
21743                canteiro_types::ActionRef {
21744                    name: "orphan".into(),
21745                    command: "true".into(),
21746                    args: vec![],
21747                },
21748                vec!["ghost".into()],
21749            )],
21750        });
21751        let via_method = c.validate_acao_shape().unwrap_err();
21752        assert_eq!(
21753            via_method.source,
21754            canteiro_types::DecomposeError::UnknownDep {
21755                node: "orphan".into(),
21756                dep: "ghost".into(),
21757            },
21758            "expected UnknownDep on the orphan-node-depends-on-ghost \
21759             fixture, got {source:?}",
21760            source = via_method.source,
21761        );
21762    }
21763
21764    #[test]
21765    fn validate_acao_shape_accepts_non_acao_kind() {
21766        // Positive control on the identity-element arm: every non-
21767        // Acao kind passes the compound gate trivially — the paired
21768        // `caixa.kind().is_acao()` guard short-circuits before the
21769        // decompose gate ever fires, so the fold returns `Ok(())`
21770        // without touching the `:ci` slot even when a non-Acao
21771        // fixture happens to declare one (the sibling
21772        // [`crate::LayoutError::CiOnNonAcao`] kind-coherence gate
21773        // catches that at the layout altitude anyway). Pins the
21774        // identity element on every non-Acao kind. Peer with the
21775        // `validate_supervisor_shape_accepts_non_supervisor_kind` /
21776        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
21777        // identity-element pins on the sibling per-Caixa compound
21778        // gates.
21779        for kind in [
21780            CaixaKind::Biblioteca,
21781            CaixaKind::Binario,
21782            CaixaKind::Servico,
21783            CaixaKind::Supervisor,
21784            CaixaKind::Aplicacao,
21785        ] {
21786            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21787            c.kind = kind;
21788            c.validate_acao_shape().expect(
21789                "non-Acao kinds must pass the compound gate as the fold's identity element",
21790            );
21791        }
21792    }
21793
21794    #[test]
21795    fn validate_acao_shape_accepts_absent_ci_slot() {
21796        // Positive control on the second identity-element arm: a
21797        // `:kind Acao` caixa with `ci = None` passes the compound
21798        // gate trivially — the presence gate is the sibling axis
21799        // owned by [`crate::LayoutError::MissingCi`] /
21800        // [`crate::require_ci`] / [`crate::MissingCiSlot`], not by
21801        // the decompose gate. A caixa that carries no `:ci` slot
21802        // has no run to decompose, so the fold's `let Some(ci) = …
21803        // else { return Ok(()) }` arm short-circuits before the
21804        // decompose gate fires. Pins that the two axes stay
21805        // separately diagnosable at the layout altitude — a future
21806        // regression that collapsed the presence gate onto the
21807        // shape gate here would land a
21808        // [`crate::CiDecomposeFailure`] on the wrong axis and
21809        // surface an off-target diagnostic at `feira build` time.
21810        let mut c = acao_fixture("demo");
21811        c.ci = None;
21812        c.validate_acao_shape().expect(
21813            "an :kind Acao caixa with absent :ci must pass the compound gate — \
21814             the presence gate is layout's MissingCi axis, not the decompose gate",
21815        );
21816    }
21817
21818    #[test]
21819    fn validate_acao_shape_accepts_clean_fixture() {
21820        // Positive control: a well-formed Acao (a two-node acyclic
21821        // `:ci` run with `test` depending on `build`) passes the
21822        // compound gate cleanly. A future tightening of the
21823        // decompose gate's accepted set surfaces here as a test
21824        // failure first. Mirrors the peer
21825        // `validate_supervisor_shape_accepts_clean_fixture` /
21826        // `validate_aplicacao_shape_accepts_clean_fixture`
21827        // positive-control posture on the sibling per-Caixa
21828        // compound gates.
21829        let c = acao_fixture("demo");
21830        c.validate_acao_shape()
21831            .expect("clean Acao fixture must pass the compound gate");
21832    }
21833
21834    fn bare_servico_fixture(nome: &str) -> Caixa {
21835        // A minimal Servico caixa with no code and no typed slots —
21836        // the cross-family fold's identity element on every arm.
21837        // Clears the biblioteca slot the template seeds so the
21838        // per-arm patches below can each add exactly one typed slot
21839        // without a peer `ServicoOwnsCode` / layout-side kind-gate
21840        // firing upstream.
21841        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21842        c.kind = CaixaKind::Servico;
21843        c.bibliotecas = vec![];
21844        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
21845        c
21846    }
21847
21848    #[test]
21849    fn validate_kind_slot_coherence_folds_mesh_arm_matches_gate() {
21850        // Fail-before-pass-after per-arm equivalence pin on the M3
21851        // mesh-slot arm of the cross-family kind-coherence fold: a
21852        // non-Aplicacao caixa carrying a declared M3 mesh slot (here
21853        // a `:kind Servico` fixture with a single `:membros` entry —
21854        // the smallest possible M3 slot declaration on a foreign
21855        // kind) surfaces the same
21856        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] variant
21857        // through both the compound gate
21858        // [`Caixa::validate_kind_slot_coherence`] and the standalone
21859        // constructor [`crate::LayoutError::mesh_slots_on_non_aplicacao`]
21860        // dispatched on the same `declared_mesh_slots` list. Pins
21861        // the fold — a silent regression that de-folded the mesh
21862        // arm would surface here as a mismatch between the two
21863        // dispatches. Sibling in shape to the peer
21864        // `validate_aplicacao_shape_folds_view_arm_matches_gate` /
21865        // `validate_supervisor_shape_folds_view_arm_matches_gate` /
21866        // `validate_acao_shape_folds_decompose_arm_matches_gate`
21867        // per-arm equivalence pins on the sibling per-kind compound
21868        // gates.
21869        use crate::aplicacao::Membro;
21870        let mut c = bare_servico_fixture("demo");
21871        c.membros = vec![Membro {
21872            caixa: "cart".into(),
21873            versao: "^0.1".into(),
21874        }];
21875        let via_method = c.validate_kind_slot_coherence().unwrap_err();
21876        let via_standalone =
21877            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
21878        assert_eq!(
21879            via_method, via_standalone,
21880            "Caixa::validate_kind_slot_coherence must surface the M3 \
21881             mesh-slot arm's diagnostic byte-equal to the standalone \
21882             LayoutError::mesh_slots_on_non_aplicacao ctor on the same \
21883             declared_mesh_slots list",
21884        );
21885    }
21886
21887    #[test]
21888    fn validate_kind_slot_coherence_folds_supervisor_arm_matches_gate() {
21889        // Per-arm equivalence pin on the supervisor-tree arm — the
21890        // sibling of the mesh arm on the cross-family fold. A
21891        // non-Supervisor caixa carrying a declared supervisor slot
21892        // (a `:kind Servico` fixture with `:estrategia` set — the
21893        // smallest possible supervisor slot declaration on a
21894        // foreign kind) surfaces the same
21895        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
21896        // variant through both dispatches, pinned by field pair
21897        // through `PartialEq`.
21898        use crate::supervisor::RestartStrategy;
21899        let mut c = bare_servico_fixture("demo");
21900        c.estrategia = Some(RestartStrategy::OneForOne);
21901        let via_method = c.validate_kind_slot_coherence().unwrap_err();
21902        let via_standalone = crate::LayoutError::supervisor_slots_on_non_supervisor(
21903            &c,
21904            c.declared_supervisor_slots(),
21905        );
21906        assert_eq!(
21907            via_method, via_standalone,
21908            "Caixa::validate_kind_slot_coherence must surface the \
21909             supervisor-tree arm's diagnostic byte-equal to the \
21910             standalone LayoutError::supervisor_slots_on_non_supervisor \
21911             ctor on the same declared_supervisor_slots list",
21912        );
21913    }
21914
21915    #[test]
21916    fn validate_kind_slot_coherence_folds_servico_arm_matches_gate() {
21917        // Per-arm equivalence pin on the M2 Servico-runtime arm —
21918        // the third and last arm on the cross-family fold. A
21919        // non-Servico caixa carrying a declared M2 slot (a `:kind
21920        // Biblioteca` fixture with `:limits` set — the smallest
21921        // possible M2 slot declaration on a foreign kind) surfaces
21922        // the same [`crate::LayoutError::ServicoSlotsOnNonServico`]
21923        // variant through both dispatches. The three arms together
21924        // enumerate every typed-slot family the substrate carries
21925        // whose "declared but ignored" footgun is gated at the
21926        // layout altitude by a `{ caixa, kind, slots }` wrap variant,
21927        // so the per-arm pins collectively cover the whole
21928        // cross-family kind-coherence axis.
21929        use crate::limits::LimitsSpec;
21930        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21931        c.kind = CaixaKind::Biblioteca;
21932        c.limits = Some(LimitsSpec {
21933            memory: Some(64 * 1024 * 1024),
21934            fuel: None,
21935            wall_clock: None,
21936            cpu: None,
21937        });
21938        let via_method = c.validate_kind_slot_coherence().unwrap_err();
21939        let via_standalone =
21940            crate::LayoutError::servico_slots_on_non_servico(&c, c.declared_servico_slots());
21941        assert_eq!(
21942            via_method, via_standalone,
21943            "Caixa::validate_kind_slot_coherence must surface the M2 \
21944             Servico-runtime arm's diagnostic byte-equal to the \
21945             standalone LayoutError::servico_slots_on_non_servico ctor \
21946             on the same declared_servico_slots list",
21947        );
21948    }
21949
21950    #[test]
21951    fn validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm() {
21952        // Cross-arm ordering pin between the first two arms of the
21953        // fold: a fixture carrying BOTH a declared M3 mesh slot
21954        // (`:membros`) AND a declared supervisor-tree slot
21955        // (`:estrategia`) on a foreign kind (a `:kind Servico` here —
21956        // foreign to both the Aplicacao arm and the Supervisor arm)
21957        // surfaces the M3 mesh diagnostic first through the compound
21958        // gate. Pins the pre-fold layout wire-up's canonical
21959        // diagnostic sequence (mesh → supervisor → servico) as a
21960        // property of the substrate primitive rather than a
21961        // convention of the layout call site. A silent reordering
21962        // regression at the primitive would surface here as a
21963        // wrong-variant match before landing at a downstream
21964        // consumer's diagnostic-ordering expectation.
21965        use crate::aplicacao::Membro;
21966        use crate::supervisor::RestartStrategy;
21967        let mut c = bare_servico_fixture("demo");
21968        c.membros = vec![Membro {
21969            caixa: "cart".into(),
21970            versao: "^0.1".into(),
21971        }];
21972        c.estrategia = Some(RestartStrategy::OneForOne);
21973        let err = c.validate_kind_slot_coherence().unwrap_err();
21974        assert!(
21975            matches!(err, crate::LayoutError::MeshSlotsOnNonAplicacao { .. }),
21976            "expected MeshSlotsOnNonAplicacao to fire before \
21977             SupervisorSlotsOnNonSupervisor under the canonical \
21978             mesh → supervisor → servico order, got {err:?}",
21979        );
21980    }
21981
21982    #[test]
21983    fn validate_kind_slot_coherence_supervisor_arm_fires_before_servico_arm() {
21984        // Cross-arm ordering pin between the second and third arms
21985        // of the fold: a fixture carrying BOTH a declared
21986        // supervisor-tree slot (`:estrategia`) AND a declared M2 slot
21987        // (`:limits`) on a kind foreign to both (a `:kind Biblioteca`
21988        // here — foreign to both the Supervisor and the Servico
21989        // arms) surfaces the supervisor-tree diagnostic first
21990        // through the compound gate. Together with the peer
21991        // `_mesh_arm_fires_before_supervisor_arm` pin above this
21992        // pins the whole three-arm canonical order (mesh →
21993        // supervisor → servico) at the substrate primitive.
21994        use crate::limits::LimitsSpec;
21995        use crate::supervisor::RestartStrategy;
21996        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21997        c.kind = CaixaKind::Biblioteca;
21998        c.estrategia = Some(RestartStrategy::OneForOne);
21999        c.limits = Some(LimitsSpec {
22000            memory: Some(64 * 1024 * 1024),
22001            fuel: None,
22002            wall_clock: None,
22003            cpu: None,
22004        });
22005        let err = c.validate_kind_slot_coherence().unwrap_err();
22006        assert!(
22007            matches!(
22008                err,
22009                crate::LayoutError::SupervisorSlotsOnNonSupervisor { .. }
22010            ),
22011            "expected SupervisorSlotsOnNonSupervisor to fire before \
22012             ServicoSlotsOnNonServico under the canonical mesh → \
22013             supervisor → servico order, got {err:?}",
22014        );
22015    }
22016
22017    #[test]
22018    fn validate_kind_slot_coherence_accepts_owner_kind_on_every_arm() {
22019        // Positive control on the identity-element arm: the owner
22020        // kind of each typed-slot family passes the compound gate
22021        // even when it declares the full slot set that family owns.
22022        // Aplicacao with `:membros` populated passes the mesh arm;
22023        // Supervisor with `:estrategia` populated passes the
22024        // supervisor arm; Servico with `:limits` populated passes
22025        // the servico arm. Pins the fold's identity element on
22026        // every owner kind — a silent regression that dropped the
22027        // paired `!kind().is_<owner>()` short-circuit guard would
22028        // surface here as a false-positive rejection of every
22029        // native-slot declaration. Peer with the
22030        // `validate_<kind>_shape_accepts_non_<kind>_kind` identity-
22031        // element pins on the sibling per-Caixa compound gates.
22032        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
22033        use crate::limits::LimitsSpec;
22034        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
22035
22036        let mut apli = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22037        apli.kind = CaixaKind::Aplicacao;
22038        apli.bibliotecas = vec![];
22039        apli.membros = vec![Membro {
22040            caixa: "cart".into(),
22041            versao: "^0.1".into(),
22042        }];
22043        apli.placement = Some(Placement {
22044            estrategia: PlacementStrategy::SingleNode,
22045            clusters: vec!["rio".into()],
22046            shard_key: None,
22047            affinity: None,
22048        });
22049        apli.validate_kind_slot_coherence().expect(
22050            "an :kind Aplicacao caixa with declared M3 mesh slots must \
22051             pass the compound gate — Aplicacao is the mesh-slot family's \
22052             owner kind and the fold's identity element on that arm",
22053        );
22054
22055        let mut sup = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22056        sup.kind = CaixaKind::Supervisor;
22057        sup.bibliotecas = vec![];
22058        sup.estrategia = Some(RestartStrategy::OneForOne);
22059        sup.children = vec![ChildSpec {
22060            caixa: "worker".into(),
22061            versao: "^0.1".into(),
22062            restart: RestartPolicy::Permanent,
22063        }];
22064        sup.validate_kind_slot_coherence().expect(
22065            "an :kind Supervisor caixa with declared supervisor-tree slots \
22066             must pass the compound gate — Supervisor is the \
22067             supervisor-slot family's owner kind and the fold's identity \
22068             element on that arm",
22069        );
22070
22071        let mut svc = bare_servico_fixture("svc");
22072        svc.limits = Some(LimitsSpec {
22073            memory: Some(64 * 1024 * 1024),
22074            fuel: None,
22075            wall_clock: None,
22076            cpu: None,
22077        });
22078        svc.validate_kind_slot_coherence().expect(
22079            "an :kind Servico caixa with declared M2 slots must pass the \
22080             compound gate — Servico is the M2-slot family's owner kind \
22081             and the fold's identity element on that arm",
22082        );
22083    }
22084
22085    #[test]
22086    fn validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind() {
22087        // Positive control on the second identity-element arm: a
22088        // bare caixa (no declared typed slots) passes the compound
22089        // gate on every kind. Pins the fold's identity element on
22090        // the empty-slot axis — the paired `Vec::is_empty` short-
22091        // circuit guard fires before the wrap dispatch on all three
22092        // arms, so a bare caixa of any kind surfaces no diagnostic.
22093        // A silent regression that dropped the emptiness guard
22094        // would surface here as a false-positive rejection of every
22095        // no-slot caixa across the whole kind axis.
22096        for kind in CaixaKind::ALL {
22097            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22098            c.kind = *kind;
22099            c.bibliotecas = vec![];
22100            c.validate_kind_slot_coherence().unwrap_or_else(|err| {
22101                panic!(
22102                    "a bare :kind {kind:?} caixa (no declared typed slots) \
22103                     must pass the compound gate — the fold's identity \
22104                     element on the empty-slot axis is the paired \
22105                     Vec::is_empty short-circuit guard, got {err:?}",
22106                )
22107            });
22108        }
22109    }
22110
22111    #[test]
22112    fn run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator() {
22113        // Fail-before-pass-after identity-element pin on the owner-kind
22114        // arm of the substrate primitive: on a caixa whose kind IS the
22115        // owner of the family named by `is_owner`, the primitive
22116        // short-circuits before dispatching `accumulator` — pinned here
22117        // by a poison-pill accumulator that panics on call. If a
22118        // regression drops the `is_owner` short-circuit and always
22119        // invokes the accumulator, the poison panic surfaces here
22120        // rather than a spurious pass. Byte-equal to the pre-lift
22121        // `if !self.kind().is_<owner>() { … }` outer guard's
22122        // short-circuit at the pre-fold layout call site.
22123        let c = bare_servico_fixture("demo");
22124        c.run_kind_owned_slot_family_gate(
22125            CaixaKind::is_servico,
22126            |_| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking accumulator on the owner kind"),
22127            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on the owner kind"),
22128        )
22129        .expect(
22130            "the owner kind of a slot family must pass the substrate \
22131             primitive as the fold's identity element on the outer \
22132             is_owner guard, without invoking accumulator or wrap",
22133        );
22134    }
22135
22136    #[test]
22137    fn run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap() {
22138        // Fail-before-pass-after identity-element pin on the empty-
22139        // accumulator arm: on a non-owner kind whose per-family
22140        // accumulator yields no declared slot, the primitive short-
22141        // circuits before dispatching `wrap` — pinned here by a
22142        // poison-pill wrap that panics on call. Byte-equal to the
22143        // pre-lift `if !<slots>.is_empty() { … }` inner emptiness
22144        // guard's short-circuit at the pre-fold layout call site.
22145        let c = bare_servico_fixture("demo");
22146        c.run_kind_owned_slot_family_gate(
22147            CaixaKind::is_aplicacao,
22148            Caixa::declared_mesh_slots,
22149            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on an empty accumulator"),
22150        )
22151        .expect(
22152            "a non-owner kind carrying no declared slot in the family \
22153             must pass the substrate primitive as the fold's identity \
22154             element on the inner emptiness guard, without invoking \
22155             wrap",
22156        );
22157    }
22158
22159    #[test]
22160    fn run_kind_owned_slot_family_gate_non_owner_non_empty_wraps_verbatim() {
22161        // Equivalence pin on the refusal arm: on a non-owner kind
22162        // whose accumulator yields a non-empty slot list, the primitive
22163        // returns the caller-supplied wrap byte-equal to the direct
22164        // ctor dispatch on the same `(caixa, slots)` pair. Pins the
22165        // three-argument route through — `is_owner` fires false, the
22166        // accumulator produces the slot list, and the wrap ctor
22167        // receives verbatim what a direct dispatch would receive.
22168        // Sibling of the peer per-arm equivalence pins on
22169        // [`Caixa::validate_kind_slot_coherence`].
22170        use crate::aplicacao::Membro;
22171        let mut c = bare_servico_fixture("demo");
22172        c.membros = vec![Membro {
22173            caixa: "cart".into(),
22174            versao: "^0.1".into(),
22175        }];
22176        let via_primitive = c
22177            .run_kind_owned_slot_family_gate(
22178                CaixaKind::is_aplicacao,
22179                Caixa::declared_mesh_slots,
22180                crate::LayoutError::mesh_slots_on_non_aplicacao,
22181            )
22182            .unwrap_err();
22183        let via_direct =
22184            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22185        assert_eq!(
22186            via_primitive, via_direct,
22187            "Caixa::run_kind_owned_slot_family_gate must route the \
22188             non-owner-kind + non-empty-accumulator arm through the \
22189             caller-supplied wrap byte-equal to the direct ctor \
22190             dispatch on the same (caixa, slots) pair",
22191        );
22192    }
22193
22194    #[test]
22195    fn validate_kind_slot_coherence_routes_each_arm_through_run_kind_owned_slot_family_gate() {
22196        // Cross-primitive routing pin: every arm of the compound gate
22197        // [`Caixa::validate_kind_slot_coherence`] routes through the
22198        // substrate primitive [`Caixa::run_kind_owned_slot_family_gate`]
22199        // on its `(is_owner, accumulator, wrap)` triple. A silent
22200        // regression that de-folded one arm and re-inlined the four-
22201        // line block would surface here as a mismatch between the
22202        // compound-gate error and the direct-primitive-dispatch error
22203        // on the same fixture. Sibling of the peer
22204        // `probe_declared_entries_routes_miss_arm_through_probe_declared_entry`
22205        // cross-primitive routing pin on the layout-pipeline
22206        // existence-probe axis.
22207        use crate::aplicacao::Membro;
22208        use crate::limits::LimitsSpec;
22209        use crate::supervisor::RestartStrategy;
22210
22211        // Mesh arm — non-Aplicacao carrying a declared M3 slot.
22212        let mut mesh = bare_servico_fixture("demo");
22213        mesh.membros = vec![Membro {
22214            caixa: "cart".into(),
22215            versao: "^0.1".into(),
22216        }];
22217        let via_compound = mesh.validate_kind_slot_coherence().unwrap_err();
22218        let via_primitive = mesh
22219            .run_kind_owned_slot_family_gate(
22220                CaixaKind::is_aplicacao,
22221                Caixa::declared_mesh_slots,
22222                crate::LayoutError::mesh_slots_on_non_aplicacao,
22223            )
22224            .unwrap_err();
22225        assert_eq!(
22226            via_compound, via_primitive,
22227            "validate_kind_slot_coherence's mesh arm must route \
22228             byte-equal through the run_kind_owned_slot_family_gate \
22229             substrate primitive",
22230        );
22231
22232        // Supervisor arm — non-Supervisor carrying a declared
22233        // supervisor-tree slot on a kind foreign to both the Aplicacao
22234        // arm and this one.
22235        let mut sup = bare_servico_fixture("demo");
22236        sup.estrategia = Some(RestartStrategy::OneForOne);
22237        let via_compound = sup.validate_kind_slot_coherence().unwrap_err();
22238        let via_primitive = sup
22239            .run_kind_owned_slot_family_gate(
22240                CaixaKind::is_supervisor,
22241                Caixa::declared_supervisor_slots,
22242                crate::LayoutError::supervisor_slots_on_non_supervisor,
22243            )
22244            .unwrap_err();
22245        assert_eq!(
22246            via_compound, via_primitive,
22247            "validate_kind_slot_coherence's supervisor arm must route \
22248             byte-equal through the run_kind_owned_slot_family_gate \
22249             substrate primitive",
22250        );
22251
22252        // Servico arm — non-Servico carrying a declared M2 slot on a
22253        // kind foreign to every prior arm (Biblioteca — foreign to
22254        // both the Aplicacao mesh arm and the Supervisor supervisor
22255        // arm and the Servico M2 arm).
22256        let mut svc = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22257        svc.kind = CaixaKind::Biblioteca;
22258        svc.limits = Some(LimitsSpec {
22259            memory: Some(64 * 1024 * 1024),
22260            fuel: None,
22261            wall_clock: None,
22262            cpu: None,
22263        });
22264        let via_compound = svc.validate_kind_slot_coherence().unwrap_err();
22265        let via_primitive = svc
22266            .run_kind_owned_slot_family_gate(
22267                CaixaKind::is_servico,
22268                Caixa::declared_servico_slots,
22269                crate::LayoutError::servico_slots_on_non_servico,
22270            )
22271            .unwrap_err();
22272        assert_eq!(
22273            via_compound, via_primitive,
22274            "validate_kind_slot_coherence's servico arm must route \
22275             byte-equal through the run_kind_owned_slot_family_gate \
22276             substrate primitive",
22277        );
22278    }
22279
22280    #[test]
22281    fn validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate() {
22282        // Fail-before-pass-after per-arm equivalence pin on the
22283        // Supervisor no-code arm of the reciprocal code-surface
22284        // fold: a `:kind Supervisor` caixa carrying a declared
22285        // `:bibliotecas` entry (the smallest possible code-surface
22286        // declaration on a no-code kind) surfaces the same
22287        // [`crate::LayoutError::SupervisorOwnsCode`] variant
22288        // through both the compound gate
22289        // [`Caixa::validate_no_code_kind_coherence`] and the
22290        // standalone constructor
22291        // [`crate::LayoutError::supervisor_owns_code`]. Pins the
22292        // fold — a silent regression that de-folded the Supervisor
22293        // arm would surface here as a mismatch between the two
22294        // dispatches. Sibling in shape to the peer
22295        // `validate_kind_slot_coherence_folds_supervisor_arm_matches_gate`
22296        // per-arm equivalence pin on the cross-family
22297        // typed-slot-coherence fold.
22298        let mut c = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22299        c.kind = CaixaKind::Supervisor;
22300        c.bibliotecas = vec!["lib/sup.lisp".into()];
22301        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22302        let via_standalone = crate::LayoutError::supervisor_owns_code(&c);
22303        assert_eq!(
22304            via_method, via_standalone,
22305            "Caixa::validate_no_code_kind_coherence must surface the \
22306             Supervisor arm's diagnostic byte-equal to the standalone \
22307             LayoutError::supervisor_owns_code ctor",
22308        );
22309    }
22310
22311    #[test]
22312    fn validate_no_code_kind_coherence_folds_aplicacao_arm_matches_gate() {
22313        // Per-arm equivalence pin on the Aplicacao no-code arm —
22314        // the sibling of the Supervisor arm on the code-surface
22315        // fold. A `:kind Aplicacao` caixa carrying a declared
22316        // `:exe` entry surfaces the same
22317        // [`crate::LayoutError::AplicacaoOwnsCode`] variant through
22318        // both dispatches. Uses the `:exe` code-surface axis (a
22319        // second axis distinct from the Supervisor arm's
22320        // `:bibliotecas` fixture) so the three per-arm pins
22321        // collectively exercise every arm of the `has_code`
22322        // disjunction (`:bibliotecas || :exe || :servicos`).
22323        let mut c = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22324        c.kind = CaixaKind::Aplicacao;
22325        c.bibliotecas = vec![];
22326        c.exe = vec!["exe/app".into()];
22327        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22328        let via_standalone = crate::LayoutError::aplicacao_owns_code(&c);
22329        assert_eq!(
22330            via_method, via_standalone,
22331            "Caixa::validate_no_code_kind_coherence must surface the \
22332             Aplicacao arm's diagnostic byte-equal to the standalone \
22333             LayoutError::aplicacao_owns_code ctor",
22334        );
22335    }
22336
22337    #[test]
22338    fn validate_no_code_kind_coherence_folds_acao_arm_matches_gate() {
22339        // Per-arm equivalence pin on the Acao no-code arm — the
22340        // third and last arm on the code-surface fold. A `:kind
22341        // Acao` caixa carrying a declared `:servicos` entry
22342        // surfaces the same [`crate::LayoutError::AcaoOwnsCode`]
22343        // variant through both dispatches. Uses the `:servicos`
22344        // code-surface axis (the third distinct axis of the
22345        // `has_code` disjunction) so the three per-arm pins
22346        // collectively cover every arm of the code-surface
22347        // disjunction plus every no-code kind of the arm
22348        // dispatch.
22349        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
22350        c.kind = CaixaKind::Acao;
22351        c.bibliotecas = vec![];
22352        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22353        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22354        let via_standalone = crate::LayoutError::acao_owns_code(&c);
22355        assert_eq!(
22356            via_method, via_standalone,
22357            "Caixa::validate_no_code_kind_coherence must surface the \
22358             Acao arm's diagnostic byte-equal to the standalone \
22359             LayoutError::acao_owns_code ctor",
22360        );
22361    }
22362
22363    #[test]
22364    fn validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis() {
22365        // Positive control on the code-owning-kind identity
22366        // element: each of the three code-owning kinds
22367        // (`Biblioteca` owning `:bibliotecas`, `Binario` owning
22368        // `:exe`, `Servico` owning `:servicos`) passes the
22369        // compound gate cleanly when it declares its native code
22370        // surface. Pins the fold's second identity element — the
22371        // paired per-arm `is_<no-code-kind>()` short-circuit
22372        // fires on every code-owning kind, so a caixa with any
22373        // native code declaration on its owner kind surfaces no
22374        // diagnostic. A silent regression that dropped the paired
22375        // `is_<no-code-kind>()` short-circuit guard on any arm
22376        // would surface here as a false-positive rejection of the
22377        // corresponding owner kind. Peer with the
22378        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22379        // identity-element pin on the sibling cross-family fold.
22380        let mut bib = Caixa::from_lisp(&Caixa::template("bib")).unwrap();
22381        bib.kind = CaixaKind::Biblioteca;
22382        bib.bibliotecas = vec!["lib/bib.lisp".into()];
22383        bib.validate_no_code_kind_coherence().expect(
22384            "a :kind Biblioteca caixa with declared :bibliotecas must pass \
22385             the compound gate — Biblioteca owns the :bibliotecas code surface",
22386        );
22387
22388        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22389        bin.kind = CaixaKind::Binario;
22390        bin.bibliotecas = vec![];
22391        bin.exe = vec!["exe/bin".into()];
22392        bin.validate_no_code_kind_coherence().expect(
22393            "a :kind Binario caixa with declared :exe must pass the compound \
22394             gate — Binario owns the :exe code surface",
22395        );
22396
22397        let svc = bare_servico_fixture("svc");
22398        svc.validate_no_code_kind_coherence().expect(
22399            "a :kind Servico caixa with declared :servicos must pass the \
22400             compound gate — Servico owns the :servicos code surface",
22401        );
22402    }
22403
22404    #[test]
22405    fn validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22406        // Positive control on the has-no-code identity element:
22407        // a bare caixa (no declared code) passes the compound
22408        // gate on every kind — including the three no-code kinds
22409        // that would otherwise fire an OwnsCode diagnostic. Pins
22410        // the fold's first identity element — the paired
22411        // `!has_code` short-circuit fires before every per-arm
22412        // wrap dispatch, so a bare caixa of any kind surfaces no
22413        // diagnostic. A silent regression that dropped the
22414        // has_code guard would surface here as a false-positive
22415        // rejection of every no-code kind that declares no code.
22416        // Peer with the
22417        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22418        // identity-element pin on the sibling cross-family fold.
22419        for kind in CaixaKind::ALL {
22420            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22421            c.kind = *kind;
22422            c.bibliotecas = vec![];
22423            c.exe = vec![];
22424            c.servicos = vec![];
22425            c.validate_no_code_kind_coherence().unwrap_or_else(|err| {
22426                panic!(
22427                    "a bare :kind {kind:?} caixa (no declared code) must pass \
22428                     the compound gate — the fold's first identity element is \
22429                     the paired !has_code short-circuit, got {err:?}",
22430                )
22431            });
22432        }
22433    }
22434
22435    #[test]
22436    fn validate_ci_kind_coherence_folds_arm_matches_gate() {
22437        // Fail-before-pass-after per-arm equivalence pin on the
22438        // `:ci`-on-non-`Acao` arm: a `:kind Biblioteca` caixa
22439        // (the smallest non-`Acao` kind) carrying a declared
22440        // `:ci` slot surfaces the same
22441        // [`crate::LayoutError::CiOnNonAcao`] variant through the
22442        // compound gate [`Caixa::validate_ci_kind_coherence`] and
22443        // an inlined struct-literal wrap carrying `caixa.nome()`
22444        // + `caixa.kind()` verbatim. Pins the fold — a silent
22445        // regression that de-folded the arm would surface here as
22446        // a mismatch between the two dispatches. Sibling in shape
22447        // to the peer
22448        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22449        // per-arm equivalence pin on the reciprocal
22450        // code-surface fold.
22451        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22452        c.kind = CaixaKind::Biblioteca;
22453        c.ci = Some(canteiro_types::CiRun {
22454            workspace: "pleme-io".into(),
22455            repo: "caixa".into(),
22456            nodes: vec![],
22457        });
22458        let via_method = c.validate_ci_kind_coherence().unwrap_err();
22459        let via_standalone = crate::LayoutError::CiOnNonAcao {
22460            caixa: c.nome().to_string(),
22461            kind: c.kind(),
22462        };
22463        assert_eq!(
22464            via_method, via_standalone,
22465            "Caixa::validate_ci_kind_coherence must surface the \
22466             :ci-on-non-Acao arm's diagnostic byte-equal to a \
22467             LayoutError::CiOnNonAcao struct literal carrying the \
22468             caixa's nome + kind",
22469        );
22470    }
22471
22472    #[test]
22473    fn validate_ci_kind_coherence_fold_names_offending_kind_on_every_non_acao_kind() {
22474        // Exhaustive per-kind sweep on the non-`Acao` arm: for each
22475        // of the five non-`Acao` kinds
22476        // (`Biblioteca` / `Binario` / `Servico` / `Supervisor` /
22477        // `Aplicacao`), a caixa carrying a declared `:ci` slot
22478        // surfaces the [`crate::LayoutError::CiOnNonAcao`]
22479        // variant naming the offending kind verbatim. A silent
22480        // regression that mistyped one arm's kind-projection
22481        // (e.g. always threading `CaixaKind::Biblioteca` regardless
22482        // of the caixa's actual kind) would surface here as a
22483        // mismatch on every kind past the first. Peer of the
22484        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22485        // exhaustive-sweep pin on the sibling code-surface fold.
22486        for kind in CaixaKind::ALL {
22487            if kind.is_acao() {
22488                continue;
22489            }
22490            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22491            c.kind = *kind;
22492            c.ci = Some(canteiro_types::CiRun {
22493                workspace: "pleme-io".into(),
22494                repo: "caixa".into(),
22495                nodes: vec![],
22496            });
22497            let err = c.validate_ci_kind_coherence().unwrap_err();
22498            match err {
22499                crate::LayoutError::CiOnNonAcao {
22500                    caixa: got_caixa,
22501                    kind: got_kind,
22502                } => {
22503                    assert_eq!(
22504                        got_caixa,
22505                        c.nome(),
22506                        "CiOnNonAcao must name the offending caixa's nome verbatim on kind {kind:?}",
22507                    );
22508                    assert_eq!(
22509                        got_kind, *kind,
22510                        "CiOnNonAcao must name the offending kind verbatim on kind {kind:?}",
22511                    );
22512                }
22513                other => panic!(
22514                    "expected CiOnNonAcao on :kind {kind:?} with declared :ci, got {other:?}",
22515                ),
22516            }
22517        }
22518    }
22519
22520    #[test]
22521    fn validate_ci_kind_coherence_accepts_acao_on_every_ci_shape() {
22522        // Positive control on the owner-kind identity element: an
22523        // `:kind Acao` caixa passes the coherence gate cleanly on
22524        // every `:ci` shape — the arm's paired
22525        // `!kind().is_acao()` short-circuit fires before the
22526        // dispatch, so the fold surfaces no diagnostic even on
22527        // fixtures whose `:ci` would fail the peer
22528        // [`Self::validate_acao_shape`] decompose gate (a
22529        // duplicate-node fixture, an unknown-dep fixture, a
22530        // cyclic fixture). Pins the fold's first identity element
22531        // — a silent regression that dropped the paired
22532        // `!kind().is_acao()` short-circuit guard would surface
22533        // here as a false-positive rejection of every `Acao`
22534        // caixa. Peer with the
22535        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
22536        // identity-element pin on the sibling code-surface fold.
22537        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22538        c.kind = CaixaKind::Acao;
22539        c.bibliotecas = vec![];
22540        c.ci = Some(canteiro_types::CiRun {
22541            workspace: "pleme-io".into(),
22542            repo: "caixa".into(),
22543            nodes: vec![],
22544        });
22545        c.validate_ci_kind_coherence().expect(
22546            "a :kind Acao caixa with declared :ci must pass the compound \
22547             coherence gate — Acao is the :ci-owning kind (a malformed \
22548             :ci on Acao surfaces via validate_acao_shape's decompose gate, \
22549             not via this kind-coherence gate)",
22550        );
22551    }
22552
22553    #[test]
22554    fn validate_ci_kind_coherence_accepts_absent_ci_on_every_kind() {
22555        // Positive control on the absent-`:ci` identity element:
22556        // a caixa with `ci = None` passes the coherence gate on
22557        // every kind — including `Acao`, whose absent `:ci`
22558        // fails a separate presence gate ([`crate::LayoutError::MissingCi`])
22559        // downstream at the layout altitude, not this coherence
22560        // gate. Pins the fold's second identity element — the
22561        // paired `ci().is_some()` short-circuit fires before every
22562        // per-arm dispatch, so a caixa with no declared `:ci`
22563        // surfaces no coherence diagnostic. A silent regression
22564        // that dropped the paired `ci().is_some()` short-circuit
22565        // would surface here as a false-positive rejection on
22566        // every non-`Acao` kind. Peer with the
22567        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22568        // identity-element pin on the sibling code-surface fold.
22569        for kind in CaixaKind::ALL {
22570            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22571            c.kind = *kind;
22572            c.ci = None;
22573            c.validate_ci_kind_coherence().unwrap_or_else(|err| {
22574                panic!(
22575                    "a :kind {kind:?} caixa with no declared :ci must pass \
22576                     the compound coherence gate — the fold's second identity \
22577                     element is the paired ci().is_some() short-circuit, got \
22578                     {err:?}",
22579                )
22580            });
22581        }
22582    }
22583
22584    #[test]
22585    fn validate_foreign_code_kind_coherence_folds_arm_matches_gate() {
22586        // Fail-before-pass-after equivalence pin on the compound
22587        // foreign-code-slot coherence fold: a `:kind Servico` caixa
22588        // carrying a declared `:exe` entry (the smallest possible
22589        // foreign-code-slot declaration on a code-running kind that
22590        // is not its owner — Servico owns `:servicos`, not `:exe`)
22591        // surfaces the same [`crate::LayoutError::ForeignCodeSlot`]
22592        // variant through both the compound gate
22593        // [`Caixa::validate_foreign_code_kind_coherence`] and the
22594        // standalone constructor
22595        // [`crate::LayoutError::foreign_code_slot`] dispatched on the
22596        // same `declared_foreign_code_slots` list. Pins the fold — a
22597        // silent regression that de-folded the arm would surface here
22598        // as a mismatch between the two dispatches. Sibling in shape
22599        // to the peer
22600        // `validate_kind_slot_coherence_folds_mesh_arm_matches_gate`
22601        // / `validate_ci_kind_coherence_folds_arm_matches_gate` /
22602        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22603        // per-arm equivalence pins on the sibling kind-coherence folds.
22604        let mut c = bare_servico_fixture("demo");
22605        c.exe = vec!["exe/foreign".into()];
22606        let via_method = c.validate_foreign_code_kind_coherence().unwrap_err();
22607        let via_standalone =
22608            crate::LayoutError::foreign_code_slot(&c, c.declared_foreign_code_slots());
22609        assert_eq!(
22610            via_method, via_standalone,
22611            "Caixa::validate_foreign_code_kind_coherence must surface the \
22612             foreign-code-slot diagnostic byte-equal to the standalone \
22613             LayoutError::foreign_code_slot ctor on the same \
22614             declared_foreign_code_slots list",
22615        );
22616    }
22617
22618    #[test]
22619    fn validate_foreign_code_kind_coherence_exe_arm_precedes_servicos_arm() {
22620        // Cross-arm ordering pin on the fold's accumulator: a fixture
22621        // carrying BOTH a declared `:exe` AND a declared `:servicos`
22622        // on a kind foreign to both (a `:kind Biblioteca` here —
22623        // foreign to both the Binario arm and the Servico arm)
22624        // surfaces `:exe` first in the `ForeignCodeSlot`'s slots
22625        // list. Pins the canonical `:exe` → `:servicos` diagnostic
22626        // order [`Caixa::declared_foreign_code_slots`] establishes,
22627        // as a property of the substrate primitive rather than an
22628        // implicit accumulator convention. A silent reordering
22629        // regression at the accumulator would surface here as a
22630        // wrong-first-slot list before landing at a downstream
22631        // consumer's diagnostic-ordering expectation.
22632        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22633        c.kind = CaixaKind::Biblioteca;
22634        c.exe = vec!["exe/demo".into()];
22635        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22636        let err = c.validate_foreign_code_kind_coherence().unwrap_err();
22637        let crate::LayoutError::ForeignCodeSlot { slots, .. } = &err else {
22638            panic!("expected ForeignCodeSlot variant, got {err:?}");
22639        };
22640        assert!(
22641            slots.starts_with(":exe"),
22642            "expected the :exe arm to precede the :servicos arm in the \
22643             ForeignCodeSlot slots list under the canonical :exe → :servicos \
22644             order, got slots = {slots:?}",
22645        );
22646        assert!(
22647            slots.contains(":servicos"),
22648            "expected the :servicos arm to also fire in the ForeignCodeSlot \
22649             slots list on a fixture carrying both foreign code surfaces, \
22650             got slots = {slots:?}",
22651        );
22652    }
22653
22654    #[test]
22655    fn validate_foreign_code_kind_coherence_accepts_native_slot_on_owner_kind() {
22656        // Positive control on the native-slot identity element: each
22657        // code-surface slot's owner kind passes the fold trivially
22658        // when it declares only its native code surface. `:kind
22659        // Binario` with a declared `:exe` and no `:servicos` passes
22660        // (the `!requires_exe()` guard short-circuits the arm inside
22661        // [`Caixa::declared_foreign_code_slots`], so the accumulator
22662        // returns empty); `:kind Servico` with a declared `:servicos`
22663        // and no `:exe` passes for the mirror reason. Pins the fold's
22664        // native-slot identity element on both arms — a silent
22665        // regression that dropped either per-arm `!requires_<slot>()`
22666        // predicate would surface here as a false-positive rejection
22667        // of every native-slot declaration on its owner kind. Peer
22668        // with the
22669        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22670        // identity-element pin on the sibling cross-family fold.
22671        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22672        bin.kind = CaixaKind::Binario;
22673        bin.bibliotecas = vec![];
22674        bin.exe = vec!["exe/bin".into()];
22675        bin.servicos = vec![];
22676        bin.validate_foreign_code_kind_coherence().expect(
22677            "a :kind Binario caixa with a declared native :exe and no \
22678             :servicos must pass the compound coherence gate — Binario is \
22679             the :exe slot's owner kind and the fold's native-slot identity \
22680             element on that arm",
22681        );
22682
22683        let mut svc = bare_servico_fixture("svc");
22684        svc.exe = vec![];
22685        svc.validate_foreign_code_kind_coherence().expect(
22686            "a :kind Servico caixa with a declared native :servicos and no \
22687             :exe must pass the compound coherence gate — Servico is the \
22688             :servicos slot's owner kind and the fold's native-slot identity \
22689             element on that arm",
22690        );
22691    }
22692
22693    #[test]
22694    fn validate_foreign_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22695        // Positive control on the empty-slot identity element: a
22696        // bare caixa (no declared `:exe` and no declared `:servicos`)
22697        // passes the compound gate on every kind. Pins the fold's
22698        // identity element on the empty-accumulator axis — the outer
22699        // `is_empty` short-circuit fires before the wrap dispatch on
22700        // every kind, so a bare caixa of any kind surfaces no
22701        // foreign-code-slot diagnostic. A silent regression that
22702        // dropped the emptiness guard would surface here as a
22703        // false-positive rejection of every no-code-slot caixa
22704        // across the whole kind axis. Peer with the
22705        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22706        // identity-element pin on the sibling cross-family fold.
22707        for kind in CaixaKind::ALL {
22708            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22709            c.kind = *kind;
22710            c.bibliotecas = vec![];
22711            c.exe = vec![];
22712            c.servicos = vec![];
22713            c.validate_foreign_code_kind_coherence()
22714                .unwrap_or_else(|err| {
22715                    panic!(
22716                        "a bare :kind {kind:?} caixa (no declared :exe / \
22717                         :servicos) must pass the compound coherence gate — \
22718                         the fold's identity element on the empty-accumulator \
22719                         axis is the outer Vec::is_empty short-circuit, got \
22720                         {err:?}",
22721                    )
22722                });
22723        }
22724    }
22725
22726    #[test]
22727    fn validate_required_kind_slot_folds_binario_arm_matches_gate() {
22728        // Fail-before-pass-after per-arm equivalence pin on the
22729        // `Binario` required-`:exe` arm of the required-slot fold:
22730        // a `:kind Binario` caixa carrying no declared `:exe` entry
22731        // surfaces the same
22732        // [`crate::LayoutError::BinarioWithoutExe`] variant through
22733        // both the compound gate
22734        // [`Caixa::validate_required_kind_slot`] and the standalone
22735        // constructor [`crate::LayoutError::binario_without_exe`].
22736        // Pins the fold — a silent regression that de-folded the
22737        // `Binario` arm would surface here as a mismatch between
22738        // the two dispatches. Sibling in shape to the peer
22739        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22740        // per-arm equivalence pin on the reciprocal code-surface
22741        // fold.
22742        let mut c = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22743        c.kind = CaixaKind::Binario;
22744        c.bibliotecas = vec![];
22745        c.exe = vec![];
22746        let via_method = c.validate_required_kind_slot().unwrap_err();
22747        let via_standalone = crate::LayoutError::binario_without_exe(&c);
22748        assert_eq!(
22749            via_method, via_standalone,
22750            "Caixa::validate_required_kind_slot must surface the \
22751             Binario arm's diagnostic byte-equal to the standalone \
22752             LayoutError::binario_without_exe ctor",
22753        );
22754    }
22755
22756    #[test]
22757    fn validate_required_kind_slot_folds_servico_arm_matches_gate() {
22758        // Per-arm equivalence pin on the `Servico` required-
22759        // `:servicos` arm — the sibling of the Binario arm on the
22760        // required-slot fold. A `:kind Servico` caixa carrying no
22761        // declared `:servicos` entry surfaces the same
22762        // [`crate::LayoutError::ServicoWithoutServicos`] variant
22763        // through both dispatches.
22764        let mut c = Caixa::from_lisp(&Caixa::template("svc")).unwrap();
22765        c.kind = CaixaKind::Servico;
22766        c.bibliotecas = vec![];
22767        c.servicos = vec![];
22768        let via_method = c.validate_required_kind_slot().unwrap_err();
22769        let via_standalone = crate::LayoutError::servico_without_servicos(&c);
22770        assert_eq!(
22771            via_method, via_standalone,
22772            "Caixa::validate_required_kind_slot must surface the \
22773             Servico arm's diagnostic byte-equal to the standalone \
22774             LayoutError::servico_without_servicos ctor",
22775        );
22776    }
22777
22778    #[test]
22779    fn validate_required_kind_slot_folds_acao_arm_matches_gate() {
22780        // Per-arm equivalence pin on the `Acao` required-`:ci` arm
22781        // — the third and last arm on the required-slot fold. A
22782        // `:kind Acao` caixa carrying no declared `:ci` slot
22783        // surfaces the same [`crate::LayoutError::MissingCi`]
22784        // variant through both dispatches. The three per-arm pins
22785        // collectively cover every required-slot axis and every
22786        // owner kind of the arm dispatch.
22787        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
22788        c.kind = CaixaKind::Acao;
22789        c.bibliotecas = vec![];
22790        c.ci = None;
22791        let via_method = c.validate_required_kind_slot().unwrap_err();
22792        let via_standalone = crate::LayoutError::missing_ci(&c);
22793        assert_eq!(
22794            via_method, via_standalone,
22795            "Caixa::validate_required_kind_slot must surface the \
22796             Acao arm's diagnostic byte-equal to the standalone \
22797             LayoutError::missing_ci ctor",
22798        );
22799    }
22800
22801    #[test]
22802    fn validate_required_kind_slot_accepts_owner_kind_with_required_slot_present() {
22803        // Positive control on the owner-kind-with-slot-present
22804        // identity element: each of the three owner kinds
22805        // (`Binario` with a non-empty `:exe`, `Servico` with a
22806        // non-empty `:servicos`, `Acao` with `ci = Some(_)`)
22807        // passes the compound gate cleanly when it declares its
22808        // required slot. Pins the fold's second identity element
22809        // — the paired `is_empty` / `is_none` short-circuit fires
22810        // on every owner kind whose required slot is present, so
22811        // a caixa with its native required slot surfaces no
22812        // diagnostic. A silent regression that dropped the paired
22813        // `is_empty` / `is_none` short-circuit guard on any arm
22814        // would surface here as a false-positive rejection of the
22815        // corresponding owner kind. Peer with the
22816        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
22817        // identity-element pin on the sibling code-surface fold.
22818        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22819        bin.kind = CaixaKind::Binario;
22820        bin.bibliotecas = vec![];
22821        bin.exe = vec!["exe/bin".into()];
22822        bin.validate_required_kind_slot().expect(
22823            "a :kind Binario caixa with declared :exe must pass the \
22824             required-slot gate — Binario's required slot is present",
22825        );
22826
22827        let svc = bare_servico_fixture("svc");
22828        svc.validate_required_kind_slot().expect(
22829            "a :kind Servico caixa with declared :servicos must pass \
22830             the required-slot gate — Servico's required slot is present",
22831        );
22832
22833        let acao = acao_fixture("acao");
22834        acao.validate_required_kind_slot().expect(
22835            "a :kind Acao caixa with declared :ci must pass the \
22836             required-slot gate — Acao's required slot is present",
22837        );
22838    }
22839
22840    #[test]
22841    fn validate_required_kind_slot_accepts_non_owner_kinds() {
22842        // Positive control on the non-owner-kind identity element:
22843        // every kind that is not one of the three owner kinds
22844        // (`Binario` / `Servico` / `Acao`) passes the compound gate
22845        // trivially — each per-arm predicate is
22846        // `self.kind().requires_<slot>()`, which returns `true`
22847        // only for the owner kind of that arm, so a non-owner kind
22848        // short-circuits every per-arm dispatch. Bibliotheca,
22849        // Supervisor, and Aplicacao are the three non-owner kinds
22850        // this pin exercises — none of them owns a required slot in
22851        // this fold (`Biblioteca`'s `:bibliotecas` default-file
22852        // fallback stays on the layout-side `MissingLib` fs-oracle
22853        // gate outside this fold; `Supervisor`'s `:children` and
22854        // `Aplicacao`'s `:membros` are carried by
22855        // [`CaixaKind::requires_children`] /
22856        // [`CaixaKind::requires_membros`] without a paired
22857        // layout-side wire-up). A silent regression that swapped a
22858        // per-arm predicate for a non-`requires_*` guard would
22859        // surface here as a false-positive rejection of the
22860        // corresponding non-owner kind. Peer with the
22861        // `validate_ci_kind_coherence_accepts_absent_ci_on_every_kind`
22862        // identity-element pin on the sibling `:ci` fold.
22863        for kind in CaixaKind::ALL {
22864            if kind.requires_exe() || kind.requires_servicos() || kind.requires_ci() {
22865                continue;
22866            }
22867            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22868            c.kind = *kind;
22869            c.bibliotecas = vec![];
22870            c.exe = vec![];
22871            c.servicos = vec![];
22872            c.ci = None;
22873            c.validate_required_kind_slot().unwrap_or_else(|err| {
22874                panic!(
22875                    "a :kind {kind:?} caixa (a non-owner kind on every \
22876                     required-slot arm) must pass the compound gate — the \
22877                     fold's identity element is the paired \
22878                     `self.kind().requires_<slot>()` short-circuit, got \
22879                     {err:?}",
22880                )
22881            });
22882        }
22883    }
22884}