Skip to main content

caixa_core/
manifest.rs

1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use tatara_lisp::DeriveTataraDomain;
5
6use thiserror::Error;
7
8use crate::{
9    CaixaKind, Dep,
10    behavior::BehaviorSpec,
11    dep::DepError,
12    limits::LimitsSpec,
13    render::{
14        PathShapeViolation, is_computeunit_yaml_extension, is_git_repo_url, is_lisp_extension,
15        is_sandboxed_relative_path,
16    },
17    supervisor::SupervisorSpec,
18    upgrade::UpgradeFromEntry,
19};
20
21/// Top-level manifest for a caixa (a tatara-lisp package).
22///
23/// Authored as `caixa.lisp`:
24///
25/// ```lisp
26/// (defcaixa
27///   :nome        "pangea-tatara-aws"
28///   :versao      "0.1.0"
29///   :kind        Biblioteca
30///   :edicao      "2026"
31///   :descricao   "AWS provider caixa for tatara-lisp"
32///   :repositorio "github:pleme-io/pangea-tatara-aws"
33///   :licenca     "MIT"
34///   :autores     ("pleme-io")
35///   :etiquetas   ("iac" "aws" "pangea")
36///   :deps        ((:nome "caixa-teia"    :versao "^0.1")
37///                 (:nome "iac-forge-ir"  :versao "^0.5"))
38///   :deps-dev    ((:nome "tatara-check"  :versao "*"))
39///   :bibliotecas ("lib/pangea-tatara-aws.lisp"))
40/// ```
41///
42/// Because `Caixa` derives [`tatara_lisp::domain::TataraDomain`], the manifest
43/// is parsed directly by the tatara-lisp compiler — an ill-formed manifest is
44/// a compile error, not a runtime error.
45#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[tatara(keyword = "defcaixa")]
48pub struct Caixa {
49    /// Package name — the canonical string used in `:deps`, the registry, and
50    /// the default lib/exe entry names.
51    pub nome: String,
52
53    /// Package version — a semver literal like `"0.1.0"`. Parsed lazily via
54    /// [`crate::CaixaVersion::parse`].
55    pub versao: String,
56
57    /// What this caixa produces. See [`CaixaKind`].
58    pub kind: CaixaKind,
59
60    /// Language edition — determines macro surface + compatibility flags.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub edicao: Option<String>,
63
64    /// Free-form description shown in the registry listing.
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub descricao: Option<String>,
67
68    /// Homepage or repo URL.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub repositorio: Option<String>,
71
72    /// SPDX license expression — `"MIT"`, `"Apache-2.0 OR MIT"`, etc.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub licenca: Option<String>,
75
76    /// Authors — free-form strings.
77    #[serde(default)]
78    pub autores: Vec<String>,
79
80    /// Topical tags used for registry search.
81    #[serde(default)]
82    pub etiquetas: Vec<String>,
83
84    /// Runtime dependencies.
85    #[serde(default)]
86    pub deps: Vec<Dep>,
87
88    /// Development-only dependencies (tests, lint, bench).
89    #[serde(default)]
90    pub deps_dev: Vec<Dep>,
91
92    /// Paths to executable entry points (relative to the package root).
93    /// Required when `:kind Binario`.
94    #[serde(default)]
95    pub exe: Vec<String>,
96
97    /// Paths to library entry points (relative to the package root).
98    /// First entry is the canonical `lib/<nome>.lisp`; when omitted under
99    /// `:kind Biblioteca`, the layout check expects `lib/<nome>.lisp`.
100    #[serde(default)]
101    pub bibliotecas: Vec<String>,
102
103    /// Paths to service manifests (relative to the package root).
104    /// Required when `:kind Servico`.
105    #[serde(default)]
106    pub servicos: Vec<String>,
107
108    // ── M2 typed-substrate extensions per theory/ABSORPTION-ROADMAP.md ──
109    //
110    // All four are optional + default to "absent"; existing caixas
111    // round-trip unchanged. Each maps onto a prior-art primitive named
112    // in theory/INSPIRATIONS.md:
113    //
114    //   :limits        — Lunatic per-process limits (§III.1)
115    //   :behavior      — OTP gen_server callbacks  (§II.3)
116    //   :upgrade-from  — OTP appup migration       (§II.4)
117    //   :estrategia    — OTP supervisor strategy   (§II.2 + §III.2)
118    //   :children      — OTP supervisor children    (§II.2 + §III.2)
119    //
120    // The supervisor slots are flat on Caixa (vs nested under a
121    // SupervisorSpec sub-form) to keep tatara-lisp authoring at one
122    // level of nesting; SupervisorSpec exists for validation +
123    // composition convenience (`Caixa::supervisor_view()`).
124    /// Lunatic-style per-process resource limits. None = unbounded.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub limits: Option<LimitsSpec>,
127
128    /// OTP-shaped behavior callbacks for Servico-kind caixas.
129    /// Authored as `(:on-init "..." :on-call "..." …)`.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub behavior: Option<BehaviorSpec>,
132
133    /// OTP appup — declarative upgrade instructions per prior version.
134    /// Empty list = no hot-upgrade path declared (caller falls back to
135    /// `:Restart` strategy).
136    #[serde(default)]
137    pub upgrade_from: Vec<UpgradeFromEntry>,
138
139    /// OTP supervisor strategy. Required when `:kind Supervisor`;
140    /// ignored otherwise.
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub estrategia: Option<crate::supervisor::RestartStrategy>,
143
144    /// Max restarts before the supervisor itself fails. Defaults via
145    /// SupervisorSpec at validation time.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub max_restarts: Option<u32>,
148
149    /// Sliding window for `max_restarts`. Authored as a duration
150    /// string (`"60s"`, `"5m"`).
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub restart_window: Option<String>,
153
154    /// Static children of a supervisor. Required for OneForOne /
155    /// OneForAll / RestForOne; must be empty for SimpleOneForOne.
156    #[serde(default)]
157    pub children: Vec<crate::supervisor::ChildSpec>,
158
159    // ── M3 Aplicacao slots (theory/MESH-COMPOSITION.md) ─────────────────
160    //
161    // Required when :kind Aplicacao; ignored otherwise.
162    // Composed into a typed AplicacaoSpec via Caixa::aplicacao_view().
163    /// Member Servicos that make up this Aplicacao. Each is a
164    /// caixa-name + version-constraint pair. Required for Aplicacao.
165    #[serde(default)]
166    pub membros: Vec<crate::aplicacao::Membro>,
167
168    /// WIT-typed inter-Servico contracts. Each `:de` and `:para`
169    /// must reference a name in `:membros`.
170    #[serde(default)]
171    pub contratos: Vec<crate::aplicacao::WitContract>,
172
173    /// Mesh-level policies (timeout, retries, circuit-breaker, mTLS,
174    /// rate-limit). Apply to every contrato unless overridden per-edge
175    /// in M4.
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub politicas: Option<crate::aplicacao::MeshPolicy>,
178
179    /// Placement strategy across the cluster fleet
180    /// (single-node | replicated | sharded).
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub placement: Option<crate::aplicacao::Placement>,
183
184    /// External entry point — gateway / ingress shape. Optional;
185    /// only for public Aplicacaos.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub entrada: Option<crate::aplicacao::Entrada>,
188
189    // ── Acao slot (CANTEIRO §7.1-C) ──────────────────────────────────────
190    //
191    // Required when :kind Acao; ignored otherwise (mirrors the M2/
192    // supervisor-tree/M3 slot triads above — a declared-but-foreign `:ci`
193    // is a `LayoutError::CiOnNonAcao` build error, not a silent drop).
194    /// Typed CI run — a repo's CI run as a set of typed nodes + their
195    /// dependency edges. Required for `:kind Acao`; validated (not
196    /// rendered) by the `caixa-actions` renderer via
197    /// `canteiro_types::decompose`. See `caixa-actions`' crate docs for
198    /// the M0 validate-only contract.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub ci: Option<canteiro_types::CiRun>,
201}
202
203/// Why reading a manifest into a [`Caixa`] failed.
204///
205/// Split from [`ManifestError`] (which reports a *parsed* manifest that is
206/// semantically wrong) because the two answer different questions, and the
207/// distinction is the whole point of this type: `ManifestError` means "your
208/// caixa is wrong", `LeituraError::DialetoEstrangeiro` means "this file is not
209/// a caixa".
210#[derive(Debug, thiserror::Error)]
211pub enum LeituraError {
212    /// The source is not readable as a `(defcaixa …)` package manifest — bad
213    /// syntax, a wrong head symbol, an unknown or mistyped slot.
214    ///
215    /// `#[source]`, not `#[error(transparent)]`. Transparent delegates
216    /// `source()` past the inner error to ITS source, which drops the
217    /// `LispError` off the cause chain — and `feira`'s
218    /// `load_caixa_parse_error_preserves_underlying_lisp_error_on_chain`
219    /// pins that a caller can `downcast_ref::<tatara_lisp::LispError>()`
220    /// through an anyhow context to read the typed payload. That pin caught
221    /// this exact regression when the variant first landed transparent.
222    #[error("{0}")]
223    Leitura(
224        #[source]
225        #[from]
226        tatara_lisp::LispError,
227    ),
228
229    /// The source IS a well-formed `(defcaixa …)` form, but of a different
230    /// declaration than this crate's.
231    ///
232    /// The variant that did not exist before, and whose absence is the defect.
233    /// A `(defcaixa :name "x" :ecosystem :go …)` used to reach the derive's
234    /// `parse_kwargs_strict` and come back as an unknown-keyword rejection —
235    /// byte-identical in shape to a typo in a real manifest. Measured over the
236    /// org checkout on 2026-07-31, that shape is the MAJORITY of the corpus, so
237    /// the confusing error was also the common one.
238    ///
239    /// Carrying the dialect means a consumer can branch on "not mine" without
240    /// re-parsing, and a census can count it. Every user-facing byte-string
241    /// (canonical keyword, one-line description, consuming crate) is a
242    /// projection of [`crate::dialeto::CaixaDialeto`] — the variant stores the
243    /// typed dialect and the `#[error]` template calls
244    /// [`CaixaDialeto::palavra_canonica`] /
245    /// [`CaixaDialeto::descricao`] / [`CaixaDialeto::consumidor`] on it, so
246    /// the three axes cannot silently diverge from the classification. Prior
247    /// to this closure the variant carried each accessor's return value as a
248    /// stored `&'static str` snapshot alongside `dialeto`, and the sole
249    /// constructor at [`Caixa::from_lisp`] filled all four fields — a caller
250    /// could construct `DialetoEstrangeiro { dialeto: Molde,
251    /// palavra_canonica: "defcaixa", … }` and every downstream consumer
252    /// (Display, ad-hoc audit, future JSON serialization) would silently
253    /// disagree with `dialeto.palavra_canonica() == "defmolde"`. The typed
254    /// enum owns the projections; the variant only carries the axis.
255    #[error(
256        "this is a `{palavra}` declaration ({desc}), read by \
257         {cons} — not a caixa-core package manifest. `defcaixa` is the \
258         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
259         are different declarations that shared one keyword until 2026-07-31",
260        palavra = dialeto.palavra_canonica(),
261        desc = dialeto.descricao(),
262        cons = dialeto.consumidor()
263    )]
264    DialetoEstrangeiro {
265        /// Which declaration this actually is. Sole authoritative axis;
266        /// every user-facing projection routes through
267        /// [`crate::dialeto::CaixaDialeto`]'s typed accessors so the four
268        /// axes cannot silently disagree.
269        dialeto: crate::dialeto::CaixaDialeto,
270    },
271
272    /// Not a manifest declaration at all.
273    #[error(transparent)]
274    Dialeto(#[from] crate::dialeto::DialetoError),
275}
276
277/// Substrate-canonical universal-axis per-[`Caixa`] `:licenca` SPDX-shaped
278/// license-expression fallback for the `Option<String>` `:licenca` slot —
279/// the `"MIT"` SPDX identifier every [`caixa-helm`]-rendered
280/// `lareira-<nome>` Helm chart's `README.md` `## License` section folds an
281/// author-omitted (`None`) `:licenca` slot through, extracted as a typed
282/// `pub const` so every substrate-side consumer that resolves "what license
283/// scalar does an author-omitted `:licenca` degrade onto?" reaches for
284/// exactly one substrate-primitive `&'static str`.
285///
286/// The `:licenca` fallback axis has one production consumer today — the
287/// [`caixa-helm`] `build_readme` fold at `caixa-helm/src/lib.rs`'s
288/// `caixa.licenca().unwrap_or(CAIXA_LICENCA_DEFAULT)` `README.md`
289/// `## License` section body — with three sibling caixa-core sites that
290/// cite the `"MIT"` fallback in prose (this crate's [`Caixa::licenca`]
291/// accessor's docstring, [`Self::validate_licenca`]'s docstring, and the
292/// [`ManifestError::LicencaEmpty`] `#[error]` template's user-facing text)
293/// all quoting the exact byte-string a future substrate-side rebrand of the
294/// fallback (a tightening to `"Apache-2.0"` as the substrate absorbs the
295/// wasm-component-model conventions the `wasi:*` WIT worlds already carry,
296/// a per-cluster license-default overlay the M4 CR materializer resolves
297/// per-CR, a promotion to the plain `Option<String>` byte-string into a
298/// richer `SpdxExpression` enum once the SPDX-expression parser lands per
299/// [`Self::validate_licenca`]'s docstring roadmap) would silently split
300/// against — the caixa-helm renderer would emit the new byte, the
301/// docstrings would still cite the prior byte, and every author who reads
302/// the accessor docstring before authoring would file a fresh
303/// `:licenca "MIT"` verbatim rather than defer to the substrate default,
304/// with the drift surfacing at chart-README-audit time far from the
305/// substrate rebrand commit.
306///
307/// Prior to this lift the sole production emitter (`build_readme`) carried
308/// an inline `"MIT"` byte literal at
309/// `caixa-helm/src/lib.rs:1018`'s `.unwrap_or("MIT")` fallback arm — one
310/// occurrence of the same load-bearing per-`Caixa` universal-axis
311/// SPDX-shaped license-expression convention as the four sibling caixa-core
312/// docstring citations, drift-prone by construction ahead of the second
313/// occurrence the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
314/// materializer's per-Aplicacao registry-annotation synthesis (the
315/// [`Self::validate_licenca`] roadmap already names the `Chart.yaml
316/// annotations["artifacthub.io/license"]` axis every registry-facing chart
317/// carries as the second consumer) will surface.
318///
319/// The `"MIT"` value pins the canonical CAIXA-SDLC §I license scaffold
320/// every `feira init`-emitted [`Self::template`] carries verbatim
321/// (`:licenca "MIT"`) and every substrate-side renderer fixture
322/// ([`caixa-helm`]'s `sample_caixa`, [`caixa-flux`]'s renderer fixtures,
323/// [`caixa-mesh`]'s renderer fixtures) seeds by construction, matching the
324/// pleme-io repo `LICENSE` header this workspace itself ships under. The
325/// alternatives an author declares explicitly (compound SPDX expressions
326/// like `"Apache-2.0 OR MIT"`, permissive-family peers like
327/// `"Apache-2.0"` / `"BSD-3-Clause"`, license-with-exception forms like
328/// `"Apache-2.0 WITH LLVM-exception"`) express deliberate license postures
329/// an author declares explicitly, never a posture an author-omitted slot
330/// should silently assume by default.
331///
332/// Lifted as a typed `pub const` so the substrate's chosen license
333/// fallback has exactly one source of truth on the `:licenca` fallback
334/// axis, on the same substrate-primitive lift discipline the peer
335/// per-`Caixa` load-bearing-scalar constants
336/// ([`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`],
337/// [`crate::version::DEFAULT_GIT_REMOTE`],
338/// [`crate::version::DEFAULT_PLEME_GIT_ORG`]) already carry on the sibling
339/// per-`Caixa` universal-axis publish-side convention surface, and the
340/// same discipline the sibling M2 per-supervisor default set carries
341/// end-to-end ([`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`],
342/// [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`],
343/// [`crate::supervisor::SUPERVISOR_RESTART_WINDOW_DEFAULT`],
344/// [`crate::supervisor::SUPERVISOR_CHILD_RESTART_DEFAULT`]) and the M3
345/// per-`:placement` default set already carries
346/// ([`crate::aplicacao::PLACEMENT_ESTRATEGIA_DEFAULT`]) on the paired
347/// M2 / M3 typed-slot-default axes. First typed default on the outer
348/// top-level [`Caixa`] universal-axis surface to converge onto the
349/// substrate-primitive-lift discipline the M2 / M3 typed-slot families
350/// already carry.
351pub const CAIXA_LICENCA_DEFAULT: &str = "MIT";
352
353impl Caixa {
354    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
355    ///
356    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
357    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
358    /// and who reads it, instead of an unknown-keyword rejection that reads as
359    /// "your manifest is broken".
360    ///
361    /// The ordering is load-bearing. Handing a foreign dialect to the derive
362    /// first and interpreting the failure afterwards would mean guessing from
363    /// an error message, and the guess would be wrong for every file whose
364    /// first unknown slot happens to be one both schemas could plausibly carry.
365    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
366        use tatara_lisp::domain::TataraDomain;
367        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
368        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
369
370        // Route the foreign-dialect rejection gate through the lifted
371        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
372        // typed predicate rather than the pre-lift hand-rolled three-arm
373        // `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
374        // literal — the `defmolde` declaration-family partition (the two-
375        // arity closure of [`crate::dialeto::CaixaDialeto::Molde`] and
376        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two arms
377        // whose sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
378        // projection already collapses onto `"defmolde"` and whose sibling
379        // [`crate::dialeto::CaixaDialeto::consumidor`] projection already
380        // collapses onto `"pleme-doc-gen"`) resolves through one dispatch
381        // on the substrate primitive. `Pacote` (the tatara-lisp package
382        // manifest this derive can parse) and `Desconhecido` (deliberately
383        // falls through to the derive rather than short-circuiting: a
384        // `(defcaixa …)` matching neither schema is most likely a genuine
385        // package manifest with a typo in `:nome`, and the derive's
386        // diagnostic — which names the offending keyword and suggests the
387        // nearest slot — is far better than anything this classifier
388        // could say) both return `false` from `is_molde_family()` and fall
389        // through to the derive. Only the typed dialect flows into the
390        // error — the three user-facing projections (canonical keyword,
391        // description, consumer) are read at Display time through
392        // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
393        // variant cannot carry a snapshot that drifts from
394        // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
395        // `descricao` / `consumidor`. A future fifth dialect the
396        // [`crate::dialeto`] module doc's "third dialect" hazard
397        // actualises that belongs to the `defmolde` family lands one
398        // match arm at [`crate::dialeto::CaixaDialeto::is_molde_family`]
399        // and this gate picks up the new arm by construction — the pre-
400        // lift wildcard `foreign =>` was compile-time-anonymous and would
401        // silently absorb any hypothetical fifth `defcaixa`-family arm as
402        // foreign; routing the partition through the typed predicate
403        // closes both drift surfaces.
404        let dialeto = crate::dialeto::classify_form(first)?;
405        if dialeto.is_molde_family() {
406            return Err(LeituraError::DialetoEstrangeiro { dialeto });
407        }
408
409        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
410    }
411
412    /// Register `Caixa` with the global tatara-lisp domain registry so
413    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
414    /// the registry (e.g. `tatara-check`).
415    ///
416    /// Returns the typed [`tatara_lisp::KeywordCollision`] on the second
417    /// (and every subsequent) call in the same process — one keyword,
418    /// one type, per process is a hard invariant of the upstream
419    /// registry, and a caller that hits it must fix its crate graph
420    /// rather than swallowing the error. Peer of the sibling per-crate
421    /// `register()` entry points at `caixa-flake/src/flake.rs`,
422    /// `caixa-fmt/src/lisp_config.rs`, `caixa-lacre/src/lock.rs`,
423    /// `caixa-lint/src/lisp_config.rs`, `caixa-resolver/src/lisp_config.rs`
424    /// — every substrate crate that owns a tatara-lisp keyword now
425    /// propagates the same typed error verbatim, so a downstream binary
426    /// that seeds the registry (`tatara-check`, the future LSP) reaches
427    /// for one shape at every call site.
428    ///
429    /// # Errors
430    ///
431    /// [`tatara_lisp::KeywordCollision`] when a peer type has already
432    /// claimed the `defcaixa` keyword in this process.
433    pub fn register() -> Result<(), tatara_lisp::KeywordCollision> {
434        tatara_lisp::domain::register::<Self>()
435    }
436
437    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
438    /// accessor every consumer of the top-level manifest's license axis
439    /// keys off — returns the author-declared `:licenca` byte-string
440    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
441    /// `Option<String>` storage. `None` when the slot is absent (the
442    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
443    /// fallback" shape [`Self::validate_licenca`] documents at
444    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
445    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
446    /// predicate too, so an authored-but-unset `:licenca` round-trips to
447    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
448    /// section structurally identical to one that omits the slot).
449    ///
450    /// The `:licenca` slot carries the universal-axis SPDX-expression
451    /// license identifier every kind of caixa emits under (CAIXA-SDLC
452    /// §I — the author-facing surface every `defcaixa` form supplies) —
453    /// the typed slot's `Option<String>` accept-set (empty-string
454    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
455    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
456    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
457    /// section (caixa-helm/src/lib.rs:962) and (through future
458    /// tightening documented at [`Self::validate_licenca`]) the
459    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
460    /// registry-facing chart carries. Every downstream consumer that
461    /// reads the license byte-string keys off this scalar (the
462    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
463    /// routes through `self.licenca.as_deref()`, the caixa-helm
464    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
465    /// the fallback off the `Option::is_none()` arm, every future
466    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
467    /// acknowledges).
468    ///
469    /// Prior to this lift the `.licenca` field was accessed inline at
470    /// two production sites — [`Self::validate_licenca`]'s
471    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
472    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
473    /// "MIT".into())` `README.md` `## License` fold — two open-coded
474    /// field-accesses that expressed no compile-time link back to the
475    /// typed slot. A future extension of the `:licenca` axis to a
476    /// richer author surface — a per-`:licenca` structured SPDX
477    /// expression parser + license-id allowlist (the future tightening
478    /// [`Self::validate_licenca`]'s docstring acknowledges), a
479    /// per-cluster license-default overlay the M4 CR materializer
480    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
481    /// unlisted caixa" arm), a promotion of the plain
482    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
483    /// once the SPDX-expression parser lands — would have had to be
484    /// threaded through both open-coded copies in lockstep or the
485    /// validate gate and the caixa-helm emit path would silently
486    /// disagree on which license a given [`Caixa`] resolves to (an
487    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
488    /// while the emit path silently rendered a stale `MIT` fallback,
489    /// or vice versa). Lifting the resolution to a typed method on the
490    /// substrate primitive means every downstream consumer of the
491    /// caixa's per-`Caixa` license surface reaches for exactly one
492    /// typed dispatch — the resolver's accept-set migrates as a unit
493    /// on any future axis addition.
494    ///
495    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
496    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
497    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
498    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
499    /// substrate primitive, thin projections at each consumer"
500    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
501    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
502    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
503    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
504    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
505    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
506    /// typed-slot atom axes, extended here to the outer top-level
507    /// `Caixa` universal-axis surface. Named `licenca()` to match the
508    /// storage field's name; the accessor's identity maps onto the
509    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
510    /// carries.
511    #[must_use]
512    pub const fn licenca(&self) -> Option<&str> {
513        match &self.licenca {
514            Some(s) => Some(s.as_str()),
515            None => None,
516        }
517    }
518
519    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
520    /// accessor every consumer of the top-level manifest's homepage /
521    /// source-of-truth axis keys off — returns the author-declared
522    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
523    /// from the typed slot's own `Option<String>` storage. `None` when
524    /// the slot is absent (the canonical "omit to defer to the renderer's
525    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
526    /// carries the `Option<String>` through verbatim so an author-omitted
527    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
528    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
529    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
530    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
531    /// fallback derived from `caixa.nome`).
532    ///
533    /// The `:repositorio` slot carries the universal-axis git-repo-URL
534    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
535    /// §I — the author-facing surface every `defcaixa` form supplies) —
536    /// the typed slot's `Option<String>` accept-set (empty-string
537    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
538    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
539    /// past the shared [`crate::render::is_git_repo_url`] predicate the
540    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
541    /// four load-bearing downstream consumers:
542    ///
543    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
544    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
545    ///     universal-axis identity gate wired at caixa-build time.
546    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
547    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
548    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
549    ///     that ingests the chart (ArtifactHub, chartmuseum,
550    ///     `helm search repo`) surfaces as the chart's canonical source-
551    ///     of-truth link.
552    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
553    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
554    ///     chart's `README.md` header link back to the source repo,
555    ///     which every author who inspects the rendered chart bundle
556    ///     lands at.
557    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
558    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
559    ///     the rendered `GitRepository` CR's `spec.url` field, which
560    ///     FluxCD's `source-controller` polls to reconcile the caixa's
561    ///     manifest bundle from git.
562    ///
563    /// Prior to this lift the `.repositorio` field was accessed inline
564    /// at four production sites — [`Self::validate_repositorio`]'s
565    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
566    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
567    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
568    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
569    /// `README.md` `## Source` fold, and the caixa-flux
570    /// `ClusterBundleOpts::for_caixa`
571    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
572    /// `GitRepository.spec.url` fold — four open-coded field-accesses
573    /// that expressed no compile-time link back to the typed slot. A
574    /// future extension of the `:repositorio` axis to a richer author
575    /// surface — a per-`:repositorio` structured
576    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
577    /// (the future tightening [`Self::validate_repositorio`]'s
578    /// docstring anticipates alongside the peer per-`:deps :fonte
579    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
580    /// materializer resolves per-CR (the "cluster policy rewrites
581    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
582    /// arm the private-registry story acknowledges), a promotion of
583    /// the plain `Option<String>` byte-string to a richer
584    /// `RepoUrl` enum discriminated on scheme — would have had to be
585    /// threaded through all four open-coded copies in lockstep or the
586    /// validate gate and the three emit paths would silently disagree
587    /// on which URL a given [`Caixa`] resolves to (an author's
588    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
589    /// while one of the emit paths silently rendered a stale URL, or
590    /// vice versa). Lifting the resolution to a typed method on the
591    /// substrate primitive means every downstream consumer of the
592    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
593    /// typed dispatch — the resolver's accept-set migrates as a unit on
594    /// any future axis addition.
595    ///
596    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
597    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
598    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
599    /// projection pattern this lift folds on. Same "one typed dispatch
600    /// on the substrate primitive, thin projections at each consumer"
601    /// discipline the peer per-`:placement`
602    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
603    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
604    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
605    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
606    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
607    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
608    /// typed-slot atom axes, extended here to the second outer top-level
609    /// `Caixa` universal-axis surface. Named `repositorio()` to match
610    /// the storage field's name; the accessor's identity maps onto the
611    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
612    /// carries.
613    #[must_use]
614    pub const fn repositorio(&self) -> Option<&str> {
615        match &self.repositorio {
616            Some(s) => Some(s.as_str()),
617            None => None,
618        }
619    }
620
621    /// Substrate-canonical per-`Caixa` **resolved-git-repo-URL** composer —
622    /// returns the caixa's canonical git-source-of-truth URL as an owned
623    /// [`String`], author-declared `:repositorio` byte-string verbatim on
624    /// the `Some` arm and the substrate's canonical pleme-org github URL
625    /// fallback ([`crate::DEFAULT_PLEME_GIT_ORG`] and [`Self::nome`]
626    /// interpolated into `https://github.com/<org>/<nome>`) on the
627    /// `None` arm. Every substrate-side consumer that resolves
628    /// "which git URL does this caixa's source live at?" reaches for
629    /// exactly one typed dispatch on the substrate primitive — the raw
630    /// `caixa.repositorio().map(str::to_owned).unwrap_or_else(|| format!(
631    /// "https://github.com/{org}/{nome}", org = DEFAULT_PLEME_GIT_ORG,
632    /// nome = caixa.nome()))` open-coded composition every prior caller
633    /// re-derived collapses onto one canonical arm.
634    ///
635    /// Distinct from [`Self::repositorio`] (`Option<&str>`, exposes the
636    /// author-omitted / author-declared partition to the caller) — this
637    /// accessor is the **resolved** URL surface, folding the fallback in
638    /// at the substrate-primitive boundary. Every consumer that keys off
639    /// the `Option::is_none()` discriminator (a [`Chart.yaml`] `home:`
640    /// field emit that must omit the field entirely on an author-omitted
641    /// `:repositorio`, per the [`Self::repositorio`] docstring's
642    /// documented four-consumer list) reaches through the raw
643    /// [`Self::repositorio`] `Option<&str>` accessor by construction — the
644    /// resolved-URL composer sits alongside it as the second projection
645    /// on the same underlying `:repositorio` slot rather than replacing
646    /// the raw accessor.
647    ///
648    /// The fallback branch is the exact byte-image of the prior inline
649    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url` composer at
650    /// caixa-flux/src/lib.rs:2080 — pinned by the sibling caixa-flux
651    /// byte-parity test
652    /// `cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`
653    /// against a future implementation of this method that reordered the
654    /// `format!` template arguments, migrated the `<org>` segment to a
655    /// different constant (the [`crate::DEFAULT_PLEME_GIT_ORG`] axis a
656    /// future substrate-side git-org migration may split off), or
657    /// silently absorbed the empty-string arm (a hypothetical
658    /// `Some("") → fallback` collapse the raw [`Self::repositorio`]
659    /// accessor's docstring explicitly rejects on the sibling raw
660    /// accessor).
661    ///
662    /// Peer of the sibling per-`&Caixa`-axis composed helpers
663    /// [`caixa-flux::cluster_bundle_for_caixa`] (06d52d7) on the sibling
664    /// substrate-side renderer surface — same "close the composed
665    /// substrate-primitive at one canonical arm on the single-`&Caixa`
666    /// dispatch, converge every prior open-coded caller onto the arm"
667    /// discipline extended onto the resolved-git-URL projection of the
668    /// per-`Caixa` `:repositorio` axis. Owns per-call [`String`]
669    /// allocation on both arms (the `Some` arm's `str::to_owned` and the
670    /// `None` arm's `format!`) — the by-value return matches every
671    /// downstream consumer's field-fill shape (the caixa-flux
672    /// `ClusterBundleOpts::git_url: String` field, every future
673    /// `Chart.yaml` `home:` fold's `Option<String>` field-fill on the
674    /// `Some` arm).
675    #[must_use]
676    pub fn canonical_git_url(&self) -> String {
677        self.repositorio().map_or_else(
678            || {
679                format!(
680                    "https://github.com/{org}/{nome}",
681                    org = crate::DEFAULT_PLEME_GIT_ORG,
682                    nome = self.nome(),
683                )
684            },
685            str::to_owned,
686        )
687    }
688
689    /// Substrate-canonical per-`Caixa` **resolved-publish-tag** composer —
690    /// returns the caixa's canonical Zig-style git-publish-tag as an owned
691    /// [`String`], derived by concatenating
692    /// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] with the typed
693    /// [`Self::versao`] byte-string on a single `format!` template.
694    /// Every substrate-side consumer that resolves "which git tag does this
695    /// caixa publish under?" reaches for exactly one typed dispatch on the
696    /// substrate primitive — the raw `format!("{prefix}{versao}", prefix =
697    /// caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao = caixa.versao())`
698    /// open-coded composition every prior caller re-derived collapses onto
699    /// one canonical arm.
700    ///
701    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
702    /// git-URL composer on the paired per-`Caixa` git-remote axis — same
703    /// "close the composed substrate-primitive at one canonical arm on the
704    /// single-`&Caixa` dispatch, converge every prior open-coded caller
705    /// onto the arm" discipline extended from the resolved-URL projection
706    /// of the per-`Caixa` `:repositorio` axis onto the resolved-tag
707    /// projection of the per-`Caixa` `:versao` axis. The two accessors
708    /// jointly close the pair of scalars every `FluxCD` `GitRepository` CR
709    /// keys off (`spec.url` via [`Self::canonical_git_url`],
710    /// `spec.ref.tag` via [`Self::publish_tag`]) at the substrate primitive
711    /// — a downstream consumer that reaches through both accessors reads
712    /// the complete published-git-identity of a caixa through two typed
713    /// dispatches, not four open-coded field accesses.
714    ///
715    /// The reader-side (`caixa-flux::cluster_bundle` /
716    /// `ClusterBundleOpts::for_caixa`'s `git_ref` field, every future
717    /// per-cluster snapshot bundle emitter, the future M4
718    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's tag-carrier
719    /// slot on the tatara `Process` intent) always resolves the tag under
720    /// the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] prefix — this
721    /// method encodes that reader-side convention. The writer-side
722    /// (`caixa-feira`'s `feira publish` `--prefix` clap flag) allows the
723    /// operator to override the prefix at publish time; the two surfaces
724    /// intentionally sit on the "canonical default + operator override"
725    /// pair the sibling [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] constant's
726    /// own docstring documents — a `feira publish --prefix release/`
727    /// override is the operator's explicit opt-out from the substrate
728    /// default, not a supported drift axis.
729    ///
730    /// The composition body is the exact byte-image of the prior inline
731    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_ref` composer at
732    /// caixa-flux/src/lib.rs:2105 — pinned by the sibling caixa-flux
733    /// byte-parity test
734    /// `cluster_bundle_opts_for_caixa_git_ref_routes_through_publish_tag_accessor`
735    /// against a future implementation of this method that reordered the
736    /// `format!` template arguments, migrated the `<prefix>` segment to a
737    /// different constant (the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] axis
738    /// a future Zig-style-tag rebrand may split off — the constant's own
739    /// docstring anticipates a substrate-side move to `release/<versao>`
740    /// or bare `<versao>` shapes once a sibling forge convention adopts a
741    /// slash-namespaced or bare-scalar form), interposed a canonicalization
742    /// pass on the `:versao` axis (a SemVer-2 build-metadata strip an OCI-
743    /// tag normalizer might apply once the M4 registry-alignment slot
744    /// lands), or silently absorbed an empty `:versao` arm (which cannot
745    /// occur past the [`Self::validate_versao`] gate but which a
746    /// hypothetical bypass on the accessor path must not silently paper
747    /// over).
748    ///
749    /// Owns per-call [`String`] allocation via the single `format!`
750    /// invocation — the by-value return matches every downstream
751    /// consumer's field-fill shape (the caixa-flux `GitRefSpec::Tag(String)`
752    /// variant's owned payload, every future `intent.aplicacao.tag: String`
753    /// field-fill on the M4 CR materializer's tag-carrier slot).
754    #[must_use]
755    pub fn publish_tag(&self) -> String {
756        format!(
757            "{prefix}{versao}",
758            prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
759            versao = self.versao(),
760        )
761    }
762
763    /// Substrate-canonical per-`Caixa` **resolved-Helm-chart-name** composer
764    /// — returns the caixa's canonical `lareira-<nome>` per-Servico Helm
765    /// chart identity as an owned [`String`], derived by dispatching through
766    /// the substrate-canonical [`crate::lareira_chart_name`] helper against
767    /// the typed [`Self::nome`] byte-string. Every substrate-side consumer
768    /// that resolves "which Helm chart identity does this caixa render
769    /// under?" reaches for exactly one typed dispatch on the substrate
770    /// primitive — the raw `caixa_core::lareira_chart_name(caixa.nome())`
771    /// two-step compose every prior caller re-derived collapses onto one
772    /// canonical arm on the single-`&Caixa` dispatch.
773    ///
774    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
775    /// git-URL composer + [`Self::publish_tag`] (07e05b8) resolved-publish-
776    /// tag composer on the paired per-`Caixa` published-artifact-identity
777    /// axis — same "close the composed substrate-primitive at one canonical
778    /// arm on the single-`&Caixa` dispatch, converge every prior open-coded
779    /// caller onto the arm" discipline extended from the resolved-URL /
780    /// resolved-tag projections of the `:repositorio` / `:versao` axes onto
781    /// the resolved-chart-name projection of the `:nome` axis. The three
782    /// accessors jointly close the triple of scalars every per-Servico
783    /// deploy artifact keys off (git source URL via
784    /// [`Self::canonical_git_url`], git source tag via
785    /// [`Self::publish_tag`], per-Servico Helm chart identity via
786    /// [`Self::lareira_chart_name`]) at the substrate primitive — a
787    /// downstream consumer that reaches through all three reads the
788    /// complete deploy-artifact identity of a caixa through three typed
789    /// dispatches, not six open-coded compositions across three renderer
790    /// crates.
791    ///
792    /// The reader-side (three production sites at the time of the lift —
793    /// [`caixa-helm::render_chart_for_servico_with`]'s `ChartDir.name`
794    /// composer at caixa-helm/src/lib.rs:778, the peer
795    /// [`caixa-flux::cluster_bundle`]'s per-CR `chart_name` binding at
796    /// caixa-flux/src/lib.rs:2219, and
797    /// [`caixa-tatara::process_for_aplicacao`]'s `release_name`
798    /// composer at caixa-tatara/src/lib.rs:227, plus every future
799    /// per-Servico OCI publish emitter the CAIXA-SDLC §II
800    /// `caixa-publish.yml` reusable workflow's `skopeo push` step keys
801    /// off, the future per-cluster snapshot bundle emitter, the future
802    /// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
803    /// per-member chart-carrier slot on the tatara `Process` intent) —
804    /// always resolves the chart name under the canonical
805    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] prefix; this method encodes
806    /// that reader-side convention. The joint-length invariant the peer
807    /// [`Self::validate_nome_chart_name_budget`] gate enforces at
808    /// caixa-build time (author-declared `:nome` + fixed prefix ≤
809    /// [`crate::DNS_1123_LABEL_MAX_LEN`]) is verified on the input to
810    /// this composer by construction, so the produced `lareira-<nome>`
811    /// string is a valid Helm chart-name segment on every accept-set
812    /// input.
813    ///
814    /// The composition body is the exact byte-image of the prior inline
815    /// `caixa_core::lareira_chart_name(caixa.nome())` two-step form every
816    /// prior caller re-derived — pinned by the sibling caixa-helm /
817    /// caixa-flux / caixa-tatara byte-parity tests
818    /// `<crate>_lareira_chart_name_routes_through_caixa_accessor` against
819    /// a future implementation of this method that reordered the
820    /// composition arguments, migrated the `<prefix>` segment to a
821    /// different constant (the [`crate::LAREIRA_CHART_NAME_PREFIX`] axis a
822    /// future substrate-side chart-family rebrand may split off — the
823    /// constant's own docstring anticipates a substrate-side move once
824    /// the `lareira-` scoping intent outlives the family it names),
825    /// interposed a canonicalization pass on the `:nome` axis (a per-
826    /// registry namespace-qualification an M4 CR materializer might apply
827    /// per-CR — the "`pleme-io/checkout` vs `partner-org/checkout`
828    /// collision" arm the multi-tenant-registry story acknowledges), or
829    /// silently absorbed an empty `:nome` arm (which cannot occur past
830    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
831    /// on the accessor path must not silently paper over).
832    ///
833    /// Owns per-call [`String`] allocation via the single
834    /// [`crate::lareira_chart_name`] `format!` invocation — the by-value
835    /// return matches every downstream consumer's field-fill shape (the
836    /// caixa-helm `ChartDir.name: String` field, the caixa-flux per-CR
837    /// `chart_name: String` binding, the caixa-tatara
838    /// `AplicacaoIntent.release_name: Option<String>` field-fill on the
839    /// `Some` arm).
840    #[must_use]
841    pub fn lareira_chart_name(&self) -> String {
842        crate::lareira_chart_name(self.nome())
843    }
844
845    /// Substrate-canonical per-`Caixa` **resolved-OCI-chart-ref** composer
846    /// — returns the caixa's canonical `oci://<registry>/lareira-<nome>`
847    /// per-Servico Helm chart OCI artifact reference as an owned
848    /// [`String`], derived by dispatching through the substrate-canonical
849    /// [`crate::oci_chart_ref`] helper (which itself composes
850    /// [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied `registry` +
851    /// [`crate::lareira_chart_name`]-of-[`Self::nome`]) against the
852    /// caller-supplied `registry` and the typed [`Self::nome`] byte-string.
853    /// Every substrate-side consumer that resolves "which OCI chart
854    /// artifact does this caixa publish under, in this registry?" reaches
855    /// for exactly one typed dispatch on the substrate primitive — the raw
856    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step compose
857    /// every prior caller re-derived collapses onto one canonical arm on
858    /// the single-`(&Caixa, &str)` dispatch.
859    ///
860    /// Fourth member of the paired per-`Caixa` published-artifact-identity
861    /// axis alongside [`Self::canonical_git_url`] (124f864) /
862    /// [`Self::publish_tag`] (07e05b8) / [`Self::lareira_chart_name`]
863    /// (a8f0bee) — same "close the composed substrate-primitive at one
864    /// canonical arm on the single-`&Caixa` dispatch, converge every
865    /// prior open-coded caller onto the arm" discipline extended from the
866    /// resolved-URL / resolved-tag / resolved-chart-name projections of
867    /// the `:repositorio` / `:versao` / `:nome` axes onto the resolved-
868    /// OCI-ref projection over the paired `(registry, :nome)` inputs. The
869    /// four accessors jointly close the per-`Caixa` published-artifact-
870    /// identity surface every downstream consumer of a caixa's published
871    /// deploy artifacts keys off (git source URL via
872    /// [`Self::canonical_git_url`], git source tag via
873    /// [`Self::publish_tag`], per-Servico Helm chart identity via
874    /// [`Self::lareira_chart_name`], per-registry OCI chart artifact
875    /// reference via [`Self::oci_chart_ref`]) at the substrate primitive
876    /// — a downstream consumer that reaches through all four reads the
877    /// complete deploy-artifact identity of a caixa through four typed
878    /// dispatches, not eight open-coded compositions across four renderer
879    /// crates. The unique-signature dispatch (`(&Caixa, &str)` on this
880    /// method vs. `&Caixa` on the sibling three) reflects the extra input
881    /// axis this composer folds in: unlike the git-URL / git-tag / chart-
882    /// name axes (each derived purely from a `&Caixa`), the OCI-ref axis
883    /// pairs the caixa's per-`:nome` chart identity with the caller-
884    /// supplied per-registry authority segment, so the accessor threads
885    /// the registry byte-string through as a positional `&str`.
886    ///
887    /// The reader-side (one production site at the time of the lift —
888    /// [`caixa-tatara::process_for_aplicacao`]'s `derive_chart_ref` helper
889    /// at caixa-tatara/src/lib.rs:333 that composes the emitted
890    /// `AplicacaoIntent.chart_ref` scalar the tatara-reconciler feeds into
891    /// `helm install`, plus every future per-Servico OCI publish emitter
892    /// the CAIXA-SDLC §II `caixa-publish.yml` reusable workflow's
893    /// `skopeo push` step keys off, the future per-cluster snapshot bundle
894    /// emitter's per-CR `oci://…` field-fill on the M4 registry-alignment
895    /// slot, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
896    /// materializer's per-member `chart_ref` slot on the tatara `Process`
897    /// intent, the `FluxCD` `HelmRelease` `spec.chart.spec.chart` field-fill
898    /// on the OCI-source path an M4 per-cluster registry-rewrite overlay
899    /// applies per-CR) — always resolves the OCI ref under the canonical
900    /// [`crate::OCI_SCHEME_PREFIX`] scheme prefix + the canonical
901    /// [`Self::lareira_chart_name`] chart-name segment; this method
902    /// encodes that reader-side convention.
903    ///
904    /// The composition body is the exact byte-image of the prior inline
905    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step form
906    /// every prior caller re-derived — pinned by the sibling caixa-tatara
907    /// byte-parity test
908    /// `derive_chart_ref_routes_through_caixa_oci_chart_ref_accessor`
909    /// against a future implementation of this method that reordered the
910    /// composition arguments, migrated the `<scheme>` segment to a
911    /// different constant (the [`crate::OCI_SCHEME_PREFIX`] axis a future
912    /// substrate-side registry-protocol rebrand may split off — the
913    /// constant's own docstring anticipates a substrate-side move once
914    /// Helm 3 / `FluxCD` introduce a successor scheme past `oci://`),
915    /// migrated the `<chart>` segment off the paired
916    /// [`crate::lareira_chart_name`] composer (a per-registry
917    /// namespace-qualification an M4 CR materializer might apply per-CR),
918    /// interposed a canonicalization pass on the `registry` axis (an OCI-
919    /// authority normalization once the M4 registry-alignment slot lands),
920    /// or silently absorbed an empty `:nome` arm (which cannot occur past
921    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
922    /// on the accessor path must not silently paper over).
923    ///
924    /// Owns per-call [`String`] allocation via the single
925    /// [`crate::oci_chart_ref`] `format!` invocation — the by-value return
926    /// matches every downstream consumer's field-fill shape (the caixa-
927    /// tatara `AplicacaoIntent.chart_ref: String` field-fill, every
928    /// future `intent.aplicacao.chart_ref: String` field-fill on the M4
929    /// CR materializer's chart-ref-carrier slot, every future
930    /// `HelmRelease.spec.chart.spec.chart: String` field-fill on the OCI-
931    /// source path).
932    #[must_use]
933    pub fn oci_chart_ref(&self, registry: &str) -> String {
934        crate::oci_chart_ref(registry, self.nome())
935    }
936
937    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
938    /// chart-description scalar accessor every consumer of the top-level
939    /// manifest's Chart.yaml `description:` axis keys off — returns the
940    /// author-declared `:descricao` byte-string verbatim as an
941    /// `Option<&str>`, borrowed from the typed slot's own
942    /// `Option<String>` storage. `None` when the slot is absent (the
943    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
944    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
945    /// omitted slot through a `format!("Generated chart for caixa Servico
946    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
947    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
948    /// and [`caixa-feira`]'s `render_flake` folds it through a
949    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
950    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
951    ///
952    /// The `:descricao` slot carries the universal-axis free-form-prose
953    /// chart-description identifier every kind of caixa emits under
954    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
955    /// supplies) — the typed slot's `Option<String>` accept-set
956    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
957    /// chart-description-shape-invalid rejected through
958    /// [`ManifestError::DescricaoInvalid`] past the shared
959    /// [`crate::render::is_chart_description_shape`] predicate the peer
960    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
961    /// load-bearing downstream consumers:
962    ///
963    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
964    ///     gate binding — the universal-axis identity gate wired at
965    ///     caixa-build time.
966    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
967    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
968    ///     chart's `Chart.yaml` `description:` field, which
969    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
970    ///     `WARNING [chart.metadata.description]: description is required`
971    ///     when absent) and which every registry that ingests the chart
972    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
973    ///     chart's canonical one-line prose descriptor.
974    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
975    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
976    ///     header directly beneath the `# <chart-name>` title, which
977    ///     every author who inspects the rendered chart bundle lands at.
978    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
979    ///     top-level fold — the emitted `flake.nix`'s `description`
980    ///     field, which every Nix consumer (`nix flake show`,
981    ///     `nix flake metadata`, downstream flake-registry ingestors)
982    ///     surfaces as the flake's canonical descriptor.
983    ///
984    /// Prior to this lift the `.descricao` field was accessed inline at
985    /// four production sites — [`Self::validate_descricao`]'s
986    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
987    /// caixa-helm `build_chart_yaml`
988    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
989    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
990    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
991    /// `README.md` header fold, and the caixa-feira `render_flake`
992    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
993    /// `description = ""` fold — four open-coded field-accesses that
994    /// expressed no compile-time link back to the typed slot. A future
995    /// extension of the `:descricao` axis to a richer author surface —
996    /// a per-`:descricao` locale-tagged multi-language descriptor map
997    /// (the "one caixa, N language-tagged prose descriptions" arm
998    /// author-tooling internationalization anticipates), a
999    /// per-registry-target length-and-shape overlay the M4 CR
1000    /// materializer resolves per-CR (the "ArtifactHub caps description
1001    /// at 512 bytes but the internal registry caps at 256" arm), a
1002    /// promotion of the plain `Option<String>` byte-string to a richer
1003    /// `ChartDescription` newtype guaranteeing the
1004    /// `is_chart_description_shape` predicate at the type level — would
1005    /// have had to be threaded through all four open-coded copies in
1006    /// lockstep or the validate gate and the three emit paths would
1007    /// silently disagree on which prose string a given [`Caixa`]
1008    /// resolves to (an author's
1009    /// `:descricao "Checkout flow orchestration."` would satisfy
1010    /// validate while one of the emit paths silently rendered a stale
1011    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
1012    /// resolution to a typed method on the substrate primitive means
1013    /// every downstream consumer of the caixa's per-`Caixa`
1014    /// chart-description surface reaches for exactly one typed dispatch
1015    /// — the resolver's accept-set migrates as a unit on any future
1016    /// axis addition.
1017    ///
1018    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
1019    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
1020    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
1021    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
1022    /// lift folds on. Same "one typed dispatch on the substrate
1023    /// primitive, thin projections at each consumer" discipline the
1024    /// peer per-`:placement`
1025    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1026    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1027    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1028    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1029    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1030    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1031    /// typed-slot atom axes, extended here to the third outer top-level
1032    /// `Caixa` universal-axis surface. Named `descricao()` to match the
1033    /// storage field's name; the accessor's identity maps onto the
1034    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1035    /// carries. The one remaining universal `Option<String>` slot
1036    /// (`:edicao`) folds on this pattern next.
1037    #[must_use]
1038    pub const fn descricao(&self) -> Option<&str> {
1039        match &self.descricao {
1040            Some(s) => Some(s.as_str()),
1041            None => None,
1042        }
1043    }
1044
1045    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
1046    /// accessor every consumer of the top-level manifest's tatara-lisp
1047    /// edition-selector axis keys off — returns the author-declared
1048    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
1049    /// the typed slot's own `Option<String>` storage. `None` when the
1050    /// slot is absent (the canonical "omit the slot to defer to the
1051    /// substrate's default edition" shape every existing
1052    /// [`caixa-resolver`] integration test fixture carries via
1053    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
1054    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
1055    /// arm by construction, so an author-omitted `:edicao` round-trips
1056    /// to a build without triggering the year-shape predicate).
1057    ///
1058    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
1059    /// decimal-year language-edition identifier every kind of caixa
1060    /// emits under (CAIXA-SDLC §I — the author-facing surface every
1061    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
1062    /// accept-set (empty-string rejected through
1063    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
1064    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
1065    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
1066    /// onto one load-bearing downstream consumer today
1067    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
1068    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
1069    /// future edition-aware substrate consumer the CAIXA-SDLC §I
1070    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
1071    /// selector every edition-aware build step keys off, the future
1072    /// per-edition compatibility-flag overlay the M4 CR materializer
1073    /// resolves per-CR, the peer [`Caixa::template`] canonical
1074    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
1075    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
1076    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
1077    /// carry `edicao: Some("2026".into())` by construction).
1078    ///
1079    /// Prior to this lift the `.edicao` field was accessed inline at
1080    /// one production site — [`Self::validate_edicao`]'s
1081    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
1082    /// open-coded field-access that expressed no compile-time link
1083    /// back to the typed slot. A future extension of the `:edicao`
1084    /// axis to a richer author surface — a per-`:edicao` known-
1085    /// edition allowlist (the future tightening
1086    /// [`Self::validate_edicao`]'s docstring acknowledges past the
1087    /// structural year-shape floor, rejecting year-shaped values that
1088    /// don't name a tatara-lisp edition the substrate actually
1089    /// understands — `"1999"` is year-shaped but no `1999` edition
1090    /// exists), a per-edition compatibility-flag overlay the M4 CR
1091    /// materializer resolves per-CR (the "edition `"2026"` enables
1092    /// macro-surface features the sibling `"2018"` gates behind a
1093    /// feature flag" arm the edition-selector story anticipates), a
1094    /// promotion of the plain `Option<String>` byte-string to a
1095    /// richer `CaixaEdition` enum discriminated on year once a sibling
1096    /// edition to `"2026"` lands — would have had to be threaded
1097    /// through the open-coded copy in lockstep with every future
1098    /// edition-aware consumer, or the validate gate and the future
1099    /// edition-aware consumer path would silently disagree on which
1100    /// edition a given [`Caixa`] resolves to (an author's
1101    /// `:edicao "2026"` would satisfy validate while a future
1102    /// edition-aware consumer silently defaulted to a stale edition,
1103    /// or vice versa). Lifting the resolution to a typed method on
1104    /// the substrate primitive means every downstream consumer of the
1105    /// caixa's per-`Caixa` edition surface reaches for exactly one
1106    /// typed dispatch — the resolver's accept-set migrates as a unit
1107    /// on any future axis addition.
1108    ///
1109    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
1110    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
1111    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
1112    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
1113    /// `Option<&str>` scalar" projection pattern this lift folds on.
1114    /// Same "one typed dispatch on the substrate primitive, thin
1115    /// projections at each consumer" discipline the peer per-`:placement`
1116    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1117    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1118    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1119    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1120    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1121    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1122    /// typed-slot atom axes, extended here to close the outer top-level
1123    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
1124    /// slot. Named `edicao()` to match the storage field's name; the
1125    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1126    /// vocabulary the slot's docstring already carries.
1127    #[must_use]
1128    pub const fn edicao(&self) -> Option<&str> {
1129        match &self.edicao {
1130            Some(s) => Some(s.as_str()),
1131            None => None,
1132        }
1133    }
1134
1135    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
1136    /// label caixa-identity scalar accessor every consumer of the top-
1137    /// level manifest's identity axis keys off — returns the author-
1138    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
1139    /// the typed slot's own `String` storage. Non-optional (`:nome` is
1140    /// a required-axis scalar every `defcaixa` form must supply; the
1141    /// [`Self::from_lisp`] derive rejects an omitted / non-string
1142    /// `:nome` at parse time, so a `Caixa` past parse definitionally
1143    /// carries a non-`None` `:nome`).
1144    ///
1145    /// The `:nome` slot carries the universal-axis DNS-1123-label
1146    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
1147    /// the primary identity axis every `defcaixa` form supplies
1148    /// alongside `:versao` / `:kind`; the substrate-wide identity every
1149    /// other typed surface that names a caixa reaches through — `:deps`
1150    /// entries, `:membros` entries, `:children` entries, the
1151    /// `lareira-<nome>` Helm chart name every per-Servico renderer
1152    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
1153    /// renderer emits) — the typed slot's `String` accept-set (empty
1154    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
1155    /// invalid rejected through [`ManifestError::NomeInvalid`] past
1156    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
1157    /// the peer name axes each land on, joint-length-with-`lareira-`-
1158    /// prefix rejected through
1159    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
1160    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
1161    /// load-bearing downstream consumer the substrate carries — the
1162    /// two universal-axis validate gates at caixa-build time
1163    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
1164    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
1165    /// derivation every per-Servico renderer keys off, the caixa-helm
1166    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
1167    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
1168    /// `HTTPRoute` per-Aplicacao name axes at
1169    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
1170    /// [`crate::pleme_program_selector`] /
1171    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
1172    /// derivations, and every future substrate renderer that emits an
1173    /// artifact keyed by the caixa's identity.
1174    ///
1175    /// Prior to this lift the `.nome` field was accessed inline at a
1176    /// dozen production sites across `caixa-core` (the two universal-
1177    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
1178    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
1179    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
1180    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
1181    /// entry `name:` fold, the `flux_kustomization_source_subtree`
1182    /// per-cluster subpath derivation), and `caixa-mesh` (the
1183    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
1184    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
1185    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
1186    /// insert) — a dozen open-coded field-accesses that expressed no
1187    /// compile-time link back to the typed slot. A future extension of
1188    /// the `:nome` axis to a richer author surface — a per-`:nome`
1189    /// structured `CaixaIdentity` newtype that carries the joint-
1190    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
1191    /// enforces at the type level (rather than as a validate-time
1192    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
1193    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
1194    /// `partner-org/checkout` collision" arm the multi-tenant-registry
1195    /// story acknowledges), a promotion of the plain `String` byte-
1196    /// string to a richer `CaixaNome` newtype discriminated on
1197    /// namespace prefix — would have had to be threaded through every
1198    /// open-coded copy in lockstep or the two validate gates and the
1199    /// dozen emit paths would silently disagree on which identity a
1200    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
1201    /// would satisfy validate while one of the emit paths silently
1202    /// rendered a drifted other identity, or vice versa). Lifting the
1203    /// resolution to a typed method on the substrate primitive means
1204    /// every downstream consumer of the caixa's per-`Caixa` identity
1205    /// surface reaches for exactly one typed dispatch — the resolver's
1206    /// accept-set migrates as a unit on any future axis addition.
1207    ///
1208    /// First outer top-level [`Caixa`] `&str`-return required-scalar
1209    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
1210    /// projection pattern the sibling per-`Caixa` `:versao` future lift
1211    /// folds on. Sibling in shape to the peer per-`:membros`
1212    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
1213    /// [`crate::aplicacao::WitContract::source`] /
1214    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
1215    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
1216    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
1217    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
1218    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
1219    /// per-sub-struct required-axis accessors carry on the sibling M3
1220    /// mesh-slot-atom scalar-value axes, extended here to open the
1221    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
1222    /// Named `nome()` to match the storage field's name; the accessor's
1223    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1224    /// slot's docstring already carries.
1225    #[must_use]
1226    pub const fn nome(&self) -> &str {
1227        self.nome.as_str()
1228    }
1229
1230    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
1231    /// pinned-version scalar accessor every consumer of the top-level
1232    /// manifest's version axis keys off — returns the author-declared
1233    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
1234    /// typed slot's own `String` storage. Non-optional (`:versao` is a
1235    /// required-axis scalar every `defcaixa` form must supply alongside
1236    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
1237    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
1238    /// parse definitionally carries a non-`None` `:versao`).
1239    ///
1240    /// The `:versao` slot carries the universal-axis SemVer-2
1241    /// concrete-version body every kind of caixa emits under
1242    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
1243    /// supplies alongside `:nome` / `:kind`; the substrate-wide
1244    /// pinned-version every downstream artifact-emitting consumer
1245    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
1246    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
1247    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
1248    /// prefix composes on top of, the programs.yaml entry's `versao:`
1249    /// value the `lareira-fleet-programs` aggregator carries onto each
1250    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
1251    /// tags every substrate-side `skopeo push` writes, the lacre
1252    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
1253    /// prior-version references peers in the exact same SemVer-2 shape).
1254    /// The typed slot's `String` accept-set (empty rejected through
1255    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
1256    /// through [`ManifestError::VersaoInvalid`] past
1257    /// [`semver::Version::parse`]) maps onto every load-bearing
1258    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
1259    /// universal-axis validate gate at caixa-build time, the
1260    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
1261    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
1262    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
1263    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
1264    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
1265    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
1266    /// tag derivation (`format!("{prefix}{versao}")`), and every future
1267    /// substrate renderer that emits an artifact keyed by the caixa's
1268    /// pinned version.
1269    ///
1270    /// Prior to this lift the `.versao` field was accessed inline at a
1271    /// dozen production sites across `caixa-core` (the universal-axis
1272    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
1273    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
1274    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
1275    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
1276    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
1277    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
1278    /// (the `feira publish` git-tag derivation + the `feira app graph` /
1279    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
1280    /// field-accesses that expressed no compile-time link back to the
1281    /// typed slot. A future extension of the `:versao` axis to a richer
1282    /// author surface — a per-`:versao` structured `CaixaVersion` at the
1283    /// storage layer (the substrate already carries a `CaixaVersion`
1284    /// newtype at [`crate::version::CaixaVersion`], deferred until the
1285    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
1286    /// a per-registry `:versao` immutability overlay the M4 CR
1287    /// materializer enforces per-CR, a promotion of the plain `String`
1288    /// byte-string to a richer `PinnedVersao` newtype discriminated on
1289    /// SemVer-2 pre-release / build-metadata presence — would have had
1290    /// to be threaded through every open-coded copy in lockstep or the
1291    /// validate gate and the dozen emit paths would silently disagree
1292    /// on which version a given [`Caixa`] resolves to (an author's
1293    /// `:versao "0.1.0"` would satisfy validate while one of the emit
1294    /// paths silently rendered a drifted other version, or vice versa).
1295    /// Lifting the resolution to a typed method on the substrate
1296    /// primitive means every downstream consumer of the caixa's
1297    /// per-`Caixa` pinned-version surface reaches for exactly one typed
1298    /// dispatch — the resolver's accept-set migrates as a unit on any
1299    /// future axis addition.
1300    ///
1301    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
1302    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
1303    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
1304    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
1305    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
1306    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
1307    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
1308    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
1309    /// on the sibling per-typed-slot version-carrier axes, extended here
1310    /// to close the second outer top-level [`Caixa`] required-`&str`-
1311    /// carrying axis so the two universal-axis identity-carrying
1312    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
1313    /// share the same "one typed dispatch per axis" discipline. Named
1314    /// `versao()` to match the storage field's name; the accessor's
1315    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1316    /// slot's docstring already carries.
1317    #[must_use]
1318    pub const fn versao(&self) -> &str {
1319        self.versao.as_str()
1320    }
1321
1322    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
1323    /// closed-set-enum discriminant accessor every consumer of the top-
1324    /// level manifest's kind axis keys off — returns the author-declared
1325    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
1326    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
1327    /// (`:kind` is a required-axis discriminant every `defcaixa` form
1328    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
1329    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
1330    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
1331    /// variant).
1332    ///
1333    /// The `:kind` slot carries the universal-axis closed-set typed-
1334    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
1335    /// §I — the primary shape gate every renderer / verifier /
1336    /// operator branches on; the five variants `Biblioteca` /
1337    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1338    /// the caixa surface into disjoint runtime contracts) — the typed
1339    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1340    /// values through the derive-macro's symbol-arm gate, exhaustively
1341    /// matched at every downstream dispatch site) maps onto every
1342    /// load-bearing downstream consumer the substrate carries:
1343    ///
1344    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1345    ///     predicate — the canonical two-line
1346    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1347    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1348    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1349    ///     ComputeUnit` CR materializer) runs at its entry-point,
1350    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1351    ///     `actual:` field the diagnostic surfaces to name the offending
1352    ///     caixa's variant.
1353    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1354    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1355    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1356    ///     slot columns into their typed sub-spec only when the kind
1357    ///     matches (returns `None` otherwise); the future per-Servico
1358    ///     M2-view composer (`servico_view`) will follow the same shape.
1359    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1360    ///     coherence gate — the `!self.kind.requires_exe()` /
1361    ///     `!self.kind.requires_servicos()` predicates that fence
1362    ///     each code-surface slot from the wrong owning kind.
1363    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1364    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1365    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1366    ///     coherence error carriers (`SupervisorOwnsCode` /
1367    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1368    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1369    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1370    ///     variant in their `kind:` field.
1371    ///
1372    /// Prior to this lift the `.kind` field was accessed inline at
1373    /// twenty-plus production sites across `caixa-core` (the
1374    /// [`crate::render::require_kind`] entry-gate predicate + the
1375    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1376    /// composers, the `declared_foreign_code_slots` per-slot kind-
1377    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1378    /// kind ↔ code-surface predicates + four error carriers) — a score
1379    /// of open-coded field-accesses that expressed no compile-time link
1380    /// back to the typed slot. A future extension of the `:kind` axis
1381    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1382    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1383    /// variant across the wasm-component / legacy-container / native-
1384    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1385    /// kind-overlay the M4 CR materializer resolves per-CR (the
1386    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1387    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1388    /// enum to a richer `KindWithRuntime` discriminated on the
1389    /// component-model world axis — would have had to be threaded
1390    /// through every open-coded copy in lockstep or the entry gate,
1391    /// the view composers, and the layout invariants would silently
1392    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1393    /// the resolution to a typed method on the substrate primitive
1394    /// means every downstream consumer of the caixa's per-`Caixa`
1395    /// kind surface reaches for exactly one typed dispatch — the
1396    /// resolver's accept-set migrates as a unit on any future axis
1397    /// addition.
1398    ///
1399    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1400    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1401    /// required-discriminant" projection pattern. Sibling in shape to
1402    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1403    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1404    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1405    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1406    /// on the sibling nested-spec typed-slot discriminator axes,
1407    /// extended here to the outer top-level [`Caixa`] universal-axis
1408    /// surface. Named `kind()` to match the storage field's name;
1409    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1410    /// vocabulary the slot's docstring already carries.
1411    #[must_use]
1412    pub const fn kind(&self) -> CaixaKind {
1413        self.kind
1414    }
1415
1416    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1417    /// maintainer-name-list slice-accessor every consumer of the top-
1418    /// level manifest's maintainer axis keys off — returns the author-
1419    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1420    /// the same backing buffer the raw `self.autores.as_slice()` field
1421    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1422    /// empty axis every `defcaixa` form supplies with an empty `()` when
1423    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1424    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1425    /// parse definitionally carries a `Vec<String>` slot — possibly
1426    /// empty — and the returned `&[String]` degenerates to an empty
1427    /// slice on that arm without any silent `None` collapse).
1428    ///
1429    /// The `:autores` slot carries the universal-axis maintainer-name
1430    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1431    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1432    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1433    /// every downstream registry-facing artifact emits under) — the
1434    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1435    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1436    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1437    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1438    /// onto every load-bearing downstream consumer the substrate carries
1439    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1440    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1441    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1442    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1443    /// name, email: None }` record, every future per-`Caixa` registry-
1444    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1445    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1446    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1447    /// the future per-cluster author-notification overlay the M4 CR
1448    /// materializer resolves per-CR).
1449    ///
1450    /// Prior to this lift the `.autores` field was accessed inline at
1451    /// two production sites — [`Self::validate_autores`]'s `for autor
1452    /// in &self.autores` walk that gates every entry through
1453    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1454    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1455    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1456    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1457    /// two open-coded field-accesses that expressed no compile-time link
1458    /// back to the typed slot. A future extension of the `:autores` axis
1459    /// to a richer author surface — a per-`:autores` structured
1460    /// `Maintainer { name, email, url }` at the storage layer once the
1461    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1462    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1463    /// enforces per-CR (the "cluster policy demands every author declare
1464    /// an on-file `mailto:` contact" arm), a promotion of the plain
1465    /// `Vec<String>` byte-string list to a richer
1466    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1467    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1468    /// predicate already resolves through — would have had to be
1469    /// threaded through both open-coded copies in lockstep or the
1470    /// validate gate and the caixa-helm emit path would silently
1471    /// disagree on which authors a given [`Caixa`] resolves to (an
1472    /// author's `:autores ("alice" "bob")` would satisfy validate while
1473    /// the caixa-helm emit path silently rendered a drifted other
1474    /// maintainer list, or vice versa). Lifting the resolution to a
1475    /// typed method on the substrate primitive means every downstream
1476    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1477    /// for exactly one typed dispatch — the resolver's accept-set
1478    /// migrates as a unit on any future axis addition.
1479    ///
1480    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1481    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1482    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1483    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1484    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1485    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1486    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1487    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1488    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1489    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1490    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1491    /// per-M3 typed-slot list axes, extended here to the outer top-level
1492    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1493    /// `&Vec<String>`) because every downstream consumer of the author
1494    /// list treats it as a read-only sequence — the slice-view is the
1495    /// narrowest borrow that supports every present + roadmapped consumer
1496    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1497    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1498    /// reaches for (the storage-side `Vec` remains reachable through the
1499    /// `pub autores` field for the mutation-carrying serde round-trip and
1500    /// per-test fixture-mutation paths). Named `autores()` to match the
1501    /// storage field's name; the accessor's identity maps onto the
1502    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1503    /// carries.
1504    #[must_use]
1505    pub const fn autores(&self) -> &[String] {
1506        self.autores.as_slice()
1507    }
1508
1509    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1510    /// registry-search-tag-list slice-accessor every consumer of the
1511    /// top-level manifest's topical-tag axis keys off — returns the
1512    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1513    /// slice-view over the same backing buffer the raw
1514    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1515    /// list-carrying (`:etiquetas` is a default-empty axis every
1516    /// `defcaixa` form supplies with an empty `()` when unset; the
1517    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1518    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1519    /// definitionally carries a `Vec<String>` slot — possibly empty —
1520    /// and the returned `&[String]` degenerates to an empty slice on
1521    /// that arm without any silent `None` collapse).
1522    ///
1523    /// The `:etiquetas` slot carries the universal-axis topical-tag
1524    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1525    /// author-facing surface every `defcaixa` form supplies alongside
1526    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1527    /// search-facing axis every downstream registry-facing artifact
1528    /// emits under) — the typed slot's `Vec<String>` accept-set
1529    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1530    /// non-chart-keyword-shape rejected through
1531    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1532    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1533    /// every load-bearing downstream consumer the substrate carries —
1534    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1535    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1536    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1537    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1538    /// `Chart.yaml` `keywords:` array (chained with the
1539    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1540    /// dedup'd through a `BTreeSet` at emit time), every future per-
1541    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1542    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1543    /// annotation, the future per-cluster tag-notification overlay the
1544    /// M4 CR materializer resolves per-CR).
1545    ///
1546    /// Prior to this lift the `.etiquetas` field was accessed inline at
1547    /// two production sites — [`Self::validate_etiquetas`]'s `for
1548    /// etiqueta in &self.etiquetas` walk that gates every entry through
1549    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1550    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1551    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1552    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1553    /// two open-coded field-accesses that expressed no compile-time
1554    /// link back to the typed slot. A future extension of the
1555    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1556    /// structured `ChartKeyword { name, uri, category }` at the storage
1557    /// layer once the substrate absorbs `artifacthub.io/keywords`
1558    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1559    /// CR materializer enforces per-CR (the "cluster policy demands
1560    /// every tag come from a substrate-approved taxonomy" arm), a
1561    /// promotion of the plain `Vec<String>` byte-string list to a
1562    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1563    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1564    /// already resolves through — would have had to be threaded through
1565    /// both open-coded copies in lockstep or the validate gate and the
1566    /// caixa-helm emit path would silently disagree on which tags a
1567    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1568    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1569    /// path silently rendered a drifted other keyword list, or vice
1570    /// versa). Lifting the resolution to a typed method on the
1571    /// substrate primitive means every downstream consumer of the
1572    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1573    /// typed dispatch — the resolver's accept-set migrates as a unit
1574    /// on any future axis addition.
1575    ///
1576    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1577    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1578    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1579    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1580    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1581    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1582    /// fold onto the same pattern in future lifts. Sibling in shape to
1583    /// the peer per-`:supervisor`
1584    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1585    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1586    /// (a6e18d7), per-`:membros`
1587    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1588    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1589    /// (0dcc926), and per-`:upgrade-from :instructions`
1590    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1591    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1592    /// typed-slot list axes, extended here to the outer top-level
1593    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1594    /// `&Vec<String>`) because every downstream consumer of the tag
1595    /// list treats it as a read-only sequence — the slice-view is the
1596    /// narrowest borrow that supports every present + roadmapped
1597    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1598    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1599    /// the typed view reaches for (the storage-side `Vec` remains
1600    /// reachable through the `pub etiquetas` field for the mutation-
1601    /// carrying serde round-trip and per-test fixture-mutation paths).
1602    /// Named `etiquetas()` to match the storage field's name; the
1603    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1604    /// vocabulary the slot's docstring already carries.
1605    #[must_use]
1606    pub const fn etiquetas(&self) -> &[String] {
1607        self.etiquetas.as_slice()
1608    }
1609
1610    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1611    /// library-source-path-list slice-accessor every consumer of the
1612    /// top-level manifest's Biblioteca-source axis keys off — returns
1613    /// the author-declared `:bibliotecas` list verbatim as a
1614    /// `&[String]` slice-view over the same backing buffer the raw
1615    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1616    /// list-carrying (`:bibliotecas` is a default-empty axis every
1617    /// `defcaixa` form supplies with an empty `()` when unset; the
1618    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1619    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1620    /// parse definitionally carries a `Vec<String>` slot — possibly
1621    /// empty — and the returned `&[String]` degenerates to an empty
1622    /// slice on that arm without any silent `None` collapse).
1623    ///
1624    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1625    /// entry-path list every `:kind Biblioteca` caixa emits under
1626    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1627    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1628    /// substrate-wide library-carrier axis every downstream
1629    /// authoring-facing consumer keys off) — the typed slot's
1630    /// `Vec<String>` accept-set (empty-per-entry rejected through
1631    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1632    /// non-sandboxed-relative-shape rejected through
1633    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1634    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1635    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1636    /// maps onto every load-bearing downstream consumer the substrate
1637    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1638    /// empty-check + per-entry file-exists loop at
1639    /// caixa-core/src/layout.rs that gates each entry through
1640    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1641    /// [`Self::validate_code_paths`] per-slot shape gate at
1642    /// caixa-core/src/manifest.rs that walks each entry through the
1643    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1644    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1645    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1646    /// declared library file for lexical / structural errors before
1647    /// downstream `importar` resolution, every future per-`Caixa`
1648    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1649    /// (the future `tatara-lispc` compilation entry the docstring at
1650    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1651    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1652    /// the future `caixa-lsp` per-library semantic-token stream the
1653    /// caixa-lsp docstring roadmaps).
1654    ///
1655    /// Prior to this lift the `.bibliotecas` field was accessed inline
1656    /// at three production sites — [`crate::LayoutInvariants`]'s
1657    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1658    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1659    /// declared library path through the on-disk-existence check,
1660    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1661    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1662    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1663    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1664    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1665    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1666    /// coded field-accesses that expressed no compile-time link back
1667    /// to the typed slot. A future extension of the `:bibliotecas`
1668    /// axis to a richer library surface — a per-`:bibliotecas`
1669    /// structured `BibliotecaEntry { path, edition, exports }` at the
1670    /// storage layer once the substrate absorbs the per-library
1671    /// language-edition + explicit-exports tuple the tatara-lisp
1672    /// module-system roadmap acknowledges, a per-registry
1673    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1674    /// per-CR (the "cluster policy demands every biblioteca declare
1675    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1676    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1677    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1678    /// [`crate::render::is_sandboxed_relative_path`] +
1679    /// [`crate::render::is_lisp_extension`] predicates already resolve
1680    /// through — would have had to be threaded through all three
1681    /// open-coded copies in lockstep or the layout gate, the shape
1682    /// validator, and the `feira build` phase-1 parse walk would
1683    /// silently disagree on which library paths a given [`Caixa`]
1684    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1685    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1686    /// silently parsed a drifted other list, or vice versa). Lifting
1687    /// the resolution to a typed method on the substrate primitive
1688    /// means every downstream consumer of the caixa's per-`Caixa`
1689    /// library-source surface reaches for exactly one typed dispatch
1690    /// — the resolver's accept-set migrates as a unit on any future
1691    /// axis addition.
1692    ///
1693    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1694    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1695    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1696    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1697    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1698    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1699    /// `:children` / `:membros` / `:contratos`) fold onto the same
1700    /// pattern in future lifts. Sibling in shape to the peer
1701    /// per-`:supervisor`
1702    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1703    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1704    /// (a6e18d7), per-`:membros`
1705    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1706    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1707    /// (0dcc926), and per-`:upgrade-from :instructions`
1708    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1709    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1710    /// typed-slot list axes, extended here to the outer top-level
1711    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1712    /// `&Vec<String>`) because every downstream consumer of the
1713    /// library-source list treats it as a read-only sequence — the
1714    /// slice-view is the narrowest borrow that supports every
1715    /// present + roadmapped consumer (`.iter()`, `.len()`,
1716    /// `.is_empty()`) without leaking the backing `Vec`'s
1717    /// grow/push/reserve surface no consumer of the typed view
1718    /// reaches for (the storage-side `Vec` remains reachable through
1719    /// the `pub bibliotecas` field for the mutation-carrying serde
1720    /// round-trip and per-test fixture-mutation paths). Named
1721    /// `bibliotecas()` to match the storage field's name; the
1722    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1723    /// vocabulary the slot's docstring already carries.
1724    #[must_use]
1725    pub const fn bibliotecas(&self) -> &[String] {
1726        self.bibliotecas.as_slice()
1727    }
1728
1729    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1730    /// nix-built-executable-entry-path-list slice-accessor every consumer
1731    /// of the top-level manifest's Binario-executable axis keys off —
1732    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1733    /// slice-view over the same backing buffer the raw
1734    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1735    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1736    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1737    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1738    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1739    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1740    /// degenerates to an empty slice on that arm without any silent
1741    /// `None` collapse).
1742    ///
1743    /// The `:exe` slot carries the universal-axis nix-built executable
1744    /// entry-path list every `:kind Binario` caixa emits under
1745    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1746    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1747    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1748    /// downstream flake-build-facing consumer keys off) — the typed
1749    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1750    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1751    /// non-sandboxed-relative-shape rejected through
1752    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1753    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1754    /// directory paths rejected past the layout's
1755    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1756    /// onto every load-bearing downstream consumer the substrate carries
1757    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1758    /// per-entry file-exists + `exe/`-directory-fence loop at
1759    /// caixa-core/src/layout.rs that gates each entry through
1760    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1761    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1762    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1763    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1764    /// that fences code-surface slots off from the two no-code kinds,
1765    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1766    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1767    /// fences the `:exe` code surface off from every non-Binario code-
1768    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1769    /// that walks each entry through the sandbox-relative / cross-entry
1770    /// duplicate gates, every future per-`Caixa` executable-facing
1771    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1772    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1773    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1774    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1775    /// future `feira nix` per-executable Binario-target emit path).
1776    ///
1777    /// Prior to this lift the `.exe` field was accessed inline at three
1778    /// production sites — the compound-code-path `has_code =
1779    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1780    /// !caixa.servicos.is_empty()` OR-fold on the
1781    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1782    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1783    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1784    /// gate, the per-entry `for p in &caixa.exe`
1785    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1786    /// [`Self::declared_foreign_code_slots`]'s
1787    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1788    /// open-coded field-accesses that expressed no compile-time link
1789    /// back to the typed slot. A future extension of the `:exe` axis
1790    /// to a richer executable surface — a per-`:exe` structured
1791    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1792    /// layer once the substrate absorbs the per-executable
1793    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1794    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1795    /// the M4 CR materializer enforces per-CR (the "cluster policy
1796    /// demands every Binario declare an explicit `:wrapper`" arm), a
1797    /// promotion of the plain `Vec<String>` byte-string list to a
1798    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1799    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1800    /// fence already resolves through — would have had to be threaded
1801    /// through all four open-coded copies in lockstep or the layout
1802    /// gate, the shape validator, and the `feira nix` emit path would
1803    /// silently disagree on which executable paths a given [`Caixa`]
1804    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1805    /// satisfy layout while `feira nix` silently packaged a drifted
1806    /// other list, or vice versa). Lifting the resolution to a typed
1807    /// method on the substrate primitive means every downstream
1808    /// consumer of the caixa's per-`Caixa` executable-source surface
1809    /// reaches for exactly one typed dispatch — the resolver's accept-
1810    /// set migrates as a unit on any future axis addition.
1811    ///
1812    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1813    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1814    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1815    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1816    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1817    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1818    /// future lift closes onto (per the trio of code-surface list slots
1819    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1820    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1821    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1822    /// last unlifted code-surface slot). Sibling in shape to the peer
1823    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1824    /// (bc92bce), per-`:placement`
1825    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1826    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1827    /// (6c77e36), per-`:contratos`
1828    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1829    /// per-`:upgrade-from :instructions`
1830    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1831    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1832    /// typed-slot list axes, extended here to the outer top-level
1833    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1834    /// `&Vec<String>`) because every downstream consumer of the
1835    /// executable-source list treats it as a read-only sequence — the
1836    /// slice-view is the narrowest borrow that supports every
1837    /// present + roadmapped consumer (`.iter()`, `.len()`,
1838    /// `.is_empty()`) without leaking the backing `Vec`'s
1839    /// grow/push/reserve surface no consumer of the typed view
1840    /// reaches for (the storage-side `Vec` remains reachable through
1841    /// the `pub exe` field for the mutation-carrying serde
1842    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1843    /// to match the storage field's name; the accessor's identity
1844    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1845    /// docstring already carries.
1846    #[must_use]
1847    pub const fn exe(&self) -> &[String] {
1848        self.exe.as_slice()
1849    }
1850
1851    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1852    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1853    /// of the top-level manifest's Servico-component axis keys off —
1854    /// returns the author-declared `:servicos` list verbatim as a
1855    /// `&[String]` slice-view over the same backing buffer the raw
1856    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1857    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1858    /// form supplies with an empty `()` when unset; the
1859    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1860    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1861    /// definitionally carries a `Vec<String>` slot — possibly empty —
1862    /// and the returned `&[String]` degenerates to an empty slice on
1863    /// that arm without any silent `None` collapse).
1864    ///
1865    /// The `:servicos` slot carries the universal-axis
1866    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1867    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1868    /// author-facing surface every `defcaixa` form supplies alongside
1869    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1870    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1871    /// Servico-facing renderer keys off) — the typed slot's
1872    /// `Vec<String>` accept-set (empty-per-entry rejected through
1873    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1874    /// non-sandboxed-relative-shape rejected through
1875    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1876    /// extension rejected through
1877    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1878    /// entry duplicate rejected through
1879    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1880    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1881    /// renderer entry-points, out-of-`servicos/`-directory paths
1882    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1883    /// `starts_with` fence) maps onto every load-bearing downstream
1884    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1885    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1886    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1887    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1888    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1889    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1890    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1891    /// that fences code-surface slots off from the two no-code kinds,
1892    /// [`Self::declared_foreign_code_slots`]'s
1893    /// `!self.servicos.is_empty()` arm on the
1894    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1895    /// `:servicos` code surface off from every non-Servico code-running
1896    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1897    /// walks each entry through the sandbox-relative / `.computeunit.
1898    /// yaml`-extension / cross-entry duplicate gates, the
1899    /// [`crate::require_single_servico`] V0 singularity gate every
1900    /// per-Servico renderer entry-point runs through
1901    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1902    /// `feira deploy` per-verb `first_servico_path` walk at
1903    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1904    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1905    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1906    /// per-Servico OCI packager, the future M4
1907    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1908    /// per-Servico OTel collector-config emit).
1909    ///
1910    /// Prior to this lift the `.servicos` field was accessed inline at
1911    /// five production sites — the compound-code-path `has_code =
1912    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1913    /// !caixa.servicos.is_empty()` OR-fold on the
1914    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1915    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1916    /// `caixa.servicos.is_empty()`
1917    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1918    /// per-entry `for p in &caixa.servicos`
1919    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1920    /// [`Self::declared_foreign_code_slots`]'s
1921    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1922    /// and the [`crate::require_single_servico`] V0 count gate's
1923    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1924    /// projection (both the accept-arm predicate and the
1925    /// diagnostic-carrying `ServicoCountMismatch { count }`
1926    /// projection) — five open-coded field-accesses across three
1927    /// crates that expressed no compile-time link back to the typed
1928    /// slot. A future extension of the `:servicos` axis to a richer
1929    /// component surface — a per-`:servicos` structured
1930    /// `ServicoEntry { path, world, capabilities }` at the storage
1931    /// layer once the substrate absorbs the per-component WIT-world +
1932    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1933    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1934    /// materializer enforces per-CR (the "cluster policy demands every
1935    /// Servico declare an explicit `:world`" arm), a promotion of the
1936    /// plain `Vec<String>` byte-string list to a richer
1937    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1938    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1939    /// `starts_with(servicos_dir)` fence and the
1940    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1941    /// already resolve through, a promotion of the V0 singleton
1942    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1943    /// component-model multi-world boundary — would have had to be
1944    /// threaded through all five open-coded copies in lockstep or the
1945    /// layout gate, the shape validator, the V0 count gate, and the
1946    /// `feira chart` / `feira deploy` entry-point walks would silently
1947    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1948    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1949    /// yaml")` would satisfy layout while `feira chart` silently
1950    /// packaged a drifted other list, or vice versa). Lifting the
1951    /// resolution to a typed method on the substrate primitive means
1952    /// every downstream consumer of the caixa's per-`Caixa`
1953    /// ComputeUnit-CR-source surface reaches for exactly one typed
1954    /// dispatch — the resolver's accept-set migrates as a unit on any
1955    /// future axis addition.
1956    ///
1957    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1958    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1959    /// projection pattern [`Self::autores`] (b5d813f) opened,
1960    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1961    /// (8a36c23) closed the universal-axis text-tag family of, and
1962    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1963    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1964    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1965    /// a substrate-canonical slice accessor, the trio of code-surface
1966    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1967    /// tuple carries is complete on the typed dispatch surface (the
1968    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1969    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1970    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1971    /// per-element accessor swap in isolation — a future companion lift
1972    /// promotes the tuple's element type to `&[String]` and threads the
1973    /// triple of typed dispatches through as a unit). Sibling in shape
1974    /// to the peer per-`:supervisor`
1975    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1976    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1977    /// (a6e18d7), per-`:membros`
1978    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1979    /// per-`:contratos`
1980    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1981    /// per-`:upgrade-from :instructions`
1982    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1983    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1984    /// typed-slot list axes, extended here to the outer top-level
1985    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1986    /// `&Vec<String>`) because every downstream consumer of the
1987    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1988    /// the slice-view is the narrowest borrow that supports every
1989    /// present + roadmapped consumer (`.iter()`, `.len()`,
1990    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1991    /// grow/push/reserve surface no consumer of the typed view reaches
1992    /// for (the storage-side `Vec` remains reachable through the
1993    /// `pub servicos` field for the mutation-carrying serde round-trip
1994    /// and per-test fixture-mutation paths, and for the
1995    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1996    /// homogeneous-element-type shape carries the raw field access
1997    /// until the trio-closure lift promotes the tuple as a unit).
1998    /// Named `servicos()` to match the storage field's name; the
1999    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
2000    /// vocabulary the slot's docstring already carries.
2001    #[must_use]
2002    pub const fn servicos(&self) -> &[String] {
2003        self.servicos.as_slice()
2004    }
2005
2006    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
2007    /// runtime-dependency-declaration-list slice-accessor every consumer
2008    /// of the top-level manifest's runtime-dep-graph axis keys off —
2009    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
2010    /// slice-view over the same backing buffer the raw
2011    /// `self.deps.as_slice()` field access borrows from. Empty-list-
2012    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
2013    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
2014    /// derive folds an omitted `:deps` through `#[serde(default)]` to
2015    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
2016    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
2017    /// degenerates to an empty slice on that arm without any silent
2018    /// `None` collapse).
2019    ///
2020    /// The `:deps` slot carries the universal-axis runtime dependency
2021    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
2022    /// facing surface every `defcaixa` form supplies alongside `:nome` /
2023    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
2024    /// every downstream resolver-facing artifact emits under) — the
2025    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
2026    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
2027    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
2028    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
2029    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
2030    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
2031    /// maps onto every load-bearing downstream consumer the substrate
2032    /// carries — the [`Self::validate_deps`] per-entry
2033    /// [`Dep::validate`] + within-list dedup walk at
2034    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
2035    /// cross-list self-reference gate at caixa-core/src/layout.rs that
2036    /// checks each entry against the caixa's own `:nome`, the
2037    /// caixa-resolver `for dep in &root.deps` closure walk at
2038    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
2039    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
2040    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
2041    /// caixa-crd/src/conversion.rs that materializes each entry into the
2042    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
2043    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
2044    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
2045    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
2046    /// closure emit walk the caixa-resolver docstring roadmaps).
2047    ///
2048    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2049    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2050    /// sibling `:deps-dev` future lift closes on. Peer of the closed
2051    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
2052    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
2053    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
2054    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
2055    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
2056    /// pattern onto a novel element-type axis (`Dep` composite vs the
2057    /// prior sibling family's `String` scalar). Sibling in shape to the
2058    /// peer per-`:supervisor`
2059    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
2060    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
2061    /// (a6e18d7), per-`:membros`
2062    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
2063    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
2064    /// (0dcc926), and per-`:upgrade-from :instructions`
2065    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2066    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2067    /// typed-slot list axes, extended here to the outer top-level
2068    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
2069    /// (not `&Vec<Dep>`) because every downstream consumer of the
2070    /// runtime-dep list treats it as a read-only sequence — the slice-
2071    /// view is the narrowest borrow that supports every present +
2072    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2073    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2074    /// of the typed view reaches for (the storage-side `Vec` remains
2075    /// reachable through the `pub deps` field for the mutation-carrying
2076    /// serde round-trip and per-test fixture-mutation paths). Named
2077    /// `deps()` to match the storage field's name; the accessor's
2078    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
2079    /// slot's docstring already carries.
2080    #[must_use]
2081    pub const fn deps(&self) -> &[Dep] {
2082        self.deps.as_slice()
2083    }
2084
2085    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
2086    /// development-only-dependency-declaration-list slice-accessor every
2087    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
2088    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
2089    /// slice-view over the same backing buffer the raw
2090    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
2091    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
2092    /// form supplies with an empty `()` when unset; the
2093    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
2094    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
2095    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
2096    /// the returned `&[Dep]` degenerates to an empty slice on that arm
2097    /// without any silent `None` collapse).
2098    ///
2099    /// The `:deps-dev` slot carries the universal-axis dev-only
2100    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
2101    /// the author-facing sibling of `:deps` that every `defcaixa` form
2102    /// supplies to declare tests / lint / bench closures the runtime
2103    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
2104    /// axis every downstream test-facing artifact emits under, matching
2105    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
2106    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
2107    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
2108    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
2109    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
2110    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
2111    /// within-list duplicate `:nome` rejected through
2112    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
2113    /// load-bearing downstream consumer the substrate carries — the
2114    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
2115    /// dedup walk at caixa-core/src/manifest.rs, the
2116    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
2117    /// gate at caixa-core/src/layout.rs that checks each entry against
2118    /// the caixa's own `:nome`, the caixa-resolver
2119    /// `for dep in &root.deps_dev` closure walk at
2120    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
2121    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
2122    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
2123    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
2124    /// overlay the M4 CR materializer resolves per-CR, the future
2125    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
2126    /// roadmaps).
2127    ///
2128    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2129    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2130    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
2131    /// jointly close the two-list dep-graph surface every downstream
2132    /// resolver-facing consumer keys off (runtime `:deps` +
2133    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
2134    /// pair the [`Self::validate_deps`] gate already walks in canonical
2135    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
2136    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
2137    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
2138    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
2139    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
2140    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
2141    /// dev-dep composite-element axis (`Dep` composite, matching the
2142    /// [`Self::deps`] element type). Sibling in shape to the peer
2143    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
2144    /// (bc92bce), per-`:placement`
2145    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
2146    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
2147    /// (6c77e36), per-`:contratos`
2148    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
2149    /// per-`:upgrade-from :instructions`
2150    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2151    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2152    /// typed-slot list axes, folded here to the outer top-level
2153    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
2154    /// (not `&Vec<Dep>`) because every downstream consumer of the
2155    /// dev-dep list treats it as a read-only sequence — the slice-view
2156    /// is the narrowest borrow that supports every present +
2157    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2158    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2159    /// of the typed view reaches for (the storage-side `Vec` remains
2160    /// reachable through the `pub deps_dev` field for the mutation-
2161    /// carrying serde round-trip and per-test fixture-mutation paths).
2162    /// Named `deps_dev()` to match the storage field's `snake_case` name;
2163    /// the kebab-case author-surface tag `:deps-dev` is the same axis
2164    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
2165    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
2166    /// docstring already carries.
2167    #[must_use]
2168    pub const fn deps_dev(&self) -> &[Dep] {
2169        self.deps_dev.as_slice()
2170    }
2171
2172    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
2173    /// every consumer that walks one of the two dep-list axes keyed on a
2174    /// [`crate::dep::DepList`] discriminant reaches for — routes the
2175    /// `(list: DepList) -> &[Dep]` projection through one typed method on
2176    /// the substrate primitive rather than the prior open-coded
2177    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
2178    /// inline dispatch every per-axis walker would otherwise carry.
2179    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
2180    /// `&[Dep]` slice-view over the same backing buffer the sibling
2181    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
2182    /// accessors borrow from, preserving the empty-list-carrying invariant
2183    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
2184    /// are default-empty axes every `defcaixa` form supplies with an empty
2185    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
2186    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
2187    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
2188    /// returned `&[Dep]` degenerates to an empty slice on either arm
2189    /// without any silent `None` collapse).
2190    ///
2191    /// The [`crate::dep::DepList`] closed-set typed enum is the
2192    /// substrate's canonical discriminator for the "runtime-closure
2193    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
2194    /// consumer dispatches on — the compiler-checked exhaustiveness on
2195    /// the enum's `match` arms is the build-time guarantee that no future
2196    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
2197    /// that a future third dep-list axis (a `:deps-build` build-only
2198    /// closure once the substrate grows cross-artifact heterogeneous
2199    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
2200    /// consumer. Prior to this the read side carried two per-slot
2201    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
2202    /// typed dispatch that a per-axis walker could parametrise on, so
2203    /// every per-list walker (the [`Self::validate_deps`] per-list
2204    /// [`crate::render::insert_first_seen`] dedup walk, a future
2205    /// `feira app graph` per-list dep summary, a future M4 per-cluster
2206    /// dev-closure-audit overlay the CR materializer resolves per-CR)
2207    /// open-coded the same two-block "run over `:deps`, then run over
2208    /// `:deps-dev`" pattern — a silent duplication that a future third
2209    /// dep-list axis would have had to grow a third block at every site.
2210    ///
2211    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
2212    /// (359fba5) — closes the two-side dispatch symmetry on the outer
2213    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
2214    /// side, `deps_of` on the read side, both keyed on the same
2215    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
2216    /// the substrate primitive, thin projections at each consumer"
2217    /// discipline the sibling per-slot read accessors ([`Self::nome`]
2218    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
2219    /// the outer-[`Caixa`] typed-dispatch read surface.
2220    ///
2221    /// Declared `pub const fn` — every operator in the body is already
2222    /// `const`-callable (the [`crate::dep::DepList`] enum is a plain
2223    /// closed-set `#[derive(Copy)]` discriminator so the `match` arms
2224    /// are const-evaluable, and each arm forwards through the sibling
2225    /// `pub const fn` [`Self::deps`] / [`Self::deps_dev`] per-slot
2226    /// slice accessor). Pinned load-bearing by the paired
2227    /// [`caixa_deps_of_is_const_fn`][pin] wrapper test (a
2228    /// `const fn deps_of_via_const_fn(c: &Caixa, l: DepList) -> &[Dep]`
2229    /// that forwards through this accessor) — any future accidental
2230    /// downgrade to non-`const` fails the wrapper at caixa-core build
2231    /// time with E0015 (`cannot call non-const method`), strictly
2232    /// stronger than a runtime `assert!` and side-stepping the
2233    /// destructor-in-const restriction the `Caixa` fixture's owning
2234    /// carriers rule out on the direct-`const _: () = assert!(…)`
2235    /// residence. Peer of the sibling per-`Dep` outer-accessor
2236    /// family's parallel `const`-eval-surface pass and of the outer-
2237    /// `Caixa` slice-return accessor family's earlier pass (231a968)
2238    /// — same "one canonical dispatch per axis, `const`-eval posture
2239    /// pinned at the substrate primitive, thin projections at each
2240    /// consumer" discipline extended onto the outer-`Caixa`
2241    /// typed-dispatch read surface on the [`DepList`]-keyed dep-list
2242    /// axis.
2243    ///
2244    /// [DepList]: crate::dep::DepList
2245    /// [pin]: tests::caixa_deps_of_is_const_fn
2246    #[must_use]
2247    pub const fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2248        match list {
2249            crate::dep::DepList::Prod => self.deps(),
2250            crate::dep::DepList::Dev => self.deps_dev(),
2251        }
2252    }
2253
2254    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2255    /// consumer that appends to one of the two dep-list axes keys off
2256    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2257    /// method on the substrate primitive rather than the prior
2258    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2259    /// else { &mut caixa.deps }` inline dispatch + open-coded
2260    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2261    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2262    /// a within-list name collision — the same `list: &'static str`
2263    /// diagnostic shape [`Self::validate_deps`]'s per-list
2264    /// [`crate::render::insert_first_seen`] walk raises on the peer
2265    /// parse-time within-list dedup axis, so a future author reading a
2266    /// `feira add` refusal and a `feira build` refusal reaches for the
2267    /// same corrective surface without switching diagnostic idioms.
2268    ///
2269    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2270    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2271    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2272    /// dispatches on — the compiler-checked exhaustiveness on the
2273    /// enum's `match` arms is the build-time guarantee that no future
2274    /// per-list mutation-site regresses to a bare-`bool`-flag
2275    /// (`is_dev: bool`) inline dispatch that a future third
2276    /// dep-list axis (a `:deps-build` build-only closure once the
2277    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2278    /// CAIXA-SDLC §I) would silently split at every consumer.
2279    ///
2280    /// Same "one typed dispatch on the substrate primitive, thin
2281    /// projections at each consumer" discipline the sibling per-slot
2282    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2283    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2284    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2285    /// the substrate's first typed-mutation dispatch on the top-level
2286    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2287    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2288    /// diagnostic path routed no through-line back to the typed slot,
2289    /// so a future extension of either dep-list axis to a richer author
2290    /// surface (a per-cluster override the operator pins through a
2291    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2292    /// roadmap acknowledges, an M4
2293    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2294    /// admission-webhook that normalized the list at admission time)
2295    /// would have had to be threaded through the `feira add` mutation
2296    /// site in lockstep with every read consumer or one path would
2297    /// silently disagree with the other on which list a given dep lands
2298    /// in. Lifting the resolution rule to a typed method on the
2299    /// substrate primitive means every downstream dep-list-mutating
2300    /// consumer of the top-level manifest reaches for exactly one typed
2301    /// dispatch — the resolver's accept-set migrates as a unit on any
2302    /// future axis addition.
2303    ///
2304    /// # Errors
2305    ///
2306    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2307    /// when another entry in the same list already carries the same
2308    /// `:nome` — the mutation is refused and the caller can surface the
2309    /// typed diagnostic to the author (the `feira add` verb routes the
2310    /// error through `anyhow::Error::from`, which preserves the
2311    /// canonical `#[error(...)]`-templated diagnostic body).
2312    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2313        let target = match list {
2314            crate::dep::DepList::Prod => &mut self.deps,
2315            crate::dep::DepList::Dev => &mut self.deps_dev,
2316        };
2317        if target.iter().any(|d| d.nome() == dep.nome()) {
2318            return Err(DepError::duplicate_nome(dep.nome(), list.as_str()));
2319        }
2320        target.push(dep);
2321        Ok(())
2322    }
2323
2324    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2325    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2326    /// composite-reference accessor every consumer of the top-level
2327    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2328    /// off — returns the author-declared `:limits` typed composite
2329    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2330    /// backing storage the raw `self.limits.as_ref()` field access
2331    /// borrows from, with `None` naming the "no `:limits` block
2332    /// authored — every per-axis Lunatic-sandbox cap defers to the
2333    /// wasm-engine-default arm named on the per-axis
2334    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2335    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2336    /// docstrings" partition every downstream Servico-M2-overlay
2337    /// emitter treats as "emit nothing" and the sibling
2338    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2339    /// treats as "skip the per-axis
2340    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2341    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2342    ///
2343    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2344    /// composite — the load-bearing container of every Lunatic-shaped
2345    /// per-process wasm32-sandbox cap axis every long-running wasm
2346    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2347    /// Lunatic per-process linear-memory / fuel / wall-clock /
2348    /// millicore cap primitives translated onto pleme-io's typed
2349    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2350    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2351    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2352    /// chart both fan on). Every per-`:limits` axis threads through a
2353    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2354    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2355    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2356    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2357    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2358    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2359    /// consumer that reaches for a limits axis first passes through
2360    /// this outer accessor onto the composite and then dispatches
2361    /// onto the per-axis accessor — the two-level dispatch means
2362    /// every per-`:limits` reader now routes through a typed dispatch
2363    /// on the substrate primitive at both altitudes.
2364    ///
2365    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2366    /// was accessed inline at three production sites — the
2367    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2368    /// `if let Some(l) = &caixa.limits { … }` traversal head
2369    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2370    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2371    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2372    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2373    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2374    /// [`LimitsSpec::validate`] fans onto), the
2375    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2376    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2377    /// head (caixa-core/src/render.rs:18504, which drives the
2378    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2379    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2380    /// block emitter fans on), and the
2381    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2382    /// set enumerator's `self.limits.is_some()` presence probe
2383    /// (caixa-core/src/manifest.rs:1788, which drives the
2384    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2385    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2386    /// gate reads) — three open-coded outer-field accesses that
2387    /// expressed no compile-time link back to the typed slot at the
2388    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2389    /// axis to a richer author surface (a multi-`:limits` list the M4
2390    /// CR materializer resolves per-CR at admission time so a Servico
2391    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2392    /// cluster `:limits-overrides` slot the operator pins so a
2393    /// cluster-specific policy can tighten a caixa-declared cap
2394    /// without re-authoring the `caixa.lisp`, a promotion of the
2395    /// plain `Option<LimitsSpec>` to a richer
2396    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2397    /// resolved dynamic-cap surface lands) would have had to be
2398    /// threaded through all three open-coded copies in lockstep or
2399    /// one consumer would silently disagree with the peers on which
2400    /// limits composite a given Caixa resolves to — the layout gate's
2401    /// per-axis bracket-dispatch seed reading the raw slot while the
2402    /// peer `servico_m2_overlay` emitter read an operator-resolved
2403    /// slot would silently split the build-time sandbox-shape gate
2404    /// from the runtime `ComputeUnit` CR emission gate, a three-
2405    /// consumer split at the layout gate, the M2 overlay emitter, and
2406    /// the declared-slot enumerator far from the source `caixa.lisp`
2407    /// with no field naming the limits-drift root cause. Lifting the
2408    /// resolution rule to a typed method on the substrate primitive
2409    /// means every downstream consumer of the caixa's per-`Caixa`
2410    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2411    /// one typed dispatch — the resolver's accept-set migrates as a
2412    /// unit on any future axis addition.
2413    ///
2414    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2415    /// composite-reference accessor — opens the outer-`Caixa`
2416    /// `Option<&Composite>` composite-reference projection pattern the
2417    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2418    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2419    /// [`crate::aplicacao::Placement`] / `:entrada`
2420    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2421    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2422    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2423    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2424    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2425    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2426    /// altitude — extends that "one typed dispatch on the substrate
2427    /// primitive, thin projections at each consumer" discipline onto
2428    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2429    /// runtime slot family's outer-composite axis. Returns
2430    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2431    /// clone) because every downstream consumer of the limits
2432    /// composite treats it as a read-only per-axis dispatch source —
2433    /// the reference-view is the narrowest borrow that supports every
2434    /// present + roadmapped consumer (per-axis accessor dispatch,
2435    /// `.is_empty()`-gated overlay projection, presence-probe early
2436    /// return on the "author-omitted `:limits` ⇒ engine-default
2437    /// applies" partition) without cloning the composite through
2438    /// every consumer's fast path. The `Option` half of the return-
2439    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2440    /// engine-default applies" partition (not a default composite the
2441    /// downstream must reject on emptiness) — the accessor projects
2442    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2443    /// reference-return unchanged. Named `limits()` to match the
2444    /// storage field's name verbatim and the tatara-lisp author-
2445    /// surface term (`:limits`) the field's own docstring already
2446    /// carries.
2447    #[must_use]
2448    pub const fn limits(&self) -> Option<&LimitsSpec> {
2449        self.limits.as_ref()
2450    }
2451
2452    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2453    /// composite OTP-`gen_server`-shaped callback-table optional-
2454    /// composite-reference accessor every consumer of the top-level
2455    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2456    /// keys off — returns the author-declared `:behavior` typed
2457    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2458    /// the same backing storage the raw `self.behavior.as_ref()` field
2459    /// access borrows from, with `None` naming the "no `:behavior`
2460    /// block authored — every per-callback OTP-shaped hook defers to
2461    /// the wasm-engine's runtime default arm named on the per-axis
2462    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2463    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2464    /// [`BehaviorSpec::on_state_change`] /
2465    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2466    /// partition every downstream Servico-M2-overlay emitter treats as
2467    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2468    /// per-`:behavior` shape gate treats as "skip the per-arm
2469    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2470    /// per-callback on-disk `MissingEntry` existence check".
2471    ///
2472    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2473    /// composite — the load-bearing container of every OTP-shaped
2474    /// per-Servico lifecycle-callback path axis every long-running wasm
2475    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2476    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2477    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2478    /// translated onto pleme-io's typed `:behavior :on-init` /
2479    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2480    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2481    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2482    /// chart both fan on). Every per-`:behavior` axis threads through a
2483    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2484    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2485    /// Every downstream consumer that reaches for a behavior axis
2486    /// first passes through this outer accessor onto the composite
2487    /// and then dispatches onto the per-callback accessor — the
2488    /// two-level dispatch means every per-`:behavior` reader now
2489    /// routes through a typed dispatch on the substrate primitive at
2490    /// both altitudes.
2491    ///
2492    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2493    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2494    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2495    /// keys the "per-version `:state-change` instruction must have a
2496    /// `:on-state-change` callback" precondition off this accessor's
2497    /// composite (the callback-side counterpart to the
2498    /// `:upgrade-from :instructions :state-change :script` refusal at
2499    /// the appup-side). Threading that gate's traversal input through
2500    /// this accessor closes the cross-slot invariant on the substrate
2501    /// primitive, not on the raw field.
2502    ///
2503    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2504    /// composite was accessed inline at four production sites — the
2505    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2506    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2507    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2508    /// `BehaviorError` refusal cascade + the per-callback on-disk
2509    /// [`crate::LayoutError::MissingEntry`] existence check under
2510    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2511    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2512    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2513    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2514    /// drives the `:state-change` ↔ `:on-state-change` precondition
2515    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2516    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2517    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2518    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2519    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2520    /// Servico values-block emitter fans on), and the
2521    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2522    /// set enumerator's `self.behavior.is_some()` presence probe
2523    /// (caixa-core/src/manifest.rs:1919, which drives the
2524    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2525    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2526    /// gate reads) — four open-coded outer-field accesses that
2527    /// expressed no compile-time link back to the typed slot at the
2528    /// [`Caixa`] altitude. A future extension of the `:behavior`
2529    /// outer axis to a richer author surface (a per-callback overlay
2530    /// resolver the operator materializes at admission time so a
2531    /// cluster-specific policy can inject a per-callback tracing
2532    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2533    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2534    /// dynamic}` partition once a runtime-resolved behavior-swap
2535    /// surface lands, the M4 per-callback middleware chain the
2536    /// caixa-operator's per-Servico admission webhook keys off) would
2537    /// have had to be threaded through all four open-coded copies in
2538    /// lockstep or one consumer would silently disagree with the
2539    /// peers on which behavior composite a given Caixa resolves to —
2540    /// the layout gate's per-callback existence-check seed reading
2541    /// the raw slot while the peer `servico_m2_overlay` emitter read
2542    /// an operator-resolved slot would silently split the build-time
2543    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2544    /// gate from the cross-slot `:state-change` composition gate from
2545    /// the M2 declared-slot enumerator, a four-consumer split far
2546    /// from the source `caixa.lisp` with no field naming the
2547    /// behavior-drift root cause. Lifting the resolution rule to a
2548    /// typed method on the substrate primitive means every downstream
2549    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2550    /// composite surface reaches for exactly one typed dispatch — the
2551    /// resolver's accept-set migrates as a unit on any future axis
2552    /// addition.
2553    ///
2554    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2555    /// composite-reference accessor — sibling to the opening
2556    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2557    /// `Option<&Composite>` composite-reference sub-family, extends
2558    /// the "one typed dispatch on the substrate primitive, thin
2559    /// projections at each consumer" discipline onto the second of
2560    /// the three M2 Servico-runtime slots. The remaining
2561    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2562    /// altitude — the M3 mesh-slot family (`:politicas`,
2563    /// `:placement`, `:entrada` — already closed on the inner
2564    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2565    /// d32111c) — remain the future sibling lifts on the outer
2566    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2567    /// the owning composite by copy or clone) because every
2568    /// downstream consumer of the behavior composite treats it as a
2569    /// read-only per-callback dispatch source — the reference-view is
2570    /// the narrowest borrow that supports every present + roadmapped
2571    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2572    /// overlay projection, presence-probe early return on the
2573    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2574    /// partition, cross-slot `:state-change` composition input)
2575    /// without cloning the composite through every consumer's fast
2576    /// path. The `Option` half of the return-type preserves the
2577    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2578    /// applies" partition (not a default composite the downstream
2579    /// must reject on emptiness) — the accessor projects the raw
2580    /// `Option<BehaviorSpec>` slot's presence bit through the
2581    /// reference-return unchanged. Named `behavior()` to match the
2582    /// storage field's name verbatim and the tatara-lisp author-
2583    /// surface term (`:behavior`) the field's own docstring already
2584    /// carries.
2585    #[must_use]
2586    pub const fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2587        self.behavior.as_ref()
2588    }
2589
2590    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2591    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2592    /// reference accessor every consumer of the top-level manifest's
2593    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2594    /// reader keys off — returns the author-declared `:politicas` typed
2595    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2596    /// same backing storage the raw `self.politicas.as_ref()` field
2597    /// access borrows from, with `None` naming the "no `:politicas`
2598    /// block authored — every per-axis mesh-policy scalar defers to the
2599    /// cluster-default arm named on the per-axis
2600    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2601    /// [`crate::aplicacao::MeshPolicy::retries`] /
2602    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2603    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2604    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2605    /// docstrings" partition every downstream caixa-mesh /
2606    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2607    /// "emit no per-`:politicas` overlay" and the sibling
2608    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2609    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2610    /// arm.
2611    ///
2612    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2613    /// Aplicacao typed composite — the load-bearing container of every
2614    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2615    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2616    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2617    /// composite; §V — the "no infinite blocking" per-call deadline +
2618    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2619    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2620    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2621    /// threads through a lifted per-slot accessor on the
2622    /// [`crate::aplicacao::MeshPolicy`] type: the
2623    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2624    /// mTLS-enforcement toggle, the
2625    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2626    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2627    /// (7073d0f) Gateway-API per-call deadline, the
2628    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2629    /// Envoy-outlier-detection composite. Every downstream consumer
2630    /// that reaches for a mesh-policy axis first passes through this
2631    /// outer accessor onto the composite and then dispatches onto the
2632    /// per-axis accessor — the two-level dispatch means every per-
2633    /// `:politicas` reader now routes through a typed dispatch on the
2634    /// substrate primitive at both altitudes.
2635    ///
2636    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2637    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2638    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2639    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2640    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2641    /// composite whether or not the author declared the outer slot.
2642    /// The outer accessor preserves the "author-omitted vs authored-
2643    /// empty" partition the inner accessor's `is_empty()`-gated
2644    /// renderer overlay collapses — routing the presence bit through
2645    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2646    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2647    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2648    ///
2649    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2650    /// composite was accessed inline at two production sites — the
2651    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2652    /// `self.politicas.clone().unwrap_or_default()` traversal head
2653    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2654    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2655    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2656    /// then observes), and the [`Self::declared_mesh_slots`] M3
2657    /// declared-slot-set enumerator's `self.politicas.is_some()`
2658    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2659    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2660    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2661    /// coherence gate reads) — two open-coded outer-field accesses
2662    /// that expressed no compile-time link back to the typed slot at
2663    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2664    /// outer axis to a richer author surface (a per-cluster
2665    /// `:politicas-overrides` slot the operator materializes at
2666    /// admission time so a cluster-specific policy can tighten the
2667    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2668    /// promotion of the plain `Option<MeshPolicy>` to a richer
2669    /// `{static, dynamic}` partition once the M4 per-edge
2670    /// contrato-scoped policy-override surface lands, the M5 traffic-
2671    /// shaping composition the caixa-operator's per-Aplicacao mesh
2672    /// admission webhook keys off) would have had to be threaded
2673    /// through both open-coded copies in lockstep or the Aplicacao-
2674    /// composition seed's default-fold arm would silently disagree
2675    /// with the M3 declared-slot enumerator on which policy composite
2676    /// a given Caixa resolves to — the seed reading an operator-
2677    /// resolved slot while the enumerator's presence probe read the
2678    /// raw slot would silently split the build-time mesh-artifact
2679    /// emission gate from the M3 declared-slot enumerator's kind-
2680    /// coherence gate, a two-consumer split far from the source
2681    /// `caixa.lisp` with no field naming the policy-drift root cause.
2682    /// Lifting the resolution rule to a typed method on the substrate
2683    /// primitive means every downstream consumer of the caixa's per-
2684    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2685    /// reaches for exactly one typed dispatch — the resolver's
2686    /// accept-set migrates as a unit on any future axis addition.
2687    ///
2688    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2689    /// composite-reference accessor — sibling to the opening
2690    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2691    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2692    /// reference sub-family, extends the "one typed dispatch on the
2693    /// substrate primitive, thin projections at each consumer"
2694    /// discipline onto the first of the three M3 mesh-slot axes.
2695    /// Peer of the closed inner mesh-slot outer-composite family the
2696    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2697    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2698    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2699    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2700    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2701    /// mesh-slot arm of the composite-reference family the remaining
2702    /// two axes (`:placement`, `:entrada`) fold onto in future
2703    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2704    /// composite by copy or clone) because every downstream consumer
2705    /// of the mesh-policy composite treats it as a read-only per-axis
2706    /// dispatch source — the reference-view is the narrowest borrow
2707    /// that supports every present + roadmapped consumer (per-axis
2708    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2709    /// presence-probe early return on the "author-omitted `:politicas`
2710    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2711    /// seed's default-fold arm) without cloning the composite through
2712    /// every consumer's fast path. The `Option` half of the return-
2713    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2714    /// cluster-default applies" partition (not a default composite
2715    /// the downstream must reject on emptiness) — the accessor
2716    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2717    /// through the reference-return unchanged. Named `politicas()` to
2718    /// match the storage field's name verbatim and the tatara-lisp
2719    /// author-surface term (`:politicas`) the field's own docstring
2720    /// already carries.
2721    #[must_use]
2722    pub const fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2723        self.politicas.as_ref()
2724    }
2725
2726    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2727    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2728    /// reference accessor every consumer of the top-level manifest's
2729    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2730    /// reader keys off — returns the author-declared `:placement` typed
2731    /// composite verbatim as an `Option<&Placement>` reference over the
2732    /// same backing storage the raw `self.placement.as_ref()` field
2733    /// access borrows from, with `None` naming the "no `:placement`
2734    /// block authored — every per-axis placement scalar defers to the
2735    /// cluster-default arm named on the per-axis
2736    /// [`crate::aplicacao::Placement::estrategia`] /
2737    /// [`crate::aplicacao::Placement::clusters`] /
2738    /// [`crate::aplicacao::Placement::affinity`] /
2739    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2740    /// docstrings" partition every downstream caixa-mesh /
2741    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2742    /// "emit no per-`:placement` overlay" and the sibling
2743    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2744    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2745    ///
2746    /// The outer `:placement` slot carries the M3 mesh-slot per-
2747    /// Aplicacao typed distribution composite — the load-bearing
2748    /// container of every where-does-this-Aplicacao-run axis every
2749    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2750    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2751    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2752    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2753    /// Aplicacao's typed distribution composite; §V CSE invariants —
2754    /// "distribution is a first-class typed composite, not a runtime
2755    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2756    /// typed inter-Servico contrato-edge overlay the per-cluster
2757    /// mesh renderer keys off). Every per-`:placement` axis threads
2758    /// through a lifted per-slot accessor on the
2759    /// [`crate::aplicacao::Placement`] type: the
2760    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2761    /// MESH-COMPOSITION distribution-strategy scalar, the
2762    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2763    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2764    /// M3-Adaptive-compression-hint optional-scalar, and the
2765    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2766    /// sharding extractor-expression optional-scalar. Every downstream
2767    /// consumer that reaches for a placement axis first passes through
2768    /// this outer accessor onto the composite and then dispatches onto
2769    /// the per-axis accessor — the two-level dispatch means every per-
2770    /// `:placement` reader now routes through a typed dispatch on the
2771    /// substrate primitive at both altitudes.
2772    ///
2773    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2774    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2775    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2776    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2777    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2778    /// whether or not the author declared the outer slot. The outer
2779    /// accessor preserves the "author-omitted vs authored-empty" partition
2780    /// the inner accessor collapses at the cluster-default fold —
2781    /// routing the presence bit through this accessor keeps the
2782    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2783    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2784    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2785    /// dispatch.
2786    ///
2787    /// Prior to this lift the `.placement` `Option<Placement>`
2788    /// composite was accessed inline at two production sites — the
2789    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2790    /// `self.placement.clone().unwrap_or_default()` traversal head
2791    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2792    /// the [`crate::aplicacao::Placement::default`] cluster-default
2793    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2794    /// then observes), and the [`Self::declared_mesh_slots`] M3
2795    /// declared-slot-set enumerator's `self.placement.is_some()`
2796    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2797    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2798    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2799    /// coherence gate reads) — two open-coded outer-field accesses
2800    /// that expressed no compile-time link back to the typed slot at
2801    /// the [`Caixa`] altitude. A future extension of the `:placement`
2802    /// outer axis to a richer author surface (a per-cluster
2803    /// `:placement-overrides` slot the operator materializes at
2804    /// admission time so a cluster-specific placement can tighten the
2805    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2806    /// per-tenant placement-alias table the M4
2807    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2808    /// per-CR at admission time, a promotion of the plain
2809    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2810    /// once Orleans-style virtual-actor dynamic placement comes into
2811    /// typed scope) would have had to be threaded through both open-
2812    /// coded copies in lockstep or the Aplicacao-composition seed's
2813    /// default-fold arm would silently disagree with the M3 declared-
2814    /// slot enumerator on which distribution composite a given Caixa
2815    /// resolves to — the seed reading an operator-resolved slot while
2816    /// the enumerator's presence probe read the raw slot would
2817    /// silently split the build-time distribution-artifact emission
2818    /// gate from the M3 declared-slot enumerator's kind-coherence
2819    /// gate, a two-consumer split far from the source `caixa.lisp`
2820    /// with no field naming the distribution-drift root cause.
2821    /// Lifting the resolution rule to a typed method on the substrate
2822    /// primitive means every downstream consumer of the caixa's per-
2823    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2824    /// reaches for exactly one typed dispatch — the resolver's
2825    /// accept-set migrates as a unit on any future axis addition.
2826    ///
2827    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2828    /// composite-reference accessor — sibling to the opening
2829    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2830    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2831    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2832    /// composite-reference sub-family, folds on the "one typed
2833    /// dispatch on the substrate primitive, thin projections at each
2834    /// consumer" discipline extended onto the second of the three M3
2835    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2836    /// composite family the sibling
2837    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2838    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2839    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2840    /// accessor pins already close on the inner
2841    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2842    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2843    /// [`Self::politicas`] opened, extending the discipline onto the
2844    /// second of the three M3 mesh-slot axes. The remaining M3
2845    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2846    /// discipline in the final sibling lift, closing the outer top-
2847    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2848    /// Returns `Option<&Placement>` (not the owning composite by copy
2849    /// or clone) because every downstream consumer of the placement
2850    /// composite treats it as a read-only per-axis dispatch source —
2851    /// the reference-view is the narrowest borrow that supports every
2852    /// present + roadmapped consumer (per-axis accessor dispatch,
2853    /// serde composite-serialization on the programs.yaml overlay,
2854    /// presence-probe early return on the "author-omitted `:placement`
2855    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2856    /// seed's default-fold arm) without cloning the composite through
2857    /// every consumer's fast path. The `Option` half of the return-
2858    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2859    /// cluster-default applies" partition (not a default composite
2860    /// the downstream must reject on emptiness) — the accessor
2861    /// projects the raw `Option<Placement>` slot's presence bit
2862    /// through the reference-return unchanged. Named `placement()` to
2863    /// match the storage field's name verbatim and the tatara-lisp
2864    /// author-surface term (`:placement`) the field's own docstring
2865    /// already carries.
2866    #[must_use]
2867    pub const fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2868        self.placement.as_ref()
2869    }
2870
2871    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2872    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2873    /// composite-reference accessor every consumer of the top-level
2874    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2875    /// composite reader keys off — returns the author-declared
2876    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2877    /// reference over the same backing storage the raw
2878    /// `self.entrada.as_ref()` field access borrows from, with `None`
2879    /// naming the "no `:entrada` block authored — this Aplicacao is
2880    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2881    /// partition every downstream caixa-mesh Gateway-API artifact
2882    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2883    /// backend for this Aplicacao" and the sibling
2884    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2885    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2886    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2887    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2888    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2889    /// the same `Option<&Entrada>` presence bit unchanged).
2890    ///
2891    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2892    /// Aplicacao typed external-gateway composite — the load-bearing
2893    /// container of every how-does-the-outside-world-reach-this-
2894    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2895    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2896    /// external-entry composite; §V CSE invariants — "the external
2897    /// gateway is a first-class typed composite, not a per-Servico
2898    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2899    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2900    /// API renderer keys off). Every per-`:entrada` axis threads
2901    /// through a lifted per-slot accessor on the
2902    /// [`crate::aplicacao::Entrada`] type: the
2903    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2904    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2905    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2906    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2907    /// backend `trigger.service.port` scalar, and the
2908    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2909    /// resolver every HTTPRoute-aware renderer consumes. Every
2910    /// downstream consumer that reaches for an entry axis first passes
2911    /// through this outer accessor onto the composite and then
2912    /// dispatches onto the per-axis accessor — the two-level dispatch
2913    /// means every per-`:entrada` reader now routes through a typed
2914    /// dispatch on the substrate primitive at both altitudes.
2915    ///
2916    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2917    /// seed: the Aplicacao-view builder forwards the outer `Option`
2918    /// arm verbatim (no default fold — `:entrada` is inherently
2919    /// optional; a cluster-internal Aplicacao has no external gateway
2920    /// at all, not "an external gateway that defaults to nothing"), so
2921    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2922    /// `Option<&Entrada>`-return accessor observes the same presence
2923    /// bit whether or not the author declared the outer slot. Routing
2924    /// the presence bit through this accessor keeps the
2925    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2926    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2927    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2928    /// hostname/backend/path emission dispatch.
2929    ///
2930    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2931    /// was accessed inline at two production sites — the
2932    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2933    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2934    /// which drives the forward onto the peer inner
2935    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2936    /// Gateway-API fan-out then observes), and the
2937    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2938    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2939    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2940    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2941    /// kind-coherence gate reads) — two open-coded outer-field
2942    /// accesses that expressed no compile-time link back to the typed
2943    /// slot at the [`Caixa`] altitude. A future extension of the
2944    /// `:entrada` outer axis to a richer author surface (a per-cluster
2945    /// `:entrada-overrides` slot the operator materializes at admission
2946    /// time so a cluster-specific hostname can pin the caixa-declared
2947    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2948    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2949    /// CR materializer resolves per-CR at admission time, a promotion
2950    /// of the plain `Option<Entrada>` to a richer
2951    /// `{public, private, internal}` partition once Cilium-identity-
2952    /// scoped internal gateways come into typed scope) would have had
2953    /// to be threaded through both open-coded copies in lockstep or the
2954    /// Aplicacao-composition seed's forward arm would silently
2955    /// disagree with the M3 declared-slot enumerator on which external-
2956    /// gateway composite a given Caixa resolves to — the seed reading
2957    /// an operator-resolved slot while the enumerator's presence probe
2958    /// read the raw slot would silently split the build-time gateway-
2959    /// artifact emission gate from the M3 declared-slot enumerator's
2960    /// kind-coherence gate, a two-consumer split far from the source
2961    /// `caixa.lisp` with no field naming the entry-drift root cause.
2962    /// Lifting the resolution rule to a typed method on the substrate
2963    /// primitive means every downstream consumer of the caixa's per-
2964    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2965    /// surface reaches for exactly one typed dispatch — the resolver's
2966    /// accept-set migrates as a unit on any future axis addition.
2967    ///
2968    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2969    /// return composite-reference accessor — closes the outer-`Caixa`
2970    /// `Option<&Composite>` composite-reference sub-family opened by
2971    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2972    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2973    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2974    /// folds on the "one typed dispatch on the substrate primitive,
2975    /// thin projections at each consumer" discipline extended onto the
2976    /// third and final M3 mesh-slot axis. Peer of the closed inner
2977    /// mesh-slot outer-composite family the sibling
2978    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2979    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2980    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2981    /// accessor pins already close on the inner
2982    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2983    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2984    /// altitudes of the outer-composite reference-return discipline
2985    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2986    /// slot presence) now carry the full five-arm accept-set behind a
2987    /// typed dispatch on the substrate primitive. Returns
2988    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2989    /// because every downstream consumer of the entrada composite
2990    /// treats it as a read-only per-axis dispatch source — the
2991    /// reference-view is the narrowest borrow that supports every
2992    /// present + roadmapped consumer (per-axis accessor dispatch,
2993    /// serde composite-serialization on the programs.yaml overlay,
2994    /// presence-probe early return on the "author-omitted `:entrada`
2995    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2996    /// seed's forward arm) without cloning the composite through every
2997    /// consumer's fast path. The `Option` half of the return-type
2998    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2999    /// cluster-internal Aplicacao" partition (not a default composite
3000    /// the downstream must reject on emptiness — a cluster-internal
3001    /// Aplicacao has no external gateway at all, not "a default gateway
3002    /// that emits nothing"); the accessor projects the raw
3003    /// `Option<Entrada>` slot's presence bit through the reference-
3004    /// return unchanged. Named `entrada()` to match the storage field's
3005    /// name verbatim and the tatara-lisp author-surface term
3006    /// (`:entrada`) the field's own docstring already carries.
3007    #[must_use]
3008    pub const fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
3009        self.entrada.as_ref()
3010    }
3011
3012    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
3013    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
3014    /// an `Option<&CiRun>`, borrowed from the typed slot's own
3015    /// `Option<CiRun>` storage. `None` when the slot is absent (every
3016    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
3017    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
3018    /// not silently accepted).
3019    ///
3020    /// Named `ci()` to match the storage field's name and the
3021    /// tatara-lisp author surface (`:ci`); mirrors the sibling
3022    /// `Option<&Composite>` accessors on this same `Caixa` altitude
3023    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
3024    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
3025    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
3026    /// at every consumer.
3027    #[must_use]
3028    pub const fn ci(&self) -> Option<&canteiro_types::CiRun> {
3029        self.ci.as_ref()
3030    }
3031
3032    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
3033    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
3034    /// accessor every consumer of the top-level manifest's per-Supervisor
3035    /// restart-strategy axis keys off — returns the author-declared
3036    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
3037    /// `Copy`-projected from the typed slot's own
3038    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
3039    /// (`:estrategia` is a flat-spread supervisor-only slot every
3040    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
3041    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3042    /// still omit to defer to [`RestartStrategy::default`] —
3043    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
3044    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
3045    /// [`SupervisorSpec::default`]-inherited strategy without any silent
3046    /// promotion to a fresh explicit variant at the accessor boundary).
3047    ///
3048    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
3049    /// restart-strategy discriminant every substrate-side per-Supervisor
3050    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
3051    /// closed-set `one_for_one | one_for_all | rest_for_one |
3052    /// simple_one_for_one` algebra translated onto pleme-io's typed
3053    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
3054    /// slot algebra the operator's hierarchical reconciliation scheduler
3055    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
3056    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
3057    /// supervisor slots are flat on Caixa (vs nested under a
3058    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
3059    /// level of nesting"), so the accessor's altitude is the outer
3060    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
3061    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
3062    /// (eafb619) accessor keys off. The two typed axes — the outer
3063    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
3064    /// (author-omitted arm carried as `None`) and the inner post-
3065    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
3066    /// (`Option` collapsed through the [`Self::supervisor_view`]
3067    /// `unwrap_or_default()` fold) — now share one accessor discipline for
3068    /// the shared substrate concept "the author-declared OTP-shaped
3069    /// sibling-restart-strategy variant that partitions the downstream
3070    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
3071    /// `None` arm is the pre-composition presence bit every declared-slot
3072    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
3073    /// inner-altitude non-`Option` `RestartStrategy` is the post-
3074    /// composition partition-dispatch input every strategy-arm consumer
3075    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
3076    /// Supervisor sibling-restart branch, the future M4
3077    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
3078    /// webhook) fans on.
3079    ///
3080    /// Prior to this lift the `.estrategia` field was accessed inline at
3081    /// two production sites in `caixa-core/src/manifest.rs` — the
3082    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
3083    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
3084    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3085    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
3086    /// `SupervisorSpec` construction site at `estrategia:
3087    /// self.estrategia.unwrap_or_default()` (which composes the flat-
3088    /// spread outer author-surface `Option<RestartStrategy>` onto the
3089    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
3090    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
3091    /// coded field-accesses that expressed no compile-time link back to
3092    /// the typed slot. A future extension of the outer `:estrategia` axis
3093    /// to a richer author surface (a per-cluster strategy override the
3094    /// operator pins through a future `:estrategia-overrides` overlay the
3095    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
3096    /// a per-tenant strategy-alias table the M4 CR materializer resolves
3097    /// per-CR, a per-Supervisor dynamic strategy derivation the future
3098    /// adaptive-supervision engine computes from child-failure-history
3099    /// topology, a per-child-cohort strategy split the future
3100    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
3101    /// absorption roadmap acknowledges, a promotion of the plain
3102    /// `Option<RestartStrategy>` to a richer
3103    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
3104    /// operator-resolved overlay lands) would have had to be threaded
3105    /// through both open-coded copies in lockstep or the enumerator's
3106    /// presence probe and the composition site's `unwrap_or_default()`
3107    /// fold would silently disagree on which strategy a given [`Caixa`]
3108    /// resolves to (an author's `:estrategia OneForAll` would satisfy
3109    /// the enumerator's presence probe while the composition site
3110    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3111    /// the resolution rule to a typed method on the substrate primitive
3112    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3113    /// altitude sibling-restart-strategy surface reaches for exactly one
3114    /// typed dispatch — the resolver's accept-set migrates as a unit on
3115    /// any future axis addition.
3116    ///
3117    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3118    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3119    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3120    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3121    /// `Option<u32>` and (through the future duration-newtype landing)
3122    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3123    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3124    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3125    /// the post-composition [`SupervisorSpec`] altitude — same "one
3126    /// typed dispatch on the substrate primitive, thin projections at
3127    /// each consumer" discipline extended onto the pre-composition outer
3128    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3129    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3130    /// `Option<&Composite>` composite-reference family the sibling
3131    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3132    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3133    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3134    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3135    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3136    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3137    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3138    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3139    /// pins on the inner-altitude per-`:placement` composite. Named
3140    /// `estrategia()` to match the storage field's name and the
3141    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3142    /// / per-[`crate::aplicacao::Placement`] peer
3143    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3144    /// verbatim; the accessor's identity name maps onto the canonical
3145    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3146    /// docstring already carries.
3147    #[must_use]
3148    pub const fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3149        self.estrategia
3150    }
3151
3152    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3153    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3154    /// scalar accessor every consumer of the top-level manifest's per-
3155    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3156    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3157    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3158    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3159    /// accessor returns by value; no borrow of `&self` past the call).
3160    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3161    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3162    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3163    /// still omit to defer to the [`Self::supervisor_view`]
3164    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3165    ///
3166    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3167    /// `MaxIntensity` restart-budget count that pairs with the sibling
3168    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3169    /// restart-intensity ratio the supervisor trips its own escalation on
3170    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3171    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3172    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3173    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3174    /// the outer top-level `Caixa` (per the field-shape docstring at
3175    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3176    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3177    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3178    /// composed [`SupervisorSpec`] altitude the sibling
3179    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3180    /// off. The two typed axes — the outer author-surface `Option<u32>`
3181    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3182    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3183    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3184    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3185    /// shared substrate concept "the author-declared OTP-shaped
3186    /// restart-budget count every downstream per-Supervisor consumer's
3187    /// restart-intensity budget-vs-count comparator fans on".
3188    ///
3189    /// Prior to this lift the `.max_restarts` field was accessed inline
3190    /// at two production sites in `caixa-core/src/manifest.rs` — the
3191    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3192    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3193    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3194    /// kind-coherence gate's per-slot label push) and the
3195    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3196    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3197    /// flat-spread outer author-surface `Option<u32>` onto the inner
3198    /// post-composition [`SupervisorSpec`] `u32` field the
3199    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3200    /// coded field-accesses that expressed no compile-time link back to
3201    /// the typed slot. A future extension of the outer `:max-restarts`
3202    /// axis to a richer author surface (a per-cluster restart-budget
3203    /// override the operator pins through a future `:max-restarts-overrides`
3204    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3205    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3206    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3207    /// budget derivation the future adaptive-supervision engine computes
3208    /// from child-failure-history topology, a promotion of the plain
3209    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3210    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3211    /// per-child-cohort roadmap lands) would have had to be threaded
3212    /// through both open-coded copies in lockstep or the enumerator's
3213    /// presence probe and the composition site's `unwrap_or(5)` fold
3214    /// would silently disagree on which restart-budget a given [`Caixa`]
3215    /// resolves to (an author's `:max-restarts 10` would satisfy the
3216    /// enumerator's presence probe while the composition site silently
3217    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3218    /// resolution rule to a typed method on the substrate primitive means
3219    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3220    /// restart-budget-count surface reaches for exactly one typed dispatch
3221    /// — the resolver's accept-set migrates as a unit on any future axis
3222    /// addition.
3223    ///
3224    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3225    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3226    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3227    /// projection pattern the sibling per-`Caixa`
3228    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3229    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3230    /// Peer of the inner-altitude
3231    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3232    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3233    /// typed dispatch on the substrate primitive, thin projections at
3234    /// each consumer" discipline extended onto the pre-composition outer
3235    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3236    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3237    /// the storage field's name and the per-[`SupervisorSpec`] peer
3238    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3239    /// discipline verbatim; the accessor's identity maps onto the
3240    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3241    /// field's docstring already carries.
3242    #[must_use]
3243    pub const fn max_restarts(&self) -> Option<u32> {
3244        self.max_restarts
3245    }
3246
3247    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3248    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3249    /// denominator raw-duration-string scalar accessor every consumer of
3250    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3251    /// window axis keys off — returns the author-declared `:restart-window`
3252    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3253    /// from the typed slot's own `Option<String>` storage. `None` when
3254    /// the slot is absent (the canonical "never reset — every restart
3255    /// across the supervisor's lifetime counts against the sibling
3256    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3257    /// `defcaixa` carries by `#[serde(default)]` and every
3258    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3259    /// [`Self::supervisor_view`] `restart_window: None` composition
3260    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3261    /// swallow `.and_then(|s| … .ok())` fold).
3262    ///
3263    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3264    /// shaped `Period` sliding-observation-interval duration string that
3265    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3266    /// budget count to form the `MaxIntensity / Period` restart-intensity
3267    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3268    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3269    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3270    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3271    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3272    /// holds an `Option<Duration>` routed through the shared
3273    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3274    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3275    /// authoring surface) while the inner altitude's
3276    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3277    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3278    /// is closed by the sibling [`Self::validate_restart_window`] gate
3279    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3280    /// the offending value; the view-construction path
3281    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3282    /// `None` to keep the view best-effort.
3283    ///
3284    /// Prior to this lift the `.restart_window` field was accessed inline
3285    /// at three production sites in `caixa-core/src/manifest.rs` — the
3286    /// [`Self::declared_supervisor_slots`]
3287    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3288    /// `if self.restart_window.is_some()` (which drives the
3289    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3290    /// coherence gate's per-slot label push), the
3291    /// [`Self::validate_restart_window`] `let Some(s) =
3292    /// self.restart_window.as_deref()` empty-and-shape gate binding
3293    /// (which folds the raw string through the shared
3294    /// [`crate::supervisor::duration_codec::parse`] to surface
3295    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3296    /// value), and the [`Self::supervisor_view`] `self.restart_window
3297    /// .as_deref().and_then(…)` view-construction fold (which composes
3298    /// the flat-spread outer author-surface `Option<String>` onto the
3299    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3300    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3301    /// three open-coded field-accesses that expressed no compile-time
3302    /// link back to the typed slot. A future extension of the outer
3303    /// `:restart-window` axis to a richer author surface (a per-cluster
3304    /// window override, a per-tenant window-alias table, a per-Supervisor
3305    /// dynamic window derivation the future adaptive-supervision engine
3306    /// computes from child-failure-history topology, a promotion of the
3307    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3308    /// once the future author-surface parser lands at the [`Caixa`]
3309    /// altitude and the raw-string form is retired) would have had to be
3310    /// threaded through every open-coded copy in lockstep or the three
3311    /// consumers would silently disagree on which raw string a given
3312    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3313    /// method on the substrate primitive means every downstream consumer
3314    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3315    /// string surface reaches for exactly one typed dispatch — the
3316    /// resolver's accept-set migrates as a unit on any future axis
3317    /// addition.
3318    ///
3319    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3320    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3321    /// spread projection pattern the sibling per-`Caixa`
3322    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3323    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3324    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3325    /// string arm (the outer altitude's raw-string form; the inner
3326    /// altitude's parsed [`Duration`] form is the peer
3327    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3328    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3329    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3330    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3331    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3332    /// sub-family already carries — same "one typed dispatch on the
3333    /// substrate primitive, thin projections at each consumer"
3334    /// discipline extended onto the M2 supervisor-tree flat-spread
3335    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3336    /// to match the storage field's name and the per-[`SupervisorSpec`]
3337    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3338    /// method-name discipline verbatim; the accessor's identity maps
3339    /// onto the canonical OTP-shape supervision vocabulary the
3340    /// `:restart-window` field's docstring already carries.
3341    #[must_use]
3342    pub const fn restart_window(&self) -> Option<&str> {
3343        match &self.restart_window {
3344            Some(s) => Some(s.as_str()),
3345            None => None,
3346        }
3347    }
3348
3349    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3350    /// outer-composite OTP-appup-shaped per-prior-version migration-
3351    /// entry-list slice accessor every consumer of the top-level
3352    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3353    /// slice-view keys off — returns the author-declared `:upgrade-from`
3354    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3355    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3356    /// the raw `self.upgrade_from.as_slice()` field access borrows
3357    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3358    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3359    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3360    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3361    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3362    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3363    /// degenerates to an empty slice on that arm without any silent
3364    /// `None` collapse).
3365    ///
3366    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3367    /// migration block — the load-bearing container of every per-
3368    /// prior-`:versao` migration-instruction list the wasm-operator
3369    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3370    /// `.appup` per-prior-version `LoadModule | StateChange |
3371    /// SoftPurge | Purge | Restart` instruction algebra translated
3372    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3373    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3374    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3375    /// threads through a lifted per-entry accessor on the
3376    /// [`UpgradeFromEntry`] type: the
3377    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3378    /// version scalar accessor and the
3379    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3380    /// return per-entry instruction-list accessor (0137e5a). Every
3381    /// downstream consumer of the hot-upgrade path first passes
3382    /// through this outer accessor onto the slice and then dispatches
3383    /// per-entry through the inner accessors — the two-level dispatch
3384    /// means every per-`:upgrade-from` reader now routes through a
3385    /// typed dispatch on the substrate primitive at both altitudes.
3386    ///
3387    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3388    /// slot was accessed inline at production sites across three
3389    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3390    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3391    /// (caixa-core/src/manifest.rs, which drives the
3392    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3393    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3394    /// gate reads), the [`crate::StandardLayout::verify`] per-
3395    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3396    /// layout.rs, which fans onto the
3397    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3398    /// cross-entry duplicate gate, the
3399    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3400    /// SemVer-precedence cross-slot gate, the
3401    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3402    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3403    /// gate, and the per-instruction script-path existence-probe walk
3404    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3405    /// resolve every declared migration script against the layout
3406    /// root), and the [`crate::render::servico_m2_overlay`] per-
3407    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3408    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3409    /// projection (caixa-core/src/render.rs, which drives the
3410    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3411    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3412    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3413    /// A future extension of the outer `:upgrade-from` axis (a per-
3414    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3415    /// resolves at admission time so a cluster-specific migration
3416    /// policy can tighten a caixa-declared step without re-authoring
3417    /// the `caixa.lisp`, promotion of the plain
3418    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3419    /// partition once runtime-resolved hot-upgrade instructions land,
3420    /// per-entry priority annotation once multi-strategy fan-out
3421    /// lands) would have had to be threaded through all six open-
3422    /// coded copies in lockstep or one consumer would silently
3423    /// disagree with the peers on which upgrade slice a given Caixa
3424    /// resolves to — a six-consumer split at the enumerator, the
3425    /// three-stage validate pass, the script-path probe walk, and the
3426    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3427    /// field naming the upgrade-drift root cause. Lifting the
3428    /// resolution rule to a typed method on the substrate primitive
3429    /// means every downstream consumer of the caixa's per-`Caixa`
3430    /// OTP-appup outer-slice surface reaches for exactly one typed
3431    /// dispatch — the resolver's accept-set migrates as a unit on any
3432    /// future axis addition.
3433    ///
3434    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3435    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3436    /// outer-`Caixa` `&[Composite]` composite-slice projection
3437    /// pattern the sibling `:children`
3438    /// [`crate::supervisor::ChildSpec`] / `:membros`
3439    /// [`crate::aplicacao::Membro`] / `:contratos`
3440    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3441    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3442    /// `Option<&Composite>` composite-reference family the sibling
3443    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3444    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3445    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3446    /// `Option<&Composite>` altitude, extended here to the outer-
3447    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3448    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3449    /// (0137e5a) — same "one typed dispatch on the substrate
3450    /// primitive, thin projections at each consumer" discipline
3451    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3452    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3453    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3454    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3455    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3456    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3457    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3458    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3459    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3460    /// slice" projection pattern onto the sibling M2 typed-composite-
3461    /// element axis (`UpgradeFromEntry` composite, matching the
3462    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3463    /// different altitude).
3464    ///
3465    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3466    /// because every downstream consumer of the hot-upgrade list
3467    /// treats it as a read-only sequence — the slice-view is the
3468    /// narrowest borrow that supports every present + roadmapped
3469    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3470    /// serialization through
3471    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3472    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3473    /// the typed view reaches for (the storage-side `Vec` remains
3474    /// reachable through the `pub upgrade_from` field for the
3475    /// mutation-carrying serde round-trip and per-test fixture-
3476    /// mutation paths). Named `upgrade_from()` to match the storage
3477    /// field's `snake_case` name; the kebab-case author-surface tag
3478    /// `:upgrade-from` is the same axis after tatara-lisp's
3479    /// kebab↔snake fold and the accessor's identity maps onto the
3480    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3481    /// already carries.
3482    #[must_use]
3483    pub const fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3484        self.upgrade_from.as_slice()
3485    }
3486
3487    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3488    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3489    /// slice accessor every consumer of the top-level manifest's per-
3490    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3491    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3492    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3493    /// the same backing buffer the raw `self.children.as_slice()` field
3494    /// access borrows from. Empty-slice-carrying (the "no static children
3495    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3496    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3497    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3498    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3499    /// on those arms without any silent `None` collapse).
3500    ///
3501    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3502    /// static-child list — the load-bearing container of every per-
3503    /// child `{caixa, versao, restart}` triple the wasm-operator's
3504    /// hierarchical reconciler dispatches on at supervisor-tree
3505    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3506    /// static-child list translated onto pleme-io's typed
3507    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3508    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3509    /// dispatch fans on). Every per-child axis threads through a lifted
3510    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3511    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3512    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3513    /// version-requirement scalar accessor, and the
3514    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3515    /// per-child post-exit restart-decision-policy discriminant
3516    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3517    /// tree path first passes through this outer accessor onto the
3518    /// slice and then dispatches per-child through the inner accessors
3519    /// — the two-level dispatch means every per-`:children` reader now
3520    /// routes through a typed dispatch on the substrate primitive at
3521    /// both altitudes.
3522    ///
3523    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3524    /// accessed inline at three production sites across two files —
3525    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3526    /// declared-slot enumerator's `!self.children.is_empty()` presence
3527    /// probe (caixa-core/src/manifest.rs, which drives the
3528    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3529    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3530    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3531    /// per-supervisor typed-view composer's `self.children.clone()`
3532    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3533    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3534    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3535    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3536    /// `:children :caixa` self-parent refusal probe's
3537    /// `&caixa.children`-borrowed
3538    /// [`crate::supervisor::validate_no_self_supervision`] input
3539    /// (caixa-core/src/layout.rs, which pins the "no child names the
3540    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3541    /// extension of the outer `:children` axis (a per-cluster
3542    /// `:children-overrides` overlay the wasm-engine operator resolves
3543    /// at admission time so a cluster-specific child-set can tighten
3544    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3545    /// promotion of the plain `Vec<ChildSpec>` to a richer
3546    /// `{static, dynamic}` partition once Erlang/OTP's
3547    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3548    /// axis, per-child priority annotation once multi-strategy fan-out
3549    /// lands) would have had to be threaded through all three open-
3550    /// coded copies in lockstep or one consumer would silently
3551    /// disagree with the peers on which child slice a given Caixa
3552    /// resolves to — the enumerator's presence probe reading the raw
3553    /// slot while the peer view-composer's fold-in path read an
3554    /// operator-resolved slot would silently split the paired
3555    /// declared-slot enumerator and typed-view composition, and the
3556    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3557    /// refusal probe reading a third borrow would silently drift the
3558    /// cross-slot coherence gate's traversal input from the two peers,
3559    /// a three-consumer split at the enumerator, the view composer,
3560    /// and the self-parent gate far from the source `caixa.lisp` with
3561    /// no field naming the child-set-drift root cause. Lifting the
3562    /// resolution rule to a typed method on the substrate primitive
3563    /// means every downstream consumer of the caixa's per-`Caixa`
3564    /// OTP-supervisor outer-slice surface reaches for exactly one
3565    /// typed dispatch — the resolver's accept-set migrates as a unit
3566    /// on any future axis addition.
3567    ///
3568    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3569    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3570    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3571    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3572    /// at the outer altitude of the closed inner-`SupervisorSpec`
3573    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3574    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3575    /// borrow-shared" outer-accessor discipline extended onto the
3576    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3577    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3578    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3579    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3580    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3581    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3582    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3583    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3584    /// M2 typed-composite-element axis
3585    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3586    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3587    /// different altitude).
3588    ///
3589    /// Returns `&[crate::supervisor::ChildSpec]` (not
3590    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3591    /// child list treats it as a read-only sequence — the slice-view
3592    /// is the narrowest borrow that supports every present +
3593    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3594    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3595    /// input, `serde` slice-serialization) without leaking the backing
3596    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3597    /// reaches for (the storage-side `Vec` remains reachable through
3598    /// the `pub children` field for the mutation-carrying serde round-
3599    /// trip and per-test fixture-mutation paths, including the
3600    /// [`Self::supervisor_view`] fold-in path that clones the slot
3601    /// into the typed view). Named `children()` to match the storage
3602    /// field's name verbatim and the tatara-lisp author-surface term
3603    /// (`:children`) the field's own docstring already carries; the
3604    /// accessor's identity maps onto the canonical OTP supervision
3605    /// vocabulary the [`Caixa::children`] field's docstring already
3606    /// reaches for ("Static children of a supervisor").
3607    #[must_use]
3608    pub const fn children(&self) -> &[crate::supervisor::ChildSpec] {
3609        self.children.as_slice()
3610    }
3611
3612    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3613    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3614    /// accessor every consumer of the top-level manifest's per-Aplicacao
3615    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3616    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3617    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3618    /// same backing buffer the raw `self.membros.as_slice()` field access
3619    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3620    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3621    /// and every partially-authored Aplicacao carries before the
3622    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3623    /// `&[Membro]` degenerates to an empty slice on those arms without any
3624    /// silent `None` collapse).
3625    ///
3626    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3627    /// per-Aplicacao member list — the load-bearing container of every
3628    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3629    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3630    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3631    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3632    /// the `:entrada :para` external-gateway destination validates
3633    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3634    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3635    /// threads through a lifted per-entry accessor on the
3636    /// [`crate::aplicacao::Membro`] type: the
3637    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3638    /// identity scalar accessor (4a32abf) and the peer
3639    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3640    /// version-requirement scalar accessor (a40b0e3). Every downstream
3641    /// consumer of the mesh-graph path first passes through this outer
3642    /// accessor onto the slice and then dispatches per-member through
3643    /// the inner accessors — the two-level dispatch means every per-
3644    /// `:membros` reader now routes through a typed dispatch on the
3645    /// substrate primitive at both altitudes.
3646    ///
3647    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3648    /// inline at three production sites across two files — the
3649    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3650    /// enumerator's `!self.membros.is_empty()` presence probe
3651    /// (caixa-core/src/manifest.rs, which drives the
3652    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3653    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3654    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3655    /// composer's `self.membros.clone()` per-member fold-in path
3656    /// (caixa-core/src/manifest.rs, which materializes the typed
3657    /// [`crate::aplicacao::AplicacaoSpec`] view every
3658    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3659    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3660    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3661    /// [`crate::aplicacao::validate_no_self_membership`] input
3662    /// (caixa-core/src/layout.rs, which pins the "no member names the
3663    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3664    /// extension of the outer `:membros` axis (a per-cluster
3665    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3666    /// admission time so a cluster-specific member-set can tighten a
3667    /// caixa-declared list without re-authoring the `caixa.lisp`,
3668    /// promotion of the plain `Vec<Membro>` to a richer
3669    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3670    /// members land as a typed axis, per-member priority annotation once
3671    /// multi-strategy fan-out lands) would have had to be threaded
3672    /// through all three open-coded copies in lockstep or one consumer
3673    /// would silently disagree with the peers on which member slice a
3674    /// given Caixa resolves to — the enumerator's presence probe reading
3675    /// the raw slot while the peer view-composer's fold-in path read an
3676    /// operator-resolved slot would silently split the paired
3677    /// declared-slot enumerator and typed-view composition, and the
3678    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3679    /// refusal probe reading a third borrow would silently drift the
3680    /// cross-slot coherence gate's traversal input from the two peers, a
3681    /// three-consumer split at the enumerator, the view composer, and
3682    /// the self-membership gate far from the source `caixa.lisp` with no
3683    /// field naming the member-set-drift root cause. Lifting the
3684    /// resolution rule to a typed method on the substrate primitive
3685    /// means every downstream consumer of the caixa's per-`Caixa`
3686    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3687    /// typed dispatch — the resolver's accept-set migrates as a unit on
3688    /// any future axis addition.
3689    ///
3690    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3691    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3692    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3693    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3694    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3695    /// altitude. Peer at the outer altitude of the closed inner-
3696    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3697    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3698    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3699    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3700    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3701    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3702    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3703    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3704    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3705    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3706    /// pattern onto the sibling M3 typed-composite-element axis
3707    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3708    /// [`crate::AplicacaoSpec::membros`] element type at a different
3709    /// altitude).
3710    ///
3711    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3712    /// because every downstream consumer of the member list treats it
3713    /// as a read-only sequence — the slice-view is the narrowest borrow
3714    /// that supports every present + roadmapped consumer (`.iter()`,
3715    /// `.len()`, `.is_empty()`, the
3716    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3717    /// input, `serde` slice-serialization) without leaking the backing
3718    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3719    /// reaches for (the storage-side `Vec` remains reachable through the
3720    /// `pub membros` field for the mutation-carrying serde round-trip
3721    /// and per-test fixture-mutation paths, including the
3722    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3723    /// the typed view). Named `membros()` to match the storage field's
3724    /// name verbatim and the tatara-lisp author-surface term
3725    /// (`:membros`) the field's own docstring already carries; the
3726    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3727    /// vocabulary the [`Caixa::membros`] field's docstring already
3728    /// reaches for ("Member Servicos that make up this Aplicacao").
3729    #[must_use]
3730    pub const fn membros(&self) -> &[crate::aplicacao::Membro] {
3731        self.membros.as_slice()
3732    }
3733
3734    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3735    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3736    /// inter-Servico contract-list slice accessor every consumer of the
3737    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3738    /// slice-view keys off — returns the author-declared `:contratos`
3739    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3740    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3741    /// backing buffer the raw `self.contratos.as_slice()` field access
3742    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3743    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3744    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3745    /// single member with no inter-Servico edge carries; the returned
3746    /// `&[WitContract]` degenerates to an empty slice on those arms
3747    /// without any silent `None` collapse).
3748    ///
3749    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3750    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3751    /// container of every per-edge `{de, para, wit, endpoint | subject |
3752    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3753    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3754    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3755    /// adjacency-list seed dispatch on at mesh-artifact materialization
3756    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3757    /// `:membros` vertex set resolves against, closed by the
3758    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3759    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3760    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3761    /// per-edge axis threads through a lifted per-entry accessor on the
3762    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3763    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3764    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3765    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3766    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3767    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3768    /// and the WIT-world discriminant. Every downstream consumer of the
3769    /// mesh-graph edge path first passes through this outer accessor
3770    /// onto the slice and then dispatches per-contract through the
3771    /// inner accessors — the two-level dispatch means every
3772    /// per-`:contratos` reader now routes through a typed dispatch on
3773    /// the substrate primitive at both altitudes.
3774    ///
3775    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3776    /// accessed inline at two production sites in
3777    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3778    /// mesh-slot declared-slot enumerator's
3779    /// `!self.contratos.is_empty()` presence probe (which drives the
3780    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3781    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3782    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3783    /// typed-view composer's `self.contratos.clone()` per-contract
3784    /// fold-in path (which materializes the typed
3785    /// [`crate::aplicacao::AplicacaoSpec`] view every
3786    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3787    /// downstream `caixa-mesh` renderer dispatches on). A future
3788    /// extension of the outer `:contratos` axis (a per-cluster
3789    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3790    /// at admission time so a cluster-specific edge-set can tighten a
3791    /// caixa-declared list without re-authoring the `caixa.lisp`,
3792    /// promotion of the plain `Vec<WitContract>` to a richer
3793    /// `{static, dynamic}` partition once runtime-resolved contract
3794    /// edges land, per-edge policy annotation once the M4 per-edge
3795    /// policy overlay axis lands) would have had to be threaded through
3796    /// both open-coded copies in lockstep or one consumer would
3797    /// silently disagree with the peer on which edge slice a given
3798    /// Caixa resolves to — the enumerator's presence probe reading the
3799    /// raw slot while the peer view-composer's fold-in path read an
3800    /// operator-resolved slot would silently split the paired
3801    /// declared-slot enumerator and typed-view composition, a
3802    /// two-consumer split at the enumerator and the view composer far
3803    /// from the source `caixa.lisp` with no field naming the edge-set-
3804    /// drift root cause. Lifting the resolution rule to a typed method
3805    /// on the substrate primitive means every downstream consumer of
3806    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3807    /// reaches for exactly one typed dispatch — the resolver's
3808    /// accept-set migrates as a unit on any future axis addition.
3809    ///
3810    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3811    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3812    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3813    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3814    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3815    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3816    /// mesh-slot arm of the composite-slice sub-family the sibling
3817    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3818    /// Peer at the outer altitude of the closed inner-
3819    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3820    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3821    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3822    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3823    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3824    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3825    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3826    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3827    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3828    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3829    /// pattern onto the sibling M3 typed-composite-element axis
3830    /// ([`crate::aplicacao::WitContract`] composite, matching the
3831    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3832    /// different altitude).
3833    ///
3834    /// Returns `&[crate::aplicacao::WitContract]` (not
3835    /// `&Vec<WitContract>`) because every downstream consumer of the
3836    /// contract list treats it as a read-only sequence — the slice-view
3837    /// is the narrowest borrow that supports every present + roadmapped
3838    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3839    /// discriminant dispatch, `serde` slice-serialization) without
3840    /// leaking the backing `Vec`'s grow/push/reserve surface no
3841    /// consumer of the typed view reaches for (the storage-side `Vec`
3842    /// remains reachable through the `pub contratos` field for the
3843    /// mutation-carrying serde round-trip and per-test fixture-mutation
3844    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3845    /// clones the slot into the typed view). Named `contratos()` to
3846    /// match the storage field's name verbatim and the tatara-lisp
3847    /// author-surface term (`:contratos`) the field's own docstring
3848    /// already carries; the accessor's identity maps onto the canonical
3849    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3850    /// docstring already reaches for ("WIT-typed inter-Servico
3851    /// contracts").
3852    #[must_use]
3853    pub const fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3854        self.contratos.as_slice()
3855    }
3856
3857    /// Compose the Aplicacao-related flat slots into a single typed
3858    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3859    /// downstream renderer consumption. Returns `None` when the
3860    /// caixa isn't a `:kind Aplicacao`.
3861    #[must_use]
3862    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3863        if !self.kind().is_aplicacao() {
3864            return None;
3865        }
3866        Some(crate::aplicacao::AplicacaoSpec {
3867            membros: self.membros().to_vec(),
3868            contratos: self.contratos().to_vec(),
3869            politicas: self.politicas().cloned().unwrap_or_default(),
3870            placement: self.placement().cloned().unwrap_or_default(),
3871            entrada: self.entrada().cloned(),
3872        })
3873    }
3874
3875    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3876    /// *declares* a value on, in canonical declaration order
3877    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3878    /// `:entrada`). A slot counts as declared when its backing field
3879    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3880    ///
3881    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3882    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3883    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3884    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3885    /// caixa-flux / caixa-helm renderers only emit them for an
3886    /// Aplicacao. On any *other* kind a declared mesh slot is the
3887    /// manifest field's documented "ignored otherwise" (see the
3888    /// `:membros` … `:entrada` field docs): it silently passes
3889    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3890    /// rendered — far from the source caixa.lisp.
3891    /// [`crate::StandardLayout::verify`] consults this to reject that
3892    /// silent-drop at caixa-build time
3893    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3894    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3895    /// a slot foreign to the kind is a build error, not a silent drop.
3896    ///
3897    /// Lifted as a typed method (rather than an inline disjunction at
3898    /// the verify call site) so the mesh-slot set lives in one place —
3899    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3900    /// overlay, distributed-app takeover config) is one push here, and
3901    /// every consumer reaching for "which mesh slots are set" (the
3902    /// verify gate, a future `feira lint` kind-coherence advisory)
3903    /// inherits the canonical order without rolling its own.
3904    ///
3905    /// Each per-arm kebab-case label is routed through the peer
3906    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3907    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3908    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3909    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3910    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3911    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3912    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3913    /// kebab-case label + renderer-side artifact key) route through one
3914    /// canonical declaration per arm — same discipline the peer
3915    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3916    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3917    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3918    /// axis, extended here to close the M3 mesh-slot author-facing-label
3919    /// axis so both altitudes of the typed-slot algebra
3920    /// (per-Servico M2 + per-Aplicacao M3) share the same
3921    /// "one canonical byte-string per arm, next to the axis" discipline.
3922    #[must_use]
3923    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3924        let mut slots = Vec::new();
3925        if !self.membros().is_empty() {
3926            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3927        }
3928        if !self.contratos().is_empty() {
3929            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3930        }
3931        if self.politicas().is_some() {
3932            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3933        }
3934        if self.placement().is_some() {
3935            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3936        }
3937        if self.entrada().is_some() {
3938            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3939        }
3940        slots
3941    }
3942
3943    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3944    /// caixa *declares* a value on, in canonical declaration order
3945    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3946    /// `:children`). A slot counts as declared when its backing field
3947    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3948    ///
3949    /// The supervisor-tree slots compose the typed OTP supervisor of a
3950    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3951    /// `:children` field docs above). [`Self::supervisor_view`] only
3952    /// folds them into a validatable [`SupervisorSpec`] when the kind
3953    /// matches (returns `None` otherwise), and the wasm-operator's
3954    /// hierarchical reconciler only consumes them for a Supervisor. On
3955    /// any *other* kind a declared supervisor slot is the manifest
3956    /// field's documented "ignored otherwise" (see the `:estrategia` …
3957    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3958    /// and then vanishes — never validated, never reconciled — far from
3959    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3960    /// this to reject that silent-drop at caixa-build time
3961    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3962    /// exact mirror of the [`Self::declared_mesh_slots`] /
3963    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3964    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3965    /// error, not a silent drop.
3966    #[must_use]
3967    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3968        let mut slots = Vec::new();
3969        if self.estrategia().is_some() {
3970            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3971        }
3972        if self.max_restarts().is_some() {
3973            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3974        }
3975        if self.restart_window().is_some() {
3976            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3977        }
3978        if !self.children().is_empty() {
3979            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3980        }
3981        slots
3982    }
3983
3984    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3985    /// caixa *declares* a value on, in canonical declaration order
3986    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3987    /// declared when its backing field carries a value — a `Some(...)`,
3988    /// or a non-empty `Vec`.
3989    ///
3990    /// The M2 slots configure the runtime of a long-running wasm
3991    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3992    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3993    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3994    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3995    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3996    /// emit these slots for a Servico; on any *other* kind a declared M2
3997    /// slot is the manifest field's documented "ignored otherwise": its
3998    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3999    /// but the value is never rendered into a chart / programs.yaml entry
4000    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
4001    /// vanishes, far from the source caixa.lisp.
4002    /// [`crate::StandardLayout::verify`] consults this to reject that
4003    /// silent-drop at caixa-build time
4004    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
4005    /// mirror of the [`Self::declared_mesh_slots`] /
4006    /// [`Self::declared_supervisor_slots`] gates on the peer
4007    /// kind-exclusive slot sets: a slot foreign to the kind is a build
4008    /// error, not a silent drop.
4009    ///
4010    /// Each per-arm kebab-case label is routed through the peer
4011    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
4012    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
4013    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
4014    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
4015    /// both halves of the M2 top-level slot's dual axis (author-facing
4016    /// kebab-case label + renderer-side camelCase overlay-container wire
4017    /// key) route through one canonical declaration per arm — same
4018    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
4019    /// author-label consts (889dc18) establish on the sibling
4020    /// per-callback axis inside the `:behavior` overlay block.
4021    #[must_use]
4022    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
4023        let mut slots = Vec::new();
4024        if self.limits().is_some() {
4025            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
4026        }
4027        if self.behavior().is_some() {
4028            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
4029        }
4030        if !self.upgrade_from().is_empty() {
4031            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
4032        }
4033        slots
4034    }
4035
4036    /// The kebab-case `:slot` tags of every code-surface slot this caixa
4037    /// declares a value on that its [`CaixaKind`] doesn't natively own,
4038    /// in canonical declaration order (`:exe` → `:servicos`). A
4039    /// code-surface slot is owned by exactly one kind: `:exe` by
4040    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
4041    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
4042    /// `ComputeUnit` daemon surface).
4043    ///
4044    /// Each is silently ignored when declared on the wrong kind: the
4045    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
4046    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
4047    /// code-running kind a declared `:exe` / `:servicos` is the manifest
4048    /// field's documented "ignored otherwise" — its path is checked for
4049    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
4050    /// (which run after [`Caixa::from_lisp`]), but the value is never
4051    /// rendered into a build target or programs.yaml entry. It silently
4052    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
4053    /// caixa.lisp, with no field naming which slot is foreign.
4054    ///
4055    /// [`crate::StandardLayout::verify`] consults this to reject that
4056    /// silent-drop at caixa-build time
4057    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
4058    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
4059    /// gates ([`Self::declared_servico_slots`] /
4060    /// [`Self::declared_supervisor_slots`] /
4061    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
4062    /// axis to be closed on the typed surface. The Supervisor /
4063    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
4064    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
4065    /// diagnostics — they fire ahead of this gate on the same `verify`
4066    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
4067    /// and this method is moot. For Biblioteca / Binario / Servico, this
4068    /// gate fires when a code-running kind declares another code-running
4069    /// kind's exclusive code surface.
4070    ///
4071    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
4072    /// may legitimately ship a `lib/` helper that the underlying
4073    /// substrate (the nix flake for Binario, the wasm component build
4074    /// for Servico) bundles into its build, so the slot's
4075    /// declared-on-wrong-kind cardinality isn't a structural error on
4076    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
4077    /// is the native case (the slot's owning kind). Supervisor /
4078    /// Aplicacao declaring `:bibliotecas` is gated upstream by
4079    /// [`crate::LayoutError::SupervisorOwnsCode`] /
4080    /// [`crate::LayoutError::AplicacaoOwnsCode`].
4081    ///
4082    /// Lifted as a typed method (rather than an inline disjunction at
4083    /// the verify call site) so the foreign-code-slot set lives in one
4084    /// place — a future kind that gains its own code-surface slot is
4085    /// one push here, and every consumer reaching for "which code
4086    /// surfaces are foreign to this kind" (the verify gate, a future
4087    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
4088    /// per-caixa build-target classifier) inherits the canonical order
4089    /// without rolling its own.
4090    #[must_use]
4091    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
4092        let mut slots = Vec::new();
4093        if !self.exe().is_empty() && !self.kind().requires_exe() {
4094            slots.push(":exe");
4095        }
4096        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
4097            slots.push(":servicos");
4098        }
4099        slots
4100    }
4101
4102    /// Validate every entry of `:deps` and `:deps-dev` through
4103    /// [`Dep::validate`] — closing the parity loop with the per-axis
4104    /// `:versao` gates already wired into the typed-graph
4105    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
4106    /// 9888b13) and typed supervisor tree
4107    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
4108    ///
4109    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
4110    /// were the only `:versao` axes still untyped past
4111    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
4112    /// as a String without parsing it, so a malformed-but-non-empty
4113    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4114    /// silently passed parse and the `semver::Error` surfaced at
4115    /// lacre-resolve time, far from the source caixa.lisp, with no
4116    /// field naming which `:deps` entry carried the typo. Lifting the
4117    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4118    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4119    /// every requirement string past `validate_deps` is round-trippable
4120    /// through [`crate::parse_requirement`] without re-checking at the
4121    /// resolver layer.
4122    ///
4123    /// Both lists run through the same per-entry validator so a typo
4124    /// in `:deps-dev` surfaces with the same diagnostic as one in
4125    /// `:deps` — neither axis is a second-class citizen of the typed
4126    /// surface.
4127    ///
4128    /// Within each list, [`DepError::DuplicateNome`] closes the
4129    /// set-not-multiset discipline on the `:nome` axis: two entries
4130    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4131    /// triples that the caixa-resolver's lacre pipeline collapses to one
4132    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4133    /// silently overwrites the first at `concrete_versao`-resolve time
4134    /// (the same "second wins / one silently overwrites the other"
4135    /// shape the peer typed-graph duplicate gates already close on every
4136    /// other Vec-shaped authoring surface that keys by name). The
4137    /// duplicate check fires per-list and runs *after* each per-entry
4138    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4139    /// surfaces its narrower per-entry diagnostic
4140    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4141    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4142    /// diagnostic — the canonical "per-entry shape before cross-entry
4143    /// uniqueness" precedence the peer `:children :caixa`
4144    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4145    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4146    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4147    /// ([`crate::AplicacaoSpec::validate_placement`]),
4148    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4149    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4150    /// and the within-`:upgrade-from`-entry per-instruction-class
4151    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4152    /// [`crate::UpgradeError::DuplicateStateChange`],
4153    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4154    ///
4155    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4156    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4157    /// same name in both tables (the dev table's pin overrides the
4158    /// runtime table's pin in test/dev contexts), and caixa's surface
4159    /// mirrors that convention until a deliberate choice retires the
4160    /// override pattern. Only within-list duplicates are structurally
4161    /// incoherent — those are what this gate closes.
4162    ///
4163    /// Compound per-`Caixa` entry gate on the dep-graph axis: folds the
4164    /// two standalone dep-list validators — the per-entry + within-list
4165    /// duplicate-`:nome` walk (the [`Dep::validate`] +
4166    /// [`crate::render::insert_first_seen`] cascade this method opened
4167    /// on) and the cross-slot self-edge gate
4168    /// ([`crate::dep::validate_no_self_dep`]) — onto one substrate
4169    /// primitive on [`Caixa`]. The two arms run in the same canonical
4170    /// order the layout pipeline
4171    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4172    /// author-time gate) has always sequenced them (per-entry +
4173    /// cross-entry duplicate → cross-slot self-edge), so the fold is
4174    /// byte-for-byte equivalent to the pre-fold two-block cascade at
4175    /// that call site (pinned by the paired
4176    /// `validate_deps_folds_per_entry_arm_matches_gate` /
4177    /// `validate_deps_folds_self_edge_arm_matches_gate` equivalence
4178    /// pins and the `validate_deps_per_entry_arm_fires_before_self_edge_arm`
4179    /// ordering pin). Self-contained on `&self` — resolves its three
4180    /// inputs ([`Self::deps`], [`Self::deps_dev`], [`Self::nome`])
4181    /// through the substrate primitives' own accessor family, the same
4182    /// posture every peer per-slot compound gate
4183    /// ([`crate::AplicacaoSpec::validate_contratos`],
4184    /// [`crate::MeshPolicy::validate`],
4185    /// [`crate::SupervisorSpec::validate_children`],
4186    /// [`Self::validate_upgrade_from`]) already carries.
4187    ///
4188    /// Prior to this lift [`crate::dep::validate_no_self_dep`] lived
4189    /// only open-coded at the layout wire-up site
4190    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs)
4191    /// as a standalone two-arg dispatch immediately after this method's
4192    /// per-entry + cross-entry walk, both wrapped through the same
4193    /// [`crate::LayoutError::DepsViolation`] envelope: every future
4194    /// consumer that wanted to gate the dep-graph as a whole — the
4195    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4196    /// per-CR admission webhook re-checking `:deps` / `:deps-dev` after
4197    /// a per-entry patch, a future `feira validate --deps` per-caixa
4198    /// admission verb, a per-`:deps` overlay resolver a per-cluster
4199    /// overlay lift would materialize (each the deferred consumer this
4200    /// method's peer [`Self::deps`] / [`Self::deps_dev`] accessors'
4201    /// docstrings already name) — was structurally forced to either
4202    /// re-inline the two-dispatch cascade in lockstep with the layout
4203    /// wire-up (the duplication the PRIME DIRECTIVE names as a bug) or
4204    /// call the whole [`crate::layout::StandardLayout::verify`] pipeline
4205    /// and pay every peer per-Caixa gate to re-check one slot. Post-fold
4206    /// each such consumer reaches the two-arm compound gate through one
4207    /// call on the substrate primitive.
4208    pub fn validate_deps(&self) -> Result<(), DepError> {
4209        for &list in crate::dep::DepList::ALL {
4210            let mut seen = std::collections::HashSet::new();
4211            for dep in self.deps_of(list) {
4212                dep.validate()?;
4213                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4214                    DepError::duplicate_nome(dep.nome(), list.as_str())
4215                })?;
4216            }
4217        }
4218        crate::dep::validate_no_self_dep(self.deps(), self.deps_dev(), self.nome())?;
4219        Ok(())
4220    }
4221
4222    /// Run a per-slot typed validator on `self` and, on the per-arm
4223    /// parser-side error arm, thread the error into a paired
4224    /// [`crate::LayoutError`] wrap under `self.nome()`. Substrate
4225    /// primitive folding the 18 self-similar layout-pipeline wire-up
4226    /// sites at [`crate::layout::StandardLayout::verify`] that carry
4227    /// the identical
4228    /// `caixa.validate_<slot>().map_err(|err| crate::LayoutError::<slot>_violation(caixa, err))?;`
4229    /// cascade onto one dispatch. Each of the eighteen sites (`:nome`,
4230    /// `:nome`-chart-name-budget, `:versao`, `:deps`, `:etiquetas`,
4231    /// `:autores`, `:repositorio`, `:descricao`, `:licenca`, `:edicao`,
4232    /// `:bibliotecas`/`:exe`/`:servicos` code-path shape, `:limits`,
4233    /// `:behavior`, `:upgrade-from`, `:restart-window`, per-Supervisor
4234    /// shape, per-Aplicacao shape, per-Acao shape) carried the same
4235    /// four-line "run a per-slot typed validator on `caixa` and, on the
4236    /// per-arm parser-side error arm, thread it into the paired
4237    /// [`crate::LayoutError`] one-slot envelope through the substrate-
4238    /// canonical `layout_violation_ctors!` family (131ca0d)" cascade,
4239    /// differing only in the two names bound at each site — the
4240    /// validator (`Caixa::validate_deps` / `validate_nome` / ...) and
4241    /// the paired ctor (`LayoutError::deps_violation` / ...). Eighteen
4242    /// consumers, one identical shape, one substrate primitive on
4243    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
4244    /// a bug — on the second half of the per-slot cascade the peer
4245    /// substrate primitives on the [`crate::LayoutError`]-wrap side
4246    /// (the `layout_violation_ctors!` macro 131ca0d, the
4247    /// `layout_slot_kind_ctors!` macro 0419438, the `layout_nome_only_ctors!`
4248    /// macro 3fe3dd7, the [`crate::LayoutError::missing_entry`] ctor
4249    /// 1b09f9d, the [`crate::layout::StandardLayout::probe_declared_entry`]
4250    /// primitive fda1e35) each closed on their sibling envelopes; the
4251    /// first half of the cascade (the per-slot compound gates
4252    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
4253    /// baa4688, [`Self::validate_behavior`] 0d2877a,
4254    /// [`Self::validate_upgrade_from`] d6801df,
4255    /// [`Self::validate_aplicacao_shape`] 949a7a0,
4256    /// [`Self::validate_supervisor_shape`] 4c70105,
4257    /// [`Self::validate_acao_shape`] 5d6df54,
4258    /// [`Self::validate_kind_slot_coherence`] f0d286e,
4259    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
4260    /// [`Self::validate_ci_kind_coherence`] 9b55beb,
4261    /// [`Self::validate_required_kind_slot`] 9c385d8) each closed on
4262    /// their per-slot compound gates.
4263    ///
4264    /// Composes the [`crate::layout::LayoutError`] wrap and the per-slot
4265    /// typed validator through two typed callables: `gate` runs on
4266    /// `self` and yields a per-slot error `E`; on the `Err(E)` arm
4267    /// `wrap` re-wraps that error under `self` into a
4268    /// [`crate::layout::LayoutError`]. The `Ok(())` arm passes through
4269    /// verbatim as the fold's identity element — byte-equal to the
4270    /// pre-lift `Result::map_err` short-circuit at the `?;` marker
4271    /// every wire-up site formerly carried. Every future consumer that
4272    /// wants to run one of the per-slot gates and thread its error
4273    /// through the layout wrap (the deferred
4274    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4275    /// webhook per-slot re-check, a future `feira validate --<slot>`
4276    /// per-caixa admission verb, an overlay resolver re-running one
4277    /// gate after a per-slot patch) reaches the two-callable dispatch
4278    /// through one call rather than re-inlining the four-line cascade
4279    /// in lockstep with the pre-existing 18 wire-ups. The two callables
4280    /// reach the primitive as first-class type-checked references
4281    /// rather than the pre-lift `.map_err(|err| CTOR(caixa, err))`
4282    /// closure body — so a mismatch between the validator's `E` type
4283    /// and the ctor's `E` bound trips at the wire-up site (compile-
4284    /// time) rather than at the closure body (also compile-time, but
4285    /// with a diagnostic pointing at the closure expression rather
4286    /// than the two named callables).
4287    pub fn run_layout_gate<E, W>(
4288        &self,
4289        gate: impl FnOnce(&Caixa) -> Result<(), E>,
4290        wrap: W,
4291    ) -> Result<(), crate::LayoutError>
4292    where
4293        W: FnOnce(&Caixa, E) -> crate::LayoutError,
4294    {
4295        gate(self).map_err(|err| wrap(self, err))
4296    }
4297
4298    /// Run one arm of the cross-family kind ↔ owned-slot-family
4299    /// coherence cascade on `self`: on a caixa whose [`Self::kind`] does
4300    /// not own the typed-slot family named by `is_owner`, refuse when
4301    /// the paired `accumulator` reports any declared slot in that
4302    /// family; otherwise pass. Substrate primitive folding the three
4303    /// self-similar four-line
4304    /// `if !self.kind().is_<owner>() { let slots = self.declared_<family>_slots();
4305    /// if !slots.is_empty() { return Err(<wrap>(self, slots)); } }`
4306    /// arms at [`Self::validate_kind_slot_coherence`] onto one dispatch.
4307    /// Three consumers (M3 mesh — Aplicacao-owner, supervisor-tree —
4308    /// Supervisor-owner, M2 Servico-runtime — Servico-owner), one
4309    /// identical shape, one substrate primitive on [`Caixa`] closing
4310    /// the duplication the PRIME DIRECTIVE names as a bug on the
4311    /// outer kind-coherence arm shape — peer with the substrate
4312    /// primitives on the two adjacent halves of the same three-arm
4313    /// cascade the sibling [`Self::declared_mesh_slots`] /
4314    /// [`Self::declared_supervisor_slots`] /
4315    /// [`Self::declared_servico_slots`] accumulator family closes on
4316    /// the inner slot-set enumerator axis and the sibling
4317    /// [`crate::layout::layout_slot_kind_ctors!`] macro (0419438)
4318    /// closes on the inner wrap-envelope ctor axis. Each of the three
4319    /// [`Self::validate_kind_slot_coherence`] arms now reads through
4320    /// one call across every altitude of the per-arm cascade:
4321    /// one dispatch on this primitive for the outer guard shape, one
4322    /// dispatch on `Self::declared_<family>_slots` for the accumulator,
4323    /// one dispatch on `crate::LayoutError::<family>_on_non_<owner>`
4324    /// for the wrap ctor.
4325    ///
4326    /// Composes the outer owner-kind guard, the per-family accumulator,
4327    /// and the per-family wrap ctor through three typed callables:
4328    /// `is_owner` runs on `&self.kind()` (a `&CaixaKind` borrow so the
4329    /// `gen_platform::IsVariant`-derived `fn(&CaixaKind) -> bool`
4330    /// per-arm predicates — [`crate::CaixaKind::is_aplicacao`] /
4331    /// [`crate::CaixaKind::is_supervisor`] / [`crate::CaixaKind::is_servico`]
4332    /// — pass verbatim as function references), `accumulator` runs on
4333    /// `&self` and yields the
4334    /// per-family declared-slot list, and `wrap` runs on `(&self,
4335    /// Vec<&'static str>)` and yields the per-family
4336    /// [`crate::LayoutError`] wrap. The `is_owner` short-circuit fires
4337    /// before the accumulator dispatch (so the owner kind of each
4338    /// family passes without invoking `accumulator`, byte-equal to the
4339    /// pre-lift `if !self.kind().is_<owner>() { … }` outer guard's
4340    /// short-circuit — pinned by
4341    /// `run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator`),
4342    /// and the accumulator's `is_empty` short-circuit fires before the
4343    /// wrap dispatch (so a non-owner kind with no declared slot in that
4344    /// family passes without invoking `wrap`, byte-equal to the pre-lift
4345    /// `if !<slots>.is_empty() { … }` inner guard's short-circuit —
4346    /// pinned by
4347    /// `run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap`).
4348    /// The wrap ctor is `FnOnce(&Caixa, Vec<&'static str>) ->
4349    /// crate::LayoutError` — matching the [`crate::layout::layout_slot_kind_ctors!`]
4350    /// macro's per-variant `fn(&Caixa, Vec<&'static str>) -> LayoutError`
4351    /// substrate-canonical ctor shape verbatim, so
4352    /// [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
4353    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
4354    /// [`crate::LayoutError::servico_slots_on_non_servico`] pass as
4355    /// function references without a closure wrap. A mismatch between
4356    /// the ctor's signature and this bound trips at the wire-up site
4357    /// (compile-time) rather than at a closure body.
4358    ///
4359    /// The sibling [`crate::LayoutError::ForeignCodeSlot`] gate on the
4360    /// code-surface family sits outside this primitive because
4361    /// [`Self::declared_foreign_code_slots`] bakes the per-arm kind-
4362    /// check into the accumulator itself (each arm's
4363    /// `!self.kind().requires_<slot>()` guard fires inside the
4364    /// accumulator, not around it), so the code-surface arm carries no
4365    /// outer `is_owner`-shaped guard and its dispatch reads through
4366    /// [`Self::validate_foreign_code_kind_coherence`] verbatim without
4367    /// this primitive — the same posture the `_no_code_` /
4368    /// `_ci_kind_` coherence axes take on their respective per-arm
4369    /// shapes. The primitive here is specific to the "outer
4370    /// non-owner-kind guard + inner accumulator + inner emptiness
4371    /// guard + wrap" arm shape that fires three times in
4372    /// [`Self::validate_kind_slot_coherence`].
4373    ///
4374    /// Every future consumer that wants to gate one kind-owned slot
4375    /// family as a unit outside the composed cascade (the deferred
4376    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4377    /// webhook per-family re-check after a per-slot patch, a future
4378    /// `feira validate --<family>-coherence` per-caixa admission verb,
4379    /// a per-`Caixa` overlay resolver rejecting a kind-foreign patch
4380    /// on one family) reaches the four-line arm through one call
4381    /// rather than re-inlining the outer-guard + accumulator +
4382    /// emptiness-guard + wrap cascade in lockstep with the pre-existing
4383    /// three arms. Every future kind-owned typed-slot family (an
4384    /// `Actor`-owned per-virtual-actor grain slot the M5 Orleans-
4385    /// inspired kind reaches through, a per-Aplicacao overlay slot the
4386    /// M4 CR materializer consults) folds onto
4387    /// [`Self::validate_kind_slot_coherence`] as one additional
4388    /// dispatch on this primitive rather than a fourth open-coded
4389    /// four-line block.
4390    pub fn run_kind_owned_slot_family_gate<F, A, W>(
4391        &self,
4392        is_owner: F,
4393        accumulator: A,
4394        wrap: W,
4395    ) -> Result<(), crate::LayoutError>
4396    where
4397        F: FnOnce(&crate::CaixaKind) -> bool,
4398        A: FnOnce(&Caixa) -> Vec<&'static str>,
4399        W: FnOnce(&Caixa, Vec<&'static str>) -> crate::LayoutError,
4400    {
4401        if is_owner(&self.kind()) {
4402            return Ok(());
4403        }
4404        let slots = accumulator(self);
4405        if slots.is_empty() {
4406            return Ok(());
4407        }
4408        Err(wrap(self, slots))
4409    }
4410
4411    /// Reject `:nome` values the K8s apiserver would refuse at admission
4412    /// time. The top-level Caixa identity flows directly into every
4413    /// substrate-side artifact's `metadata.name` axis: the
4414    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4415    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4416    /// aggregator keys ComputeUnit derivation off
4417    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4418    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4419    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4420    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4421    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4422    /// ([`caixa-mesh::lib::cilium_network_policies`],
4423    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4424    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4425    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4426    /// schema enforces the DNS-1123 label rule on admission; a
4427    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4428    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4429    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4430    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4431    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4432    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4433    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4434    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4435    /// Invalid value` rejection on whichever derived artifact admitted
4436    /// first, far from the source `caixa.lisp` and without any field
4437    /// naming the offending `:nome`.
4438    ///
4439    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4440    /// substrate-side predicate the per-axis name gates already share:
4441    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4442    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4443    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4444    /// diagnostic is self-locating (the offending `:nome` is named
4445    /// verbatim) and the author can grep their `caixa.lisp` for
4446    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4447    /// every per-axis sibling gate already exposes
4448    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4449    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4450    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4451    ///
4452    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4453    /// derive macro stores the raw String) is gated by the narrower
4454    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4455    /// consulted, mirroring the empty-first cascade every per-axis
4456    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4457    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4458    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4459        // Routes through the shared
4460        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4461        // name axes each land on so drift between the eight axes'
4462        // accepted DNS-1123-label sets is structurally impossible.
4463        let nome = self.nome();
4464        crate::render::require_valid_dns_1123_label(
4465            nome,
4466            || ManifestError::NomeEmpty,
4467            |reason| ManifestError::nome_invalid(nome, reason),
4468        )
4469    }
4470
4471    /// Reject `:nome` values whose joint length with the canonical
4472    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4473    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4474    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4475    /// substrate carries materializes the caixa's `:nome` through the
4476    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4477    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4478    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4479    /// `ChartDir.name` + `Chart.yaml::name`
4480    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4481    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4482    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4483    /// `oci://<registry>/lareira-<nome>` chart ref
4484    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4485    /// admission rule strict-parses against DNS-1123-label, the Helm
4486    /// operator's tracking-secret name is derived from `release_name`
4487    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4488    /// K8s object `metadata.name` axes embed the chart name as a
4489    /// prefix — every one fails admission on a > 63-byte chart name.
4490    ///
4491    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4492    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4493    /// `:nome` of 56–63 bytes silently passed validate (the inner
4494    /// DNS-1123 check accepts the bare `:nome`) but produced a
4495    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4496    /// rejected at admission — far from the source `caixa.lisp`, with
4497    /// no field naming the overflow root cause. The
4498    /// [`lareira_chart_name`] helper's own doc comment
4499    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4500    /// "the M4 admission webhook will pin the joint-length invariant
4501    /// when it lands". This gate lands the invariant at the
4502    /// manifest-validate layer rather than waiting for the apiserver
4503    /// — the same fail-at-the-source posture every peer per-axis
4504    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4505    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4506    /// `:edicao`, etc.) takes.
4507    ///
4508    /// Thin wrapper around
4509    /// [`crate::render::is_lareira_chart_name_shape`] (the
4510    /// substrate-side predicate that composes [`lareira_chart_name`] +
4511    /// [`is_dns_1123_label`] via the lifted
4512    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4513    /// shared parser-shaped reason into the
4514    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4515    /// diagnostic is self-locating (the offending `:nome` is named
4516    /// verbatim alongside the rendered chart name and the budget) and
4517    /// the author can shorten in one edit. The gate runs across every
4518    /// `:kind` — `:nome` is the substrate-wide identity axis any
4519    /// future renderer the substrate adds can derive a
4520    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4521    /// the drift footgun where a future kind grows a chart-emitting
4522    /// render path while the validate cascade doesn't catch it.
4523    ///
4524    /// Runs *after* [`Self::validate_nome`] so the narrower
4525    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4526    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4527    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4528    /// specific shape error rather than the chart-name-budget error,
4529    /// preserving the legitimate "well-shaped `:nome` that happens to
4530    /// overflow the joint cap" arm for this gate.
4531    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4532        let nome = self.nome();
4533        crate::render::is_lareira_chart_name_shape(nome)
4534            .map_err(|reason| ManifestError::nome_chart_name_budget_exceeded(nome, reason))
4535    }
4536
4537    /// Reject `:versao` values that don't parse as [`semver::Version`].
4538    /// The top-level Caixa version flows directly into every
4539    /// substrate-side artifact that carries a "this is which version of
4540    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4541    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4542    /// SemVer-2-strict at `helm template` / `helm install` time per
4543    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4544    /// `feira publish` Zig-style `v<versao>` git tag
4545    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4546    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4547    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4548    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4549    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4550    /// `skopeo push`, the lacre closure's pinned versions
4551    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4552    /// `:upgrade-from :from` references peers in this exact `versao`
4553    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4554    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4555    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4556    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4557    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4558    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4559    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4560    /// into the version field a peer `:deps :versao` accepts;
4561    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4562    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4563    /// derive macro stores the raw String) and the failure surfaced at
4564    /// the *first* downstream consumer that strict-parses it: at
4565    /// `helm install` time as a chart-version rejection, at
4566    /// `feira publish` time as a malformed git tag, at lacre-resolve
4567    /// time as a `semver::Error` not naming the offending caixa, at
4568    /// `feira upgrade --to <versao>` time as an unresolvable
4569    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4570    /// and without any field naming the offending `:versao`.
4571    ///
4572    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4573    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4574    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4575    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4576    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4577    /// variant, carrying the offending `:versao` verbatim + a
4578    /// parser-shaped reason naming the specific violation, so the
4579    /// diagnostic is self-locating (the author can grep their
4580    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4581    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4582    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4583    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4584    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4585    /// now structurally equivalent (every value past validate is
4586    /// round-trippable through [`semver::Version::parse`] without
4587    /// re-checking at the renderer, resolver, or operator hot-upgrade
4588    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4589    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4590    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4591    ///
4592    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4593    /// the derive macro stores the raw String) is gated by the
4594    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4595    /// consulted, mirroring the empty-first cascade every per-axis
4596    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4597    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4598    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4599    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4600        let versao = self.versao();
4601        if versao.is_empty() {
4602            return Err(ManifestError::VersaoEmpty);
4603        }
4604        semver::Version::parse(versao)
4605            .map_err(|e| ManifestError::versao_invalid(versao, e.to_string()))?;
4606        Ok(())
4607    }
4608
4609    /// Compound per-`Caixa` entry gate on the M2 `:upgrade-from` slot:
4610    /// folds the three [`crate::upgrade`] top-level validators — the
4611    /// per-entry shape + cross-entry duplicate-`:from` gate
4612    /// ([`crate::upgrade::validate_upgrade_from`]), the cross-slot
4613    /// `:from < :versao` SemVer-2 precedence gate
4614    /// ([`crate::upgrade::validate_upgrade_from_against_versao`]), and the
4615    /// cross-slot `:state-change` ↔ `:on-state-change` composition gate
4616    /// ([`crate::upgrade::validate_upgrade_from_against_behavior`]) — onto
4617    /// one substrate primitive on [`Caixa`]. The three dispatches run in
4618    /// the same order the layout pipeline
4619    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4620    /// author-time gate) has always sequenced them, so the fold is
4621    /// byte-for-byte equivalent to the pre-fold three-block cascade at
4622    /// that call site (pinned by the per-arm
4623    /// `validate_upgrade_from_folds_per_entry_arm_matches_gate` /
4624    /// `_folds_versao_arm_matches_gate` / `_folds_behavior_arm_matches_gate`
4625    /// equivalence pins and by the cross-arm
4626    /// `validate_upgrade_from_per_entry_arm_fires_before_versao_arm` /
4627    /// `_versao_arm_fires_before_behavior_arm` ordering pins).
4628    ///
4629    /// Prior to this lift the three [`crate::upgrade`] top-level validators
4630    /// lived only open-coded at the layout wire-up site
4631    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4632    /// each threaded through the same `self.upgrade_from()` slice and each
4633    /// paired with the same [`crate::LayoutError::UpgradeViolation`]-wrap
4634    /// envelope: every future consumer that wanted to gate `:upgrade-from`
4635    /// as a whole — the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
4636    /// materializer's per-CR admission webhook re-checking `:upgrade-from`
4637    /// after a per-`(:from … :instructions …)` patch, a future `feira
4638    /// validate --upgrade` per-caixa admission verb, a per-`:upgrade-from`
4639    /// overlay resolver a per-cluster overlay lift would materialize —
4640    /// was structurally forced to either re-inline the three-dispatch
4641    /// cascade in lockstep with the layout wire-up (the duplication the
4642    /// PRIME DIRECTIVE names as a bug) or call the whole
4643    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4644    /// peer per-Caixa gate to re-check one slot. Post-fold each such
4645    /// consumer reaches the three-arm compound gate through one call on
4646    /// the substrate primitive.
4647    ///
4648    /// The three arms together name one contract with three axes:
4649    ///
4650    ///   - **per-entry + cross-entry graph-edge invariant** — every entry's
4651    ///     `:from` parses as SemVer-2 and every per-instruction / within-
4652    ///     entry ordering / singularity gate on each entry's
4653    ///     `:instructions` list passes, and no two entries share the same
4654    ///     parsed `:from` (the wasm-operator's OTP appup
4655    ///     `release_handler:install_release/1` analog picks at most one
4656    ///     matching block per running version — two entries with the same
4657    ///     parsed semver are an ambiguous edge in the typed upgrade graph).
4658    ///   - **cross-slot reachability invariant** — every entry's `:from`
4659    ///     is strictly less than the caixa's own `:versao` under SemVer-2
4660    ///     precedence. An entry whose `:from >= :versao` is structurally
4661    ///     unreachable by the operator's `:from`-match dispatch (the
4662    ///     operator loads the current `:versao` and matches the *running*
4663    ///     version against each entry's `:from`; an entry whose `:from >=
4664    ///     :versao` is never reached because the operator never runs a
4665    ///     version >= the current one that it could then upgrade *to* the
4666    ///     current one).
4667    ///   - **cross-slot composition invariant** — every entry carrying a
4668    ///     `(:state-change …)` instruction has a `:behavior
4669    ///     :on-state-change` callback declared on the same caixa. The
4670    ///     per-version migration script is the `gen_server:code_change/3`
4671    ///     analog and the runtime hook it is delivered through during hot
4672    ///     upgrade is the `:on-state-change` callback (the upgrade.rs
4673    ///     module doc pins the composition verbatim: "Composes with the
4674    ///     `:behavior :on-state-change` callback to deliver state migration
4675    ///     during hot upgrades").
4676    ///
4677    /// All three axes must hold together — every consumer's
4678    /// `:upgrade-from` accept-set past this compound gate is the same
4679    /// set the `feira build` author-time gate admits.
4680    ///
4681    /// The per-slot compound entry gate discipline lifted here onto the
4682    /// M2 `:upgrade-from` axis is the sibling of the peer per-kind
4683    /// compound entry gates ([`crate::render::require_supervisor_view`]
4684    /// / [`crate::render::require_aplicacao_view`] /
4685    /// [`crate::render::require_v0_servico_shape`]) that fold every
4686    /// per-kind cascade at the per-kind altitude, and of the peer
4687    /// per-slot compound gates ([`crate::AplicacaoSpec::validate_contratos`],
4688    /// [`crate::MeshPolicy::validate`],
4689    /// [`crate::SupervisorSpec::validate_children`]) that fold every
4690    /// structural axis on their slot onto one substrate primitive.
4691    /// Extended here to the last unlifted compound-cascade wire-up at
4692    /// the layout-pipeline altitude — the three-dispatch M2
4693    /// `:upgrade-from` cascade that lived only open-coded at the layout
4694    /// wire-up site.
4695    ///
4696    /// The per-instruction script-path on-disk existence-probe walk that
4697    /// [`crate::layout::StandardLayout::verify`] runs immediately after
4698    /// this gate (which resolves each entry's `:instructions
4699    /// (:state-change :script)` against the layout root) stays open-coded
4700    /// at the layout wire-up site — that arm needs the filesystem oracle
4701    /// on the [`crate::LayoutInvariants`] trait, not the pure per-Caixa
4702    /// typed-shape surface this compound gate folds. Same posture the
4703    /// peer [`Self::validate_code_paths`] takes on the sibling code-path
4704    /// axes: the typed-shape gate fires on the per-Caixa surface, the
4705    /// on-disk existence check fires on the [`crate::StandardLayout`]
4706    /// surface.
4707    ///
4708    /// # Errors
4709    ///
4710    /// Returns [`crate::UpgradeError::FromInvalid`] /
4711    /// [`crate::UpgradeError::ModuleEmpty`] /
4712    /// [`crate::UpgradeError::ModuleInvalid`] /
4713    /// [`crate::UpgradeError::EmptyScript`] /
4714    /// [`crate::UpgradeError::AbsoluteScript`] /
4715    /// [`crate::UpgradeError::ParentEscapeScript`] /
4716    /// [`crate::UpgradeError::NonLispExtensionScript`] /
4717    /// [`crate::UpgradeError::RestartNotExclusive`] /
4718    /// [`crate::UpgradeError::StateChangeWithoutPriorLoad`] /
4719    /// [`crate::UpgradeError::PurgeWithoutPriorLoad`] /
4720    /// [`crate::UpgradeError::StateChangeAfterCleanup`] /
4721    /// [`crate::UpgradeError::DuplicateLoadModule`] /
4722    /// [`crate::UpgradeError::DuplicateStateChange`] /
4723    /// [`crate::UpgradeError::DuplicateCleanup`] /
4724    /// [`crate::UpgradeError::DuplicateFrom`] on the per-entry +
4725    /// cross-entry axis; [`crate::UpgradeError::FromNotBeforeVersao`] on
4726    /// the cross-slot `:from ↔ :versao` axis;
4727    /// [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
4728    /// on the cross-slot `:state-change ↔ :on-state-change` axis.
4729    pub fn validate_upgrade_from(&self) -> Result<(), crate::UpgradeError> {
4730        crate::upgrade::validate_upgrade_from(self.upgrade_from())?;
4731        crate::upgrade::validate_upgrade_from_against_versao(self.upgrade_from(), self.versao())?;
4732        crate::upgrade::validate_upgrade_from_against_behavior(
4733            self.upgrade_from(),
4734            self.behavior(),
4735        )?;
4736        Ok(())
4737    }
4738
4739    /// Compound per-`Caixa` entry gate on the M2 `:limits` slot — folds
4740    /// the [`crate::LimitsSpec::validate`] four-axis cascade (`:memory`
4741    /// wasm32 zero-floor / below-page / above-cap / non-page-multiple;
4742    /// `:fuel` zero-floor / cap; `:wall-clock` zero-floor / cap; `:cpu`
4743    /// zero-floor / cap) onto one substrate primitive on [`Caixa`]. The
4744    /// `#[serde(default)]` absent-slot arm (`limits: None`, the
4745    /// canonical "no bound declared — engine-default applies" author
4746    /// shape [`crate::LimitsSpec::is_empty`]'s per-axis `None` cascade
4747    /// reads) is the fold's identity element and passes trivially; the
4748    /// present-slot arm (`limits: Some(l)`) dispatches to
4749    /// [`crate::LimitsSpec::validate`] verbatim, threading its per-axis
4750    /// [`crate::LimitsError`] Display through untouched.
4751    ///
4752    /// Prior to this lift the M2 `:limits` slot lived only wired
4753    /// open-coded at the layout wire-up site
4754    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4755    /// through the `if let Some(l) = caixa.limits() { l.validate() … }`
4756    /// three-line `Option::None → Ok(()) | Some(_) → …` unwrap-and-
4757    /// dispatch pattern paired with the same
4758    /// [`crate::LayoutError::LimitsViolation`]-wrap envelope: every
4759    /// future consumer that wanted to gate `:limits` as a whole — the
4760    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4761    /// per-CR admission webhook re-checking `:limits` after a per-
4762    /// `{:memory, :fuel, :wall-clock, :cpu}` patch (the exact case the
4763    /// [`Self::limits`] accessor docstring names as the second
4764    /// consumer of the slot), a future `feira validate --limits` per-
4765    /// caixa admission verb, a per-`:limits` overlay resolver a per-
4766    /// cluster `:limits-overrides` overlay lift would materialize — was
4767    /// structurally forced to either re-inline the two-line
4768    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4769    /// pattern in lockstep with the layout wire-up (the duplication the
4770    /// PRIME DIRECTIVE names as a bug) or call the whole
4771    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4772    /// peer per-Caixa gate ([`Self::validate_nome`],
4773    /// [`Self::validate_versao`], [`Self::validate_deps`],
4774    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4775    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4776    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4777    /// [`Self::validate_upgrade_from`], [`Self::validate_code_paths`],
4778    /// plus the per-kind `require_supervisor_view` /
4779    /// `require_aplicacao_view` gates, plus the on-disk existence
4780    /// walks) to re-check one slot. Post-lift each such consumer
4781    /// reaches the [`crate::LimitsSpec::validate`] four-axis cascade
4782    /// (and its identity-element on the absent slot) through one call
4783    /// on the substrate primitive.
4784    ///
4785    /// The per-slot compound entry-gate discipline lifted here onto the
4786    /// M2 `:limits` axis is the sibling of the peer per-slot compound
4787    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4788    /// [`crate::MeshPolicy::validate`],
4789    /// [`crate::SupervisorSpec::validate_children`],
4790    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`]) that
4791    /// fold every structural + cross-slot axis on their slot onto one
4792    /// substrate primitive. Extended here to the M2 `:limits` slot, the
4793    /// first of the two M2 typed slots (`:limits`, `:behavior`) whose
4794    /// per-Caixa compound-gate wire-up still lived open-coded at the
4795    /// layout altitude after the [`Self::validate_upgrade_from`] lift
4796    /// (d6801df) closed the sibling M2 slot's cascade.
4797    ///
4798    /// # Errors
4799    ///
4800    /// Returns every [`crate::LimitsError`] variant on the present-slot
4801    /// arm — verbatim from [`crate::LimitsSpec::validate`]. Passes
4802    /// trivially on the absent-slot arm (`limits: None`, the fold's
4803    /// identity element).
4804    pub fn validate_limits(&self) -> Result<(), crate::LimitsError> {
4805        match self.limits() {
4806            Some(l) => l.validate(),
4807            None => Ok(()),
4808        }
4809    }
4810
4811    /// Compound per-`Caixa` entry gate on the M2 `:behavior` slot's
4812    /// pure typed-shape surface — folds the
4813    /// [`crate::BehaviorSpec::validate`] six-slot value-shape cascade
4814    /// (each declared `:on-init` / `:on-call` / `:on-cast` / `:on-info`
4815    /// / `:on-state-change` / `:on-terminate` callback-path is
4816    /// non-empty / relative / no-`..`-parent-escape / terminating-
4817    /// `.lisp`-extension, routed through the shared
4818    /// [`crate::render::require_sandboxed_lisp_path`] arm-set) onto one
4819    /// substrate primitive on [`Caixa`]. The `#[serde(default)]`
4820    /// absent-slot arm (`behavior: None`, the canonical "no callback
4821    /// declared — the runtime falls back to the wasm-engine's default
4822    /// callback per arm" author shape [`crate::BehaviorSpec::is_empty`]'s
4823    /// per-slot `None` cascade reads) is the fold's identity element
4824    /// and passes trivially; the present-slot arm (`behavior: Some(b)`)
4825    /// dispatches to [`crate::BehaviorSpec::validate`] verbatim,
4826    /// threading its per-slot [`crate::BehaviorError`] Display through
4827    /// untouched.
4828    ///
4829    /// Scope note — the on-disk callback-path existence walk paired
4830    /// with the value-shape gate at
4831    /// [`crate::layout::StandardLayout::verify`] stays open-coded at
4832    /// the layout altitude, because it needs the
4833    /// [`crate::layout::LayoutInvariants`] filesystem oracle
4834    /// ([`crate::layout::LayoutInvariants::exists`]) that the pure
4835    /// per-Caixa typed-shape surface this compound gate folds onto has
4836    /// no reference to. Same posture the peer M2 `:upgrade-from`
4837    /// per-Caixa compound gate ([`Self::validate_upgrade_from`]
4838    /// d6801df) already carries: the pure typed-shape surface folds
4839    /// onto the substrate primitive; the per-instruction script-path
4840    /// existence probe on the paired axis (there `:state-change
4841    /// :script`; here `:on-*`) stays at the layout altitude.
4842    ///
4843    /// Prior to this lift the pure value-shape surface of the M2
4844    /// `:behavior` slot lived only wired open-coded at the layout
4845    /// wire-up site ([`crate::layout::StandardLayout::verify`],
4846    /// caixa-core/src/layout.rs), through the
4847    /// `if let Some(b) = caixa.behavior() { b.validate() … }`
4848    /// unwrap-and-dispatch pattern paired with the same
4849    /// [`crate::LayoutError::BehaviorViolation`]-wrap envelope: every
4850    /// future consumer that wanted to gate the `:behavior` slot's
4851    /// value-shape as a whole — the deferred
4852    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4853    /// admission webhook re-checking `:behavior` after a per-`{:on-init,
4854    /// :on-call, :on-cast, :on-info, :on-state-change, :on-terminate}`
4855    /// patch (the exact case the peer `:on-*` accessor docstrings on
4856    /// [`crate::BehaviorSpec`] already name as deferred consumers of
4857    /// the slot), a future `feira validate --behavior` per-caixa
4858    /// admission verb, a per-`:behavior` overlay resolver a future
4859    /// per-cluster callback-overlay lift would materialize — was
4860    /// structurally forced to either re-inline the two-line
4861    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4862    /// pattern in lockstep with the layout wire-up (the duplication the
4863    /// PRIME DIRECTIVE names as a bug) or call the whole
4864    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4865    /// peer per-Caixa gate ([`Self::validate_nome`],
4866    /// [`Self::validate_versao`], [`Self::validate_deps`],
4867    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4868    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4869    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4870    /// [`Self::validate_limits`], [`Self::validate_upgrade_from`],
4871    /// [`Self::validate_code_paths`], plus the per-kind
4872    /// `require_supervisor_view` / `require_aplicacao_view` gates, plus
4873    /// the on-disk existence walks) to re-check one slot. Post-lift
4874    /// each such consumer reaches the [`crate::BehaviorSpec::validate`]
4875    /// six-slot cascade (and its identity-element on the absent slot)
4876    /// through one call on the substrate primitive.
4877    ///
4878    /// The per-slot compound entry-gate discipline lifted here onto the
4879    /// M2 `:behavior` axis is the sibling of the peer per-slot compound
4880    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4881    /// [`crate::MeshPolicy::validate`],
4882    /// [`crate::SupervisorSpec::validate_children`],
4883    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`],
4884    /// [`Self::validate_limits`]) that fold every structural + cross-
4885    /// slot axis on their slot onto one substrate primitive. Extended
4886    /// here to the M2 `:behavior` slot, the last of the four M2 typed
4887    /// slots (`:limits`, `:behavior`, `:upgrade-from`, plus the
4888    /// supervisor-only `:children` peer) whose per-Caixa compound-gate
4889    /// wire-up still lived open-coded at the layout altitude after the
4890    /// [`Self::validate_limits`] lift (baa4688) closed the sibling M2
4891    /// `:limits` slot's cascade. With this lift the "one named per-slot
4892    /// / per-Caixa compound gate per typed slot folding every structural
4893    /// axis on that slot (plus the `Option::None` identity element for
4894    /// the `Option`-shaped slots) onto one substrate primitive"
4895    /// discipline spans every M2 typed slot uniformly, so a reader who
4896    /// has learned any peer M2 gate reads `:behavior` without a per-
4897    /// slot exception carve-out.
4898    ///
4899    /// # Errors
4900    ///
4901    /// Returns every [`crate::BehaviorError`] variant on the present-
4902    /// slot arm — verbatim from [`crate::BehaviorSpec::validate`].
4903    /// Passes trivially on the absent-slot arm (`behavior: None`, the
4904    /// fold's identity element).
4905    pub fn validate_behavior(&self) -> Result<(), crate::BehaviorError> {
4906        match self.behavior() {
4907            Some(b) => b.validate(),
4908            None => Ok(()),
4909        }
4910    }
4911
4912    /// Reject `:restart-window` values the shared
4913    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4914    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4915    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4916    /// `Option<Duration>` routed through the shared codec via `with =
4917    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4918    /// view-construction path ([`Self::supervisor_view`]) folds the
4919    /// raw string through the same shared codec and soft-swallows the
4920    /// parse error as `None` to keep the view best-effort. Without
4921    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4922    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4923    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4924    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4925    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4926    /// edge case) silently produced a `SupervisorSpec` with
4927    /// `restart_window: None`, indistinguishable from the canonical
4928    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4929    /// `MaxIntensity / Period` invariant turns into a never-reset
4930    /// supervisor far from the source `caixa.lisp`, with no field
4931    /// naming the offending `:restart-window`. Lifting the gate to a
4932    /// Caixa-level validator mirrors the trajectory of the peer
4933    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4934    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4935    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4936    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4937    ///
4938    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4939    /// (the shared codec backing `:supervisor :restart-window` as
4940    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4941    /// `:politicas :circuit-breaker :window` — all three covered by
4942    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4943    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4944    /// variant, carrying the offending raw string + a parser-shaped
4945    /// reason naming the canonical authoring form, so the diagnostic
4946    /// is self-locating (the author can grep their `caixa.lisp` for
4947    /// `:restart-window "<value>"` and fix it in one edit) and
4948    /// uniform with every other manifest-level validate diagnostic.
4949    /// With this gate the four `:restart-window`-shaped surfaces (the
4950    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4951    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4952    /// now structurally equivalent — every value past the codec is in
4953    /// one accepted set, by construction.
4954    ///
4955    /// `None` (the canonical "omit the slot to express no reset"
4956    /// shape) is accepted trivially — the gate is a no-op when the
4957    /// author didn't author a window. The empty string is rejected by
4958    /// the shared codec (its digit-only gate refuses an empty
4959    /// magnitude), surfacing the same `RestartWindowMalformed`
4960    /// diagnostic as every other rejected non-canonical shape.
4961    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4962        let Some(s) = self.restart_window() else {
4963            return Ok(());
4964        };
4965        crate::supervisor::duration_codec::parse(s)
4966            .map(|_| ())
4967            .map_err(|reason| ManifestError::restart_window_malformed(s, reason))
4968    }
4969
4970    /// Compound per-`Caixa` entry gate on the Aplicacao-kind mesh-slot
4971    /// family — folds the paired [`crate::AplicacaoSpec::validate`]
4972    /// typed-shape cascade (per-slot gates on `:membros`, `:contratos`,
4973    /// `:entrada`, `:placement`, `:politicas`, in that declared order)
4974    /// plus the cross-slot self-edge gate
4975    /// ([`crate::aplicacao::validate_no_self_membership`], the
4976    /// `:membros :caixa` ≠ `:nome` invariant the typed view cannot
4977    /// enforce on its own because it carries the membros but not the
4978    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4979    /// non-Aplicacao kinds the fold is the identity element — the paired
4980    /// [`Self::aplicacao_view`] accessor returns `None` off the
4981    /// Aplicacao arm (peer with the [`Self::validate_limits`] /
4982    /// [`Self::validate_behavior`] M2 `Option`-arm identity element),
4983    /// so the gate passes trivially without touching the mesh slots.
4984    ///
4985    /// Prior to this lift the paired cascade lived only wired open-coded
4986    /// at the layout wire-up site
4987    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4988    /// as the three-line `let view = caixa.aplicacao_view().expect(...);
4989    /// view.validate() … validate_no_self_membership(...) …` pattern
4990    /// paired with two `.map_err(|err| LayoutError::AplicacaoViolation
4991    /// { caixa, issue })` wraps — every future consumer that wanted to
4992    /// gate the Aplicacao-shape cascade as a whole (the deferred
4993    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4994    /// admission webhook re-checking `:membros` / `:contratos` after a
4995    /// per-slot patch, a future `feira validate --aplicacao` per-caixa
4996    /// admission verb, a per-Aplicacao overlay resolver) was structurally
4997    /// forced to either re-inline the two-dispatch cascade in lockstep
4998    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
4999    /// names as a bug) or call the whole
5000    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5001    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5002    /// such consumer reaches the two-arm compound gate through one call
5003    /// on the substrate primitive.
5004    ///
5005    /// Peer to the [`crate::render::require_aplicacao_view`] compound
5006    /// entry gate every per-Aplicacao *renderer* routes through
5007    /// (3aefefb folded `validate_no_self_membership` onto the renderer
5008    /// path) — this gate mirrors the same fold on the *layout* path, so
5009    /// the two consumers of the Aplicacao-shape cascade (the author-time
5010    /// gate and every per-Aplicacao renderer) share one substrate
5011    /// primitive rather than two open-coded cascades kept in lockstep.
5012    /// Same lift discipline the peer per-slot compound gates
5013    /// ([`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5014    /// b5dd55e, [`Self::validate_limits`] baa4688,
5015    /// [`Self::validate_behavior`] 0d2877a) each carry.
5016    ///
5017    /// # Errors
5018    ///
5019    /// Returns every [`crate::AplicacaoError`] variant on the present-
5020    /// kind arm — the typed-shape cascade's per-slot arms first
5021    /// (matching [`crate::AplicacaoSpec::validate`]'s declared order),
5022    /// then the cross-slot self-edge arm
5023    /// ([`crate::AplicacaoError::MembroIsSelfAplicacao`]). Passes
5024    /// trivially on non-Aplicacao kinds (the fold's identity element).
5025    pub fn validate_aplicacao_shape(&self) -> Result<(), crate::AplicacaoError> {
5026        let Some(view) = self.aplicacao_view() else {
5027            return Ok(());
5028        };
5029        view.validate()?;
5030        crate::aplicacao::validate_no_self_membership(self.membros(), self.nome())?;
5031        Ok(())
5032    }
5033
5034    /// Compound per-`Caixa` entry gate on the Supervisor-kind
5035    /// supervision-tree slot family — folds the paired
5036    /// [`crate::SupervisorSpec::validate`] typed-shape cascade
5037    /// (`:estrategia` ↔ `:children` invariants, `:max-restarts` /
5038    /// `:restart-window` bounds, per-child DNS-1123 `:caixa` names,
5039    /// semver-valid `:versao` constraints, the set-not-multiset
5040    /// duplicate-child gate) plus the cross-slot self-edge gate
5041    /// ([`crate::supervisor::validate_no_self_supervision`], the
5042    /// `:children :caixa` ≠ `:nome` invariant the typed view cannot
5043    /// enforce on its own because it carries the children but not the
5044    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
5045    /// non-Supervisor kinds the fold is the identity element — the paired
5046    /// [`Self::supervisor_view`] accessor returns `None` off the
5047    /// Supervisor arm (peer with the [`Self::validate_limits`] /
5048    /// [`Self::validate_behavior`] M2 `Option`-arm identity element and
5049    /// the sibling per-Aplicacao [`Self::validate_aplicacao_shape`]),
5050    /// so the gate passes trivially without touching the supervision-tree
5051    /// slots.
5052    ///
5053    /// Prior to this lift the paired cascade lived only wired open-coded
5054    /// at the layout wire-up site
5055    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
5056    /// as the three-line `let view = caixa.supervisor_view().expect(...);
5057    /// view.validate() … validate_no_self_supervision(...) …` pattern
5058    /// paired with two `.map_err(|err| LayoutError::SupervisorViolation
5059    /// { caixa, issue })` wraps — every future consumer that wanted to
5060    /// gate the Supervisor-shape cascade as a whole (the wasm-operator's
5061    /// hierarchical reconciliation scheduler re-checking `:children` /
5062    /// `:estrategia` after a per-slot patch, the M4
5063    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
5064    /// webhook, a future `feira validate --supervisor` per-caixa
5065    /// admission verb, a per-Supervisor overlay resolver) was structurally
5066    /// forced to either re-inline the two-dispatch cascade in lockstep
5067    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5068    /// names as a bug) or call the whole
5069    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5070    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5071    /// such consumer reaches the two-arm compound gate through one call
5072    /// on the substrate primitive.
5073    ///
5074    /// Peer to the [`crate::render::require_supervisor_view`] compound
5075    /// entry gate every per-Supervisor *renderer* would route through
5076    /// (which already folds the same `spec.validate()` +
5077    /// `validate_no_self_supervision` two-arm cascade behind its
5078    /// `require_kind` + `validate_restart_window` prelude) — this gate
5079    /// mirrors the same fold on the *layout* path, so the two consumers
5080    /// of the Supervisor-shape cascade (the author-time gate and every
5081    /// per-Supervisor renderer) share one substrate primitive rather
5082    /// than two open-coded cascades kept in lockstep. Same lift
5083    /// discipline the peer per-slot compound gates
5084    /// ([`Self::validate_aplicacao_shape`] 949a7a0,
5085    /// [`Self::validate_upgrade_from`] d6801df,
5086    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5087    /// baa4688, [`Self::validate_behavior`] 0d2877a) each carry.
5088    ///
5089    /// # Errors
5090    ///
5091    /// Returns every [`crate::SupervisorError`] variant on the present-
5092    /// kind arm — the typed-shape cascade's per-slot arms first
5093    /// (matching [`crate::SupervisorSpec::validate`]'s declared order),
5094    /// then the cross-slot self-edge arm
5095    /// ([`crate::SupervisorError::ChildSupervisesSelf`]). Passes
5096    /// trivially on non-Supervisor kinds (the fold's identity element).
5097    pub fn validate_supervisor_shape(&self) -> Result<(), crate::SupervisorError> {
5098        let Some(view) = self.supervisor_view() else {
5099            return Ok(());
5100        };
5101        view.validate()?;
5102        crate::supervisor::validate_no_self_supervision(self.children(), self.nome())?;
5103        Ok(())
5104    }
5105
5106    /// Compound per-`Caixa` entry gate on the Acao-kind `:ci` slot
5107    /// family — folds the [`crate::decompose_ci`] typed decompose gate
5108    /// (`canteiro_types::decompose` refusing every illegal
5109    /// [`canteiro_types::CiRun`] shape: duplicate node name, dependency
5110    /// on an undeclared node, dependency cycle) onto one substrate
5111    /// primitive on [`Caixa`]. On non-`Acao` kinds the fold is the
5112    /// identity element — the paired [`Self::kind`] `is_acao()` guard
5113    /// short-circuits before the decompose gate ever fires (peer with
5114    /// the [`Self::validate_aplicacao_shape`] /
5115    /// [`Self::validate_supervisor_shape`] typed-view identity element
5116    /// and the [`Self::validate_limits`] / [`Self::validate_behavior`]
5117    /// M2 `Option`-arm identity element), so the gate passes trivially
5118    /// without touching the `:ci` slot. An `:kind Acao` caixa with
5119    /// `ci = None` is also an identity-element pass: the presence gate
5120    /// is the sibling axis owned by [`crate::LayoutError::MissingCi`] /
5121    /// [`crate::require_ci`] / [`crate::MissingCiSlot`], not by the
5122    /// decompose gate — a caixa that carries no `:ci` slot has no run
5123    /// to decompose. Same split the peer per-Servico
5124    /// [`crate::LayoutError::ServicoWithoutServicos`] presence gate and
5125    /// per-Binario [`crate::LayoutError::BinarioWithoutExe`] presence
5126    /// gate keep from their sibling per-slot shape gates, so the two
5127    /// axes stay separately diagnosable at the layout altitude.
5128    ///
5129    /// Prior to this lift the decompose gate lived only wired
5130    /// open-coded at the [`caixa_actions::validate`] renderer-side
5131    /// entry gate (routed through the substrate-canonical
5132    /// [`crate::require_acao_view`] compound helper) — the *layout*
5133    /// pipeline ([`crate::layout::StandardLayout::verify`], caixa-core/
5134    /// src/layout.rs) only checked `:ci` *presence* via
5135    /// [`crate::LayoutError::MissingCi`], so a `:kind Acao` caixa
5136    /// carrying a structurally illegal `:ci` (a duplicate node name, a
5137    /// dependency on an undeclared node, a dependency cycle) passed
5138    /// `feira build` cleanly and surfaced the diagnostic only when
5139    /// [`caixa_actions::validate`] later refused it — far from the
5140    /// source `caixa.lisp` on the author-time gate side. Every future
5141    /// consumer that wanted to gate the Acao-shape cascade as a whole
5142    /// (a per-`Acao` CR materializer's admission webhook re-checking
5143    /// `:ci` after a per-node patch, a future `feira validate --acao`
5144    /// per-caixa admission verb, a per-`Acao` overlay resolver
5145    /// rejecting an added / renamed node against a cluster-local
5146    /// snapshot) was structurally forced to either re-inline the
5147    /// decompose dispatch in lockstep with the renderer-side wire-up
5148    /// (the duplication the PRIME DIRECTIVE names as a bug) or call
5149    /// the whole [`caixa_actions::validate`] renderer and pay the
5150    /// per-node accumulation to re-check one slot. Post-fold each such
5151    /// consumer reaches the decompose gate through one call on the
5152    /// substrate primitive.
5153    ///
5154    /// Peer to the [`crate::require_acao_view`] compound entry gate
5155    /// every per-`Acao` *renderer* routes through (which already folds
5156    /// the same `require_ci + decompose_ci` two-arm cascade behind its
5157    /// `require_kind` prelude) — this gate mirrors the same fold on
5158    /// the *layout* path, so the two consumers of the Acao-shape
5159    /// cascade (the author-time gate and every per-`Acao` renderer)
5160    /// share one substrate primitive rather than two open-coded
5161    /// cascades kept in lockstep. Same lift discipline the peer
5162    /// per-kind compound gates ([`Self::validate_aplicacao_shape`]
5163    /// 949a7a0, [`Self::validate_supervisor_shape`] 4c70105,
5164    /// [`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5165    /// b5dd55e, [`Self::validate_limits`] baa4688,
5166    /// [`Self::validate_behavior`] 0d2877a) each carry. Closes the
5167    /// last per-kind asymmetry: with this lift the four typed
5168    /// named-caixa kinds (`Servico` / `Aplicacao` / `Supervisor` /
5169    /// `Acao`) each carry a compound per-`Caixa` shape gate on the
5170    /// substrate, and the layout pipeline routes through the same one
5171    /// substrate primitive per kind rather than four open-coded
5172    /// cascades.
5173    ///
5174    /// # Errors
5175    ///
5176    /// Returns the [`crate::CiDecomposeFailure`] typed view on the
5177    /// present-slot arm — the caixa's `:nome` alongside the borrowed
5178    /// [`canteiro_types::DecomposeError`] source (`DuplicateNode` /
5179    /// `UnknownDep` / `Cycle`) verbatim, so a consumer that fans on
5180    /// the specific arm reaches for `err.source` directly rather than
5181    /// re-parsing the Display bytes. Passes trivially on non-`Acao`
5182    /// kinds and on `:kind Acao` caixas with absent `:ci` (the fold's
5183    /// two identity-element arms).
5184    pub fn validate_acao_shape(&self) -> Result<(), crate::CiDecomposeFailure> {
5185        if !self.kind().is_acao() {
5186            return Ok(());
5187        }
5188        let Some(ci) = self.ci() else {
5189            return Ok(());
5190        };
5191        crate::render::decompose_ci(self, ci).map(|_| ())
5192    }
5193
5194    /// Compound per-`Caixa` kind ↔ typed-slot coherence gate on the
5195    /// three "declared but ignored" typed-slot families — M3 mesh
5196    /// (`:membros` / `:contratos` / `:politicas` / `:placement` /
5197    /// `:entrada`, owned by `:kind Aplicacao`, MESH-COMPOSITION §III.1),
5198    /// supervisor-tree (`:estrategia` / `:max-restarts` /
5199    /// `:restart-window` / `:children`, owned by `:kind Supervisor`,
5200    /// INSPIRATIONS §II.2), and M2 Servico-runtime (`:limits` /
5201    /// `:behavior` / `:upgrade-from`, owned by `:kind Servico`,
5202    /// INSPIRATIONS §III.1 / §II.3 / §II.4). Folds the three sibling
5203    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5204    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5205    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
5206    /// gates — each pre-lift a self-similar five-line
5207    /// `if !caixa.kind().is_<owner>() { let slots = caixa.declared_
5208    /// <family>_slots(); if !slots.is_empty() { return
5209    /// Err(LayoutError::<family>_on_non_<owner>(caixa, slots)); } }`
5210    /// block at [`crate::layout::StandardLayout::verify`] — onto one
5211    /// substrate primitive on [`Caixa`]. Every arm passes as an
5212    /// identity element on the owner kind (the paired
5213    /// [`Self::kind`] `is_<owner>()` guard short-circuits before the
5214    /// per-family `declared_*_slots` gate fires) and on non-owner
5215    /// kinds carrying no declared slot in that family (the
5216    /// [`Vec::is_empty`] check short-circuits before the wrap fires),
5217    /// so a bare no-code caixa on any kind passes the fold trivially
5218    /// on all three arms.
5219    ///
5220    /// Prior to this lift the three-arm cascade lived only wired
5221    /// open-coded at the layout wire-up site
5222    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/
5223    /// layout.rs) as three self-similar five-line blocks paired with
5224    /// three [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5225    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5226    /// [`crate::LayoutError::servico_slots_on_non_servico`] ctor
5227    /// dispatches (each of which the peer
5228    /// [`crate::layout::layout_slot_kind_ctors!`] macro already folds
5229    /// onto one substrate primitive per typed variant, 0419438) —
5230    /// every future consumer that wanted to gate the whole
5231    /// kind-coherence cascade as a unit (the deferred
5232    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5233    /// webhook re-checking every typed-slot family after a per-slot
5234    /// patch, a future `feira validate --kind-coherence` per-caixa
5235    /// admission verb, a per-`Caixa` overlay resolver rejecting a
5236    /// kind-foreign patch against a cluster-local snapshot) was
5237    /// structurally forced to either re-inline the three-block
5238    /// cascade in lockstep with the layout wire-up (the duplication
5239    /// the PRIME DIRECTIVE names as a bug) or call the whole
5240    /// [`crate::layout::StandardLayout::verify`] pipeline and pay
5241    /// every peer per-`Caixa` gate to re-check three slot families.
5242    /// Post-fold each such consumer reaches the three-arm cascade
5243    /// through one call on the substrate primitive.
5244    ///
5245    /// Diagnostic order matches the pre-fold layout wire-up
5246    /// canonical sequence — mesh → supervisor → servico — pinned by
5247    /// the load-bearing
5248    /// `validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm`
5249    /// / `_supervisor_arm_fires_before_servico_arm` ordering pins
5250    /// below. The three arms enumerate every typed-slot family the
5251    /// substrate carries whose "declared but ignored" footgun is
5252    /// gated at the layout altitude by a `{ caixa, kind, slots }`
5253    /// wrap variant — the peer
5254    /// [`crate::LayoutError::ForeignCodeSlot`] gate on the
5255    /// code-surface family sits outside this fold because
5256    /// [`Self::declared_foreign_code_slots`] bakes the kind-check
5257    /// into the helper (so the layout wire-up carries no outer
5258    /// `if !caixa.kind().is_<owner>()` guard), and the peer
5259    /// [`crate::LayoutError::CiOnNonAcao`] gate on the `:ci` axis
5260    /// carries a distinct `{ caixa, kind }` wrap shape (no `slots`
5261    /// field — `:ci` is a single `Option` not a `Vec`-of-named-slots)
5262    /// and rides on its own peer substrate primitive
5263    /// [`Self::validate_ci_kind_coherence`] (the direct sibling to
5264    /// this fold on the `:ci` axis) — the two folds share the same
5265    /// altitude and diagnostic order at the layout wire-up site but
5266    /// keep their distinct envelope shapes, so no consumer of
5267    /// `CiOnNonAcao` sees a variant rename.
5268    ///
5269    /// Peer to the per-kind compound entry gates every substrate
5270    /// primitive on the M2/M3 typed-slot family already carries
5271    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5272    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5273    /// [`Self::validate_upgrade_from`] d6801df,
5274    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5275    /// [`Self::validate_supervisor_shape`] 4c70105,
5276    /// [`Self::validate_acao_shape`] 5d6df54): the author-time gate
5277    /// axis on the *per-slot* algebra now shares one substrate
5278    /// primitive per compound gate, and this lift closes the
5279    /// symmetric axis on the *cross-family* kind ↔ slot coherence
5280    /// algebra so the layout pipeline routes the three self-similar
5281    /// gates through one substrate primitive rather than three
5282    /// open-coded blocks. Every future kind that adds its own
5283    /// exclusive typed-slot family (an `Actor`-owned per-virtual-
5284    /// actor grain slot the M5 Orleans-inspired kind reaches
5285    /// through, a per-Aplicacao overlay slot the M4 CR materializer
5286    /// consults) folds onto this compound gate as one arm addition
5287    /// rather than a fourth open-coded block at the wire-up site.
5288    ///
5289    /// # Errors
5290    ///
5291    /// Returns the first [`crate::LayoutError`] variant surfacing
5292    /// under the canonical mesh → supervisor → servico order:
5293    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] on a non-
5294    /// Aplicacao caixa with a declared M3 mesh slot,
5295    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] on a
5296    /// non-Supervisor caixa with a declared supervisor-tree slot,
5297    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] on a
5298    /// non-Servico caixa with a declared M2 slot. Passes trivially
5299    /// on the owner kind of each family and on non-owner kinds
5300    /// carrying no declared slot in that family (the fold's identity
5301    /// element on both axes).
5302    pub fn validate_kind_slot_coherence(&self) -> Result<(), crate::LayoutError> {
5303        // Each of the three arms routes through the shared
5304        // [`Self::run_kind_owned_slot_family_gate`] substrate primitive
5305        // — the outer non-owner-kind guard + inner accumulator + inner
5306        // emptiness-guard + wrap arm shape now lands on one dispatch
5307        // per family rather than a four-line open-coded block in
5308        // lockstep across all three arms. Canonical mesh → supervisor
5309        // → servico order preserved (the primitive short-circuits
5310        // arm-by-arm; the outer `?;` cascade at this altitude threads
5311        // the first surfaced arm's error verbatim). Each of the three
5312        // ctors ([`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5313        // [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5314        // [`crate::LayoutError::servico_slots_on_non_servico`]) was
5315        // already lifted onto the substrate by the peer
5316        // [`crate::layout::layout_slot_kind_ctors!`] macro, so each arm
5317        // routes through the same substrate-canonical
5318        // `Self::<variant> { caixa, kind, slots }` wrap per arm as the
5319        // pre-lift open-coded blocks — byte-equal, pinned by the
5320        // paired `validate_kind_slot_coherence_folds_<family>_arm_matches_gate`
5321        // equivalence pins and the peer
5322        // `validate_kind_slot_coherence_{mesh,supervisor}_arm_fires_before_<next>_arm`
5323        // ordering pins.
5324        self.run_kind_owned_slot_family_gate(
5325            crate::CaixaKind::is_aplicacao,
5326            Caixa::declared_mesh_slots,
5327            crate::LayoutError::mesh_slots_on_non_aplicacao,
5328        )?;
5329        self.run_kind_owned_slot_family_gate(
5330            crate::CaixaKind::is_supervisor,
5331            Caixa::declared_supervisor_slots,
5332            crate::LayoutError::supervisor_slots_on_non_supervisor,
5333        )?;
5334        self.run_kind_owned_slot_family_gate(
5335            crate::CaixaKind::is_servico,
5336            Caixa::declared_servico_slots,
5337            crate::LayoutError::servico_slots_on_non_servico,
5338        )?;
5339        Ok(())
5340    }
5341
5342    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on
5343    /// the three no-code kinds — `Supervisor` (supervises other
5344    /// caixas, INSPIRATIONS §II.2), `Aplicacao` (composes Servicos,
5345    /// MESH-COMPOSITION §III.1), and `Acao` (owns a typed CI run,
5346    /// CANTEIRO §7.1-C). Each carries no code of its own, so
5347    /// declaring any of `:bibliotecas` / `:exe` / `:servicos`
5348    /// silently passes the layout's path-existence loops (the paths
5349    /// still resolve on disk) and then vanishes downstream — the
5350    /// per-kind renderers gate emission on
5351    /// [`crate::render::require_kind`] and only emit the code
5352    /// surface for its owning kind, so a declared code slot on a
5353    /// no-code kind is the manifest field's documented "ignored
5354    /// otherwise" footgun.
5355    ///
5356    /// Pre-lift each of the three arms lived as a self-similar
5357    /// `if !caixa.kind().is_<no-code-kind>() { … } else if has_code
5358    /// { return Err(LayoutError::<kind>_owns_code(caixa)); }` block
5359    /// at [`crate::layout::StandardLayout::verify`] — three
5360    /// consumers, three identical shapes. Every future consumer
5361    /// that wanted to gate the whole code-surface coherence cascade
5362    /// as a unit (the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
5363    /// materializer's admission webhook re-checking after a
5364    /// per-slot patch, a future `feira validate --no-code-kind`
5365    /// per-caixa admission verb, a per-`Caixa` overlay resolver
5366    /// rejecting a kind-foreign patch) was structurally forced to
5367    /// either re-inline the three-block cascade in lockstep with
5368    /// the layout wire-up (the duplication the PRIME DIRECTIVE
5369    /// names as a bug) or call the whole
5370    /// [`crate::layout::StandardLayout::verify`] pipeline. Post-fold
5371    /// each such consumer reaches the three-arm cascade through
5372    /// one call.
5373    ///
5374    /// Mirror of the sibling [`Self::validate_kind_slot_coherence`]
5375    /// fold (f0d286e) on the author-time typed-slot coherence axis:
5376    /// that gate closes the "non-owner kind declares owner-only
5377    /// typed slots" three-arm cascade on the M2 / supervisor-tree /
5378    /// M3 slot families; this gate closes the reciprocal
5379    /// "no-code kind declares code" three-arm cascade on the
5380    /// `:bibliotecas` / `:exe` / `:servicos` code surface. Together
5381    /// the two folds route every kind ↔ author-shape coherence
5382    /// diagnostic at the layout altitude through one substrate
5383    /// primitive per axis.
5384    ///
5385    /// The gate carries two identity elements:
5386    /// - **`has_code == false`** — any kind (including the three
5387    ///   no-code kinds) that declares no code passes the paired
5388    ///   `!has_code` short-circuit before every per-arm dispatch.
5389    /// - **Code-owning kinds** (`Biblioteca` owning
5390    ///   `:bibliotecas`, `Binario` owning `:exe`, `Servico` owning
5391    ///   `:servicos`) — the three no-code arm-firing predicates
5392    ///   short-circuit on every code-owning kind, so the gate
5393    ///   passes trivially regardless of what code they declare.
5394    ///   Foreign-code-slot violations on a code-owning kind (e.g.
5395    ///   `:kind Servico` declaring `:exe`) surface through the
5396    ///   sibling [`crate::LayoutError::ForeignCodeSlot`] gate on
5397    ///   [`Self::declared_foreign_code_slots`], not through this
5398    ///   gate.
5399    ///
5400    /// Unlike the sibling cross-family
5401    /// [`Self::validate_kind_slot_coherence`], the three arms of
5402    /// this fold are mutually exclusive by construction — `:kind`
5403    /// is a single-valued [`CaixaKind`] discriminator so at most
5404    /// one arm can fire per caixa — and no cross-arm ordering pin
5405    /// is meaningful (the pre-fold three-block cascade at the
5406    /// wire-up site was already unreachable past the first
5407    /// matching arm).
5408    ///
5409    /// Peer to the per-kind compound entry gates every substrate
5410    /// primitive on the M2/M3 typed-slot family already carries
5411    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5412    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5413    /// [`Self::validate_upgrade_from`] d6801df,
5414    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5415    /// [`Self::validate_supervisor_shape`] 4c70105,
5416    /// [`Self::validate_acao_shape`] 5d6df54,
5417    /// [`Self::validate_kind_slot_coherence`] f0d286e): the
5418    /// author-time gate axis on the *per-slot* and *cross-family
5419    /// typed-slot* algebras each share one substrate primitive per
5420    /// compound gate, and this lift closes the third axis on the
5421    /// *code-surface* algebra so the layout pipeline routes all
5422    /// three coherence axes through one substrate primitive rather
5423    /// than nine open-coded blocks. Every future no-code kind
5424    /// (an `Actor` virtual-actor arm the M5 Orleans-inspired kind
5425    /// reaches through if it lands as a no-code composer, a future
5426    /// `Namespace` grouping kind) folds onto this compound gate
5427    /// as one arm addition rather than a fourth open-coded block
5428    /// at the wire-up site.
5429    ///
5430    /// # Errors
5431    ///
5432    /// Returns the [`crate::LayoutError`] variant naming the
5433    /// offending no-code kind:
5434    /// [`crate::LayoutError::SupervisorOwnsCode`] on a `:kind
5435    /// Supervisor` caixa with any declared code,
5436    /// [`crate::LayoutError::AplicacaoOwnsCode`] on a `:kind
5437    /// Aplicacao` caixa with any declared code,
5438    /// [`crate::LayoutError::AcaoOwnsCode`] on a `:kind Acao` caixa
5439    /// with any declared code. Passes trivially on every kind with
5440    /// no declared code and on every code-owning kind regardless
5441    /// of declared code (the fold's two identity-element arms).
5442    pub fn validate_no_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5443        let has_code =
5444            !self.bibliotecas().is_empty() || !self.exe().is_empty() || !self.servicos().is_empty();
5445        if !has_code {
5446            return Ok(());
5447        }
5448        if self.kind().is_supervisor() {
5449            return Err(crate::LayoutError::supervisor_owns_code(self));
5450        }
5451        if self.kind().is_aplicacao() {
5452            return Err(crate::LayoutError::aplicacao_owns_code(self));
5453        }
5454        if self.kind().is_acao() {
5455            return Err(crate::LayoutError::acao_owns_code(self));
5456        }
5457        Ok(())
5458    }
5459
5460    /// Compound per-`Caixa` kind ↔ `:ci` coherence gate — the `Acao`
5461    /// axis-only companion to the sibling three-arm
5462    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) on the
5463    /// M3 mesh / supervisor-tree / M2 Servico-runtime typed-slot
5464    /// families. `:ci` carries a typed CI run
5465    /// ([`canteiro_types::CiRun`], CANTEIRO §7.1-C) that only the
5466    /// `caixa-actions` renderer decomposes + validates and only for a
5467    /// `:kind Acao`. On any *other* kind a declared `:ci` is the
5468    /// manifest field's documented "ignored otherwise" — it silently
5469    /// passes verify and then vanishes (never decomposed, never
5470    /// rendered), far from the source `caixa.lisp`.
5471    ///
5472    /// Pre-lift the arm lived as a self-similar
5473    /// `if caixa.ci().is_some() && !caixa.kind().is_acao() { return
5474    /// Err(LayoutError::CiOnNonAcao { caixa: caixa.nome().to_string(),
5475    /// kind: caixa.kind() }); }` block at
5476    /// [`crate::layout::StandardLayout::verify`] — one consumer today
5477    /// but every future consumer that wanted to gate the `:ci`
5478    /// coherence axis as a unit (the deferred
5479    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5480    /// webhook re-checking after a per-slot patch, a future
5481    /// `feira validate --ci-coherence` per-caixa admission verb, a
5482    /// per-`Caixa` overlay resolver rejecting a kind-foreign `:ci`
5483    /// patch) was structurally forced to either re-inline the
5484    /// two-condition guard in lockstep with the layout wire-up (the
5485    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5486    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5487    /// Post-fold each such consumer reaches the arm through one call.
5488    ///
5489    /// Peer of the sibling three-arm
5490    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5491    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5492    /// axes under a uniform `{ caixa, kind, slots }` envelope
5493    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5494    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5495    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]). The `:ci`
5496    /// axis stays on its own primitive because
5497    /// [`crate::LayoutError::CiOnNonAcao`] carries a distinct
5498    /// `{ caixa, kind }` wrap shape (no `slots` field — `:ci` is a
5499    /// single `Option` not a `Vec`-of-named-slots) whose reshape
5500    /// onto the sibling `{ caixa, kind, slots }` envelope would
5501    /// force a variant rename touching every consumer of
5502    /// `CiOnNonAcao`; the two folds share the same
5503    /// author-time-vs-renderer split and diagnostic altitude, and
5504    /// route through peer substrate primitives on the same
5505    /// [`Caixa`] surface.
5506    ///
5507    /// Peer to the per-kind compound entry gates every substrate
5508    /// primitive on the M2/M3 typed-slot family already carries
5509    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5510    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5511    /// [`Self::validate_upgrade_from`] d6801df,
5512    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5513    /// [`Self::validate_supervisor_shape`] 4c70105,
5514    /// [`Self::validate_acao_shape`] 5d6df54,
5515    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5516    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2): every
5517    /// author-time coherence axis on the typed [`Caixa`] surface now
5518    /// routes through one substrate primitive per axis rather than
5519    /// an open-coded block at the layout wire-up site.
5520    ///
5521    /// The gate carries two identity elements:
5522    /// - **`ci().is_none()`** — a caixa that declares no `:ci`
5523    ///   passes the first short-circuit before every per-arm
5524    ///   dispatch, on every kind. The canonical shape of the four
5525    ///   non-`Acao` kinds (`Biblioteca` / `Binario` / `Servico` /
5526    ///   `Supervisor` / `Aplicacao`) is `ci = None` — the arm
5527    ///   never fires on a well-shaped fixture.
5528    /// - **`:kind Acao`** — the owner-kind arm short-circuits on
5529    ///   every `Acao` caixa regardless of its `:ci` shape; a
5530    ///   malformed `:ci` on an `Acao` surfaces through the peer
5531    ///   [`Self::validate_acao_shape`] compound decompose gate
5532    ///   (5d6df54), not through this coherence gate.
5533    ///
5534    /// # Errors
5535    ///
5536    /// Returns [`crate::LayoutError::CiOnNonAcao`] naming the
5537    /// offending caixa's nome + kind on any non-`Acao` caixa with
5538    /// `:ci` declared. Passes trivially on every kind that declares
5539    /// no `:ci` and on every `:kind Acao` caixa regardless of
5540    /// declared `:ci` (the fold's two identity-element arms).
5541    pub fn validate_ci_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5542        if self.ci().is_some() && !self.kind().is_acao() {
5543            return Err(crate::LayoutError::CiOnNonAcao {
5544                caixa: self.nome().to_string(),
5545                kind: self.kind(),
5546            });
5547        }
5548        Ok(())
5549    }
5550
5551    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on the
5552    /// two exclusive code-surface slots — `:exe` (owned only by
5553    /// [`crate::CaixaKind::Binario`], the nix-built executable surface)
5554    /// and `:servicos` (owned only by [`crate::CaixaKind::Servico`],
5555    /// the wasm-component + `ComputeUnit` daemon surface). The
5556    /// `caixa-helm` / `caixa-flux` / `caixa-flake` renderers gate
5557    /// emission on [`crate::render::require_kind`]`(_, <owning-kind>)`
5558    /// and only emit the slot for its owning kind — so on any *other*
5559    /// code-running kind a declared `:exe` / `:servicos` is the
5560    /// manifest field's documented "ignored otherwise": the path is
5561    /// validated by the per-kind path-existence loops in
5562    /// [`crate::layout::StandardLayout::verify`], but the value is
5563    /// never rendered into a build target or programs.yaml entry —
5564    /// it silently passes `feira build` and then vanishes, far from
5565    /// the source `caixa.lisp`, with no field naming which slot is
5566    /// foreign.
5567    ///
5568    /// Pre-lift the arm lived as a self-similar four-line `let
5569    /// foreign_code_slots = caixa.declared_foreign_code_slots(); if
5570    /// !foreign_code_slots.is_empty() { return
5571    /// Err(LayoutError::foreign_code_slot(caixa, foreign_code_slots));
5572    /// }` block at [`crate::layout::StandardLayout::verify`] — one
5573    /// consumer today but every future consumer that wanted to gate
5574    /// the code-surface coherence axis as a unit (the deferred
5575    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5576    /// webhook re-checking after a per-slot patch, a future
5577    /// `feira validate --foreign-code` per-caixa admission verb, a
5578    /// per-`Caixa` overlay resolver rejecting a kind-foreign code-
5579    /// slot patch) was structurally forced to either re-inline the
5580    /// two-condition guard in lockstep with the layout wire-up (the
5581    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5582    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5583    /// Post-fold each such consumer reaches the arm through one call.
5584    ///
5585    /// Peer of the sibling three-arm
5586    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5587    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5588    /// axes under the uniform `{ caixa, kind, slots }` envelope
5589    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5590    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5591    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]); this gate
5592    /// carries the code-surface axis under the same
5593    /// `{ caixa, kind, slots }` envelope
5594    /// ([`crate::LayoutError::ForeignCodeSlot`]). The two folds share
5595    /// the envelope shape but stay separate primitives because the
5596    /// per-arm predicate differs: the cross-family fold rides on the
5597    /// outer `!self.kind().is_<owner>()` guard *paired* with a
5598    /// per-family `declared_<family>_slots` accumulator, while this
5599    /// fold's per-arm kind-check is baked into
5600    /// [`Self::declared_foreign_code_slots`] itself (each arm's
5601    /// `!self.kind().requires_<slot>()` guard fires inside the
5602    /// accumulator, not around it) — so a `:kind Binario` declaring
5603    /// `:servicos` and a `:kind Servico` declaring `:exe` are both
5604    /// caught by one accumulator sweep rather than by two independent
5605    /// arm dispatches. Peer with [`Self::validate_ci_kind_coherence`]
5606    /// (9b55beb) which carries the `:ci` axis on its own primitive
5607    /// for the same "distinct per-arm predicate shape, shared
5608    /// diagnostic altitude" reason.
5609    ///
5610    /// Peer to the per-kind and per-slot compound entry gates every
5611    /// substrate primitive on the M2/M3 typed-slot family already
5612    /// carries ([`Self::validate_deps`] b5dd55e,
5613    /// [`Self::validate_limits`] baa4688,
5614    /// [`Self::validate_behavior`] 0d2877a,
5615    /// [`Self::validate_upgrade_from`] d6801df,
5616    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5617    /// [`Self::validate_supervisor_shape`] 4c70105,
5618    /// [`Self::validate_acao_shape`] 5d6df54,
5619    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5620    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5621    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5622    /// author-time coherence axis on the typed [`Caixa`] surface now
5623    /// routes through one substrate primitive per axis rather than an
5624    /// open-coded block at the layout wire-up site. This closes the
5625    /// last open-coded kind ↔ slot coherence gate at the layout
5626    /// altitude — every kind-coherence diagnostic is now a substrate
5627    /// primitive.
5628    ///
5629    /// The gate carries three identity elements:
5630    /// - **Code-owning kinds on their native slot** — a
5631    ///   [`crate::CaixaKind::Binario`] declaring `:exe`, a
5632    ///   [`crate::CaixaKind::Servico`] declaring `:servicos` — each
5633    ///   arm's `!requires_<slot>()` predicate short-circuits inside
5634    ///   [`Self::declared_foreign_code_slots`], so the accumulator
5635    ///   returns an empty `Vec` and the outer `is_empty` short-
5636    ///   circuits before the wrap fires.
5637    /// - **Bare caixas** — a caixa with no declared code on any kind
5638    ///   passes the same accumulator's `is_empty` short-circuit on
5639    ///   every arm.
5640    /// - **No-code kinds** ([`crate::CaixaKind::Supervisor`] /
5641    ///   [`crate::CaixaKind::Aplicacao`] / [`crate::CaixaKind::Acao`])
5642    ///   declaring code — dominated upstream by the sibling
5643    ///   [`Self::validate_no_code_kind_coherence`] (3bbf6a2) which
5644    ///   surfaces [`crate::LayoutError::SupervisorOwnsCode`] /
5645    ///   [`crate::LayoutError::AplicacaoOwnsCode`] /
5646    ///   [`crate::LayoutError::AcaoOwnsCode`] first at the layout
5647    ///   wire-up site, so this gate never fires on a no-code kind
5648    ///   through the layout pipeline. A standalone caller reaching
5649    ///   this primitive without the sibling `_no_code_` gate first
5650    ///   would see a no-code kind's declared `:exe` / `:servicos`
5651    ///   surface `ForeignCodeSlot` here (the two folds partition the
5652    ///   diagnostic responsibility along the "declared no-code slot"
5653    ///   axis: no-code kinds get `OwnsCode`, code-running kinds get
5654    ///   `ForeignCodeSlot`), and the layout wire-up's canonical
5655    ///   `_no_code_` → `_foreign_code_` ordering keeps the
5656    ///   [`crate::LayoutError::SupervisorOwnsCode`] / … arm the one
5657    ///   that surfaces in the composed pipeline.
5658    ///
5659    /// Diagnostic order within the arm matches the pre-fold layout
5660    /// wire-up canonical sequence — `:exe` → `:servicos` — pinned by
5661    /// [`Self::declared_foreign_code_slots`]'s per-arm push order.
5662    ///
5663    /// # Errors
5664    ///
5665    /// Returns [`crate::LayoutError::ForeignCodeSlot`] naming the
5666    /// offending caixa's nome + kind + declared foreign-code slot
5667    /// list on any code-running kind ([`crate::CaixaKind::Biblioteca`]
5668    /// / [`crate::CaixaKind::Binario`] / [`crate::CaixaKind::Servico`])
5669    /// declaring another code-running kind's exclusive code surface.
5670    /// Passes trivially on every native-slot declaration (Binario
5671    /// with `:exe`, Servico with `:servicos`), on every bare caixa,
5672    /// and on every no-code kind (dominated upstream by the sibling
5673    /// [`Self::validate_no_code_kind_coherence`] `OwnsCode` gates —
5674    /// see the identity-element notes above).
5675    pub fn validate_foreign_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5676        let foreign_code_slots = self.declared_foreign_code_slots();
5677        if !foreign_code_slots.is_empty() {
5678            return Err(crate::LayoutError::foreign_code_slot(
5679                self,
5680                foreign_code_slots,
5681            ));
5682        }
5683        Ok(())
5684    }
5685
5686    /// Compound per-`Caixa` required-slot gate on the three
5687    /// [`crate::CaixaKind`] arms whose sole payload is a canonical
5688    /// typed slot: `Binario`'s `:exe`, `Servico`'s `:servicos`,
5689    /// `Acao`'s `:ci`. Each arm refuses a caixa on its owner kind
5690    /// that declares no value in the corresponding required slot,
5691    /// so `feira build` (the canonical author-time gate) surfaces the
5692    /// self-locating "this kind needs this slot" diagnostic at the
5693    /// source `caixa.lisp` rather than deferring the failure to a
5694    /// downstream consumer (a nix build with no `:exe` to build, a
5695    /// programs.yaml fan-out with no `:servicos` to enumerate, a
5696    /// `caixa-actions` decompose with no `:ci` to walk).
5697    ///
5698    /// Pre-lift each of the three arms lived as a self-similar
5699    /// `if caixa.kind().requires_<slot>() && caixa.<slot>().is_<empty>() {
5700    /// return Err(LayoutError::<kind>_without_<slot>(caixa)); }`
5701    /// block at [`crate::layout::StandardLayout::verify`] — three
5702    /// consumers, three identical shapes, one substrate primitive on
5703    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
5704    /// a bug. Each of the three inner ctors
5705    /// ([`crate::LayoutError::binario_without_exe`] /
5706    /// [`crate::LayoutError::servico_without_servicos`] /
5707    /// [`crate::LayoutError::missing_ci`]) was already lifted onto
5708    /// the substrate by the peer [`crate::layout::layout_nome_only_ctors!`]
5709    /// macro, so the primitive routes through the same
5710    /// `Self::<variant>(caixa.nome().to_string())` tuple-literal
5711    /// wrap per arm as the pre-lift open-coded blocks.
5712    ///
5713    /// The paired `Biblioteca`-arm required-slot check
5714    /// ([`crate::LayoutError::MissingLib`]) stays open-coded at the
5715    /// layout wire-up site by design: it needs the filesystem oracle
5716    /// on [`crate::layout::LayoutInvariants`] to check the default
5717    /// `lib/<nome>.lisp` fallback path, which the pure per-`Caixa`
5718    /// typed-shape surface this fold rides on has no reference to.
5719    /// Same posture the peer [`Self::validate_no_code_kind_coherence`]
5720    /// fold takes on the on-disk existence loops.
5721    ///
5722    /// Diagnostic order at the primitive matches the pre-fold layout
5723    /// wire-up canonical sequence — `:exe` → `:servicos` → `:ci` —
5724    /// the same three-arm sweep the peer [`crate::CaixaKind`]
5725    /// discriminator carries at its `requires_*` accessors. Unlike
5726    /// the sibling cross-family [`Self::validate_kind_slot_coherence`]
5727    /// fold, the three arms of this fold are mutually exclusive by
5728    /// construction — `:kind` is a single-valued [`crate::CaixaKind`]
5729    /// discriminator so at most one arm can fire per caixa — and no
5730    /// cross-arm ordering pin is meaningful (the pre-fold three-block
5731    /// cascade at the wire-up site was already unreachable past the
5732    /// first matching arm).
5733    ///
5734    /// Peer to the per-kind and per-slot compound entry gates every
5735    /// substrate primitive on the M2/M3 typed-slot family already
5736    /// carries ([`Self::validate_deps`] b5dd55e,
5737    /// [`Self::validate_limits`] baa4688,
5738    /// [`Self::validate_behavior`] 0d2877a,
5739    /// [`Self::validate_upgrade_from`] d6801df,
5740    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5741    /// [`Self::validate_supervisor_shape`] 4c70105,
5742    /// [`Self::validate_acao_shape`] 5d6df54,
5743    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5744    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5745    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5746    /// author-time coherence axis on the typed [`Caixa`] surface
5747    /// now routes through one substrate primitive per axis rather
5748    /// than an open-coded block at the layout wire-up site.
5749    ///
5750    /// The gate carries two identity elements:
5751    /// - **Non-owner kinds** — each per-arm predicate is
5752    ///   `self.kind().requires_<slot>()`, which returns `true` only
5753    ///   for the owning kind ([`crate::CaixaKind::Binario`] on `:exe`,
5754    ///   [`crate::CaixaKind::Servico`] on `:servicos`,
5755    ///   [`crate::CaixaKind::Acao`] on `:ci`). Every non-owner kind
5756    ///   passes each per-arm dispatch trivially.
5757    /// - **Owner kinds with the required slot present** — a
5758    ///   [`crate::CaixaKind::Binario`] with a non-empty `:exe`, a
5759    ///   [`crate::CaixaKind::Servico`] with a non-empty `:servicos`,
5760    ///   an [`crate::CaixaKind::Acao`] with `ci = Some(_)` — passes
5761    ///   its arm's `is_empty` / `is_none` short-circuit.
5762    ///
5763    /// # Errors
5764    ///
5765    /// Returns the [`crate::LayoutError`] variant naming the
5766    /// offending owner kind:
5767    /// [`crate::LayoutError::BinarioWithoutExe`] on a
5768    /// [`crate::CaixaKind::Binario`] caixa with no declared `:exe`,
5769    /// [`crate::LayoutError::ServicoWithoutServicos`] on a
5770    /// [`crate::CaixaKind::Servico`] caixa with no declared
5771    /// `:servicos`, [`crate::LayoutError::MissingCi`] on a
5772    /// [`crate::CaixaKind::Acao`] caixa with no declared `:ci`.
5773    /// Passes trivially on every non-owner kind and on every owner
5774    /// kind with its required slot present.
5775    pub fn validate_required_kind_slot(&self) -> Result<(), crate::LayoutError> {
5776        if self.kind().requires_exe() && self.exe().is_empty() {
5777            return Err(crate::LayoutError::binario_without_exe(self));
5778        }
5779        if self.kind().requires_servicos() && self.servicos().is_empty() {
5780            return Err(crate::LayoutError::servico_without_servicos(self));
5781        }
5782        if self.kind().requires_ci() && self.ci().is_none() {
5783            return Err(crate::LayoutError::missing_ci(self));
5784        }
5785        Ok(())
5786    }
5787
5788    /// Reject per-entry values on the three Caixa-level code-surface
5789    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
5790    /// layout checker's `root.join(p)` sandbox would silently subvert.
5791    /// Same three structural footguns the peer
5792    /// [`BehaviorSpec::validate`] (b0c8389) and
5793    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
5794    /// (26da2c7) already close on the M2 `:behavior :on-*` and
5795    /// `:upgrade-from :state-change :script` axes, here lifted onto
5796    /// the three top-level code-path axes through the shared
5797    /// [`is_sandboxed_relative_path`] predicate:
5798    ///
5799    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
5800    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
5801    ///     [`Path::join`] as the base itself — `root.join("")` ==
5802    ///     `root`, so the existence check (`self.exists(&root)`)
5803    ///     trivially passes (the project root exists), and the layout
5804    ///     silently treats the project root as a biblioteca / exe /
5805    ///     servico entry. The `:bibliotecas` loop then hands the root
5806    ///     to `tatara_lisp::read` at `feira build` time as if the root
5807    ///     directory itself were a Lisp source file — a parse error
5808    ///     far from the source `caixa.lisp` with no field naming the
5809    ///     offending entry.
5810    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
5811    ///     [`Path::join`] *replaces* the base when the right-hand side
5812    ///     is absolute, so `root.join("/etc/passwd")` resolves to
5813    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
5814    ///     The existence check then silently consults whatever the
5815    ///     escaped path resolves to — for `:bibliotecas`, the layout
5816    ///     has no `starts_with`-fence (only `:exe` is fenced under
5817    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
5818    ///     `:bibliotecas` entry that happens to resolve on disk
5819    ///     silently passes. For `:exe` / `:servicos` the fence catches
5820    ///     the absolute case downstream as `ExeOutsideDir` /
5821    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
5822    ///     doesn't exist), but with a downstream-shaped diagnostic
5823    ///     that names the resolved escape path rather than the
5824    ///     authoring footgun at the source.
5825    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
5826    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
5827    ///     [`std::path::Component::ParentDir`] anywhere round-trips
5828    ///     through [`Path::join`] as a traversal above the caixa root.
5829    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
5830    ///     *component-aware* (not canonical-path-aware), so
5831    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
5832    ///     is **true** even though the canonical resolution
5833    ///     `{parent of root}/escape.lisp` lives outside the caixa root
5834    ///     — the fence silently lets the parent-escape through, and
5835    ///     the existence check passes if that escape-target happens
5836    ///     to exist. Caught regardless of where the `..` sits
5837    ///     (leading, mid-path, trailing) so the gate matches the peer
5838    ///     predicate's full coverage.
5839    ///
5840    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
5841    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
5842    /// same per-slot diagnostic shape every peer per-axis path-gate
5843    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
5844    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
5845    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
5846    /// order [`Caixa::declared_foreign_code_slots`] uses for its
5847    /// canonical foreign-code-slot diagnostic, so a manifest with
5848    /// multiple malformed slots surfaces the lexicographically-earliest
5849    /// slot's diagnostic deterministically.
5850    ///
5851    /// Lifted to the typed surface as a Caixa-level validator (peer
5852    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
5853    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
5854    /// and wired into [`crate::StandardLayout::verify`] before the
5855    /// existence-check loops so the diagnostic names the offending
5856    /// slot at the source caixa.lisp rather than reporting a
5857    /// downstream `MissingEntry` / `ExeOutsideDir` /
5858    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
5859    /// The fourth typed code-path surface — every author-supplied
5860    /// path on the manifest — is now structurally accept-shaped
5861    /// past validate, peer with `:behavior :on-*` and
5862    /// `:upgrade-from :state-change :script`.
5863    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
5864        /// Per-slot file-type contract for the three Caixa-level
5865        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
5866        /// Each variant names the predicate the per-entry file-type
5867        /// gate consults; [`Self::None`] opts the slot out of any
5868        /// file-type contract. Lifted as a typed local enum so the
5869        /// per-slot dispatch is exhaustive at the `match` — adding a
5870        /// future axis to the typed-substrate `:` slot set (the
5871        /// future `:assets` resource axis the M5 roadmap names, the
5872        /// future `:nix-flake` derivation axis the caixa-flake
5873        /// emitter consults) lands as one variant + one `match` arm,
5874        /// not a coordinated rewrite of every per-slot bool flag.
5875        ///
5876        /// Peer of the typed-substrate per-slot variant disciplines
5877        /// already established on this surface
5878        /// ([`crate::supervisor::RestartStrategy`] +
5879        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
5880        /// supervision-tree axis,
5881        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
5882        /// placement axis, [`crate::aplicacao::WitTarget`] on the
5883        /// `:contratos` payload-target axis): the typed `enum` is
5884        /// the substrate's single source of truth for the per-axis
5885        /// dispatch, and every consumer (the per-arm body here, the
5886        /// future feira-lint per-slot diagnostic renderer, the M4
5887        /// per-axis admission webhook) reaches for the same typed
5888        /// surface rather than re-deriving the partition from inline
5889        /// flag combinations.
5890        enum CodePathFileType {
5891            /// `:exe` — nix-build derivation output, no terminating-
5892            /// extension contract (the canonical `"exe/<name>"`
5893            /// fixtures the layout's `ExeOutsideDir` error message
5894            /// documents carry no extension by convention).
5895            None,
5896            /// `:bibliotecas` — tatara-lisp source files the
5897            /// `feira build` loop reads through `tatara_lisp::read`
5898            /// at parse time. Routes to [`is_lisp_extension`].
5899            LispSource,
5900            /// `:servicos` — ComputeUnit-CR YAML files the
5901            /// caixa-helm / caixa-flux renderers consume through
5902            /// `serde_yaml::from_str`. Routes to
5903            /// [`is_computeunit_yaml_extension`].
5904            ComputeUnitYaml,
5905        }
5906
5907        // The per-slot [`CodePathFileType`] selects which axes carry the
5908        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
5909        // source axis (the `feira build` loop at
5910        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
5911        // `tatara_lisp::read` at parse time) — the lifted
5912        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
5913        // `:exe` is the nix-built executable surface (per the canonical
5914        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
5915        // error message documents and every in-tree
5916        // `caixa_with_code_paths` positive control uses) — its file-type
5917        // contract is "nix-build derivation output", not a typed source
5918        // file, so [`CodePathFileType::None`] opts the slot out of any
5919        // file-type gate. `:servicos` is the `.computeunit.yaml`
5920        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
5921        // renderers consume each entry through `serde_yaml::from_str` as
5922        // a typed `ComputeUnit` CR) — the lifted
5923        // [`is_computeunit_yaml_extension`] predicate gates the compound
5924        // `.computeunit.yaml` suffix. All three axes are surfaced through
5925        // the same iteration so the sandbox-shape + duplicate gates
5926        // apply uniformly; the typed file-type dispatch fires per-slot
5927        // exactly where the downstream consumer's accepted set demands
5928        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
5929        // compounding lift on the peer 64772a9 `:bibliotecas`
5930        // `.lisp`-gate trajectory — the second of the three code-path
5931        // axes to land on a typed compound-suffix gate, with the same
5932        // self-locating per-slot diagnostic shape every peer per-axis
5933        // file-type lift uses (`*NonLispExtension { slot, path }` /
5934        // `*NonComputeUnitYamlExtension { slot, path }`).
5935        for (slot, list, file_type) in [
5936            (
5937                ":bibliotecas",
5938                &self.bibliotecas,
5939                CodePathFileType::LispSource,
5940            ),
5941            (":exe", &self.exe, CodePathFileType::None),
5942            (
5943                ":servicos",
5944                &self.servicos,
5945                CodePathFileType::ComputeUnitYaml,
5946            ),
5947        ] {
5948            // Per-slot set-not-multiset gate on the typed code-path axis.
5949            // Every peer Vec-shaped author-supplied list past validate is
5950            // a set, not a multiset: `:membros :caixa`
5951            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
5952            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
5953            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
5954            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
5955            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
5956            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
5957            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
5958            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
5959            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
5960            // the three code-path lists are the last Vec-shaped author-
5961            // supplied slots on the typed Caixa surface still admitting a
5962            // duplicate entry silently. Scope is per-list (`:bibliotecas`
5963            // duplicates are flagged within `:bibliotecas`, not across
5964            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
5965            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
5966            // legitimate dev-vs-runtime shape on the dep axis, fenced
5967            // separately by [`crate::dep::validate_no_self_dep`]). On the
5968            // code-path axis a cross-slot collision is structurally
5969            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
5970            // fence — `:exe` and `:servicos` entries are confined to their
5971            // own directory trees, so the only way a string could appear
5972            // on two code-path lists is the (rare, structurally invalid)
5973            // case where `:bibliotecas` carries an `"exe/<x>"` or
5974            // `"servicos/<x>.yaml"`-shaped path.
5975            //
5976            // Without the gate three authoring footguns silently passed:
5977            //
5978            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
5979            //     canonical copy-paste-the-wrong-file footgun. `feira
5980            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
5981            //     list and re-parses the same file twice, wasting work
5982            //     and silently masking the author's intent to declare a
5983            //     *second* biblioteca.
5984            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
5985            //     Binario surface. The future `caixa-flake` `nix flake`
5986            //     emitter that materializes each `:exe` entry as a flake
5987            //     `packages.<exe-name>` derivation would collide on the
5988            //     duplicate package name and surface a flake-eval error
5989            //     far from the source `caixa.lisp`.
5990            //   - `:servicos ("servicos/x.computeunit.yaml"
5991            //     "servicos/x.computeunit.yaml")` — the same footgun on
5992            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
5993            //     renderers already refuse `:servicos.len() != 1` with
5994            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
5995            //     that diagnostic surfaces "too many servicos" without
5996            //     naming "duplicate entry" — the typed self-locating
5997            //     "which entry is the duplicate" framing only lands at
5998            //     this gate.
5999            //
6000            // Same `seen.insert(entry.as_str())` shape every peer per-list
6001            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
6002            // 86c769b, `:deps` 359fba5) and the same "structural shape
6003            // checks fire before the duplicate check on the same entry"
6004            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
6005            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
6006            // empty entry first, not the duplicate on the later pair).
6007            let mut seen = std::collections::HashSet::new();
6008            for entry in list {
6009                let path = Path::new(entry);
6010                match is_sandboxed_relative_path(path) {
6011                    Ok(()) => {}
6012                    Err(PathShapeViolation::Empty) => {
6013                        return Err(ManifestError::CodePathEmpty { slot });
6014                    }
6015                    Err(PathShapeViolation::Absolute) => {
6016                        return Err(ManifestError::code_path_absolute(slot, path));
6017                    }
6018                    Err(PathShapeViolation::ParentEscape) => {
6019                        return Err(ManifestError::code_path_parent_escape(slot, path));
6020                    }
6021                }
6022                // The per-slot file-type gate dispatched through the
6023                // typed [`CodePathFileType`] selector above. Each variant
6024                // routes to the lifted predicate the downstream consumer
6025                // demands:
6026                //
6027                //   - [`LispSource`] → [`is_lisp_extension`] for
6028                //     `:bibliotecas` (the `feira build` loop's
6029                //     `tatara_lisp::read` consumer);
6030                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
6031                //     for `:servicos` (the caixa-helm / caixa-flux
6032                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
6033                //     accepted set);
6034                //   - [`None`] for `:exe` — the nix-build derivation-
6035                //     output axis has no terminating-extension contract.
6036                //
6037                // Fires after the sandbox-shape arms so a path that is
6038                // *both* sandbox-escaping and wrong-extension surfaces
6039                // the more fundamental sandbox-shape diagnostic first
6040                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
6041                // `ParentEscape` → `NonLispExtension` arm-ordering on
6042                // `:behavior :on-*` c97815a, and `EmptyScript` →
6043                // `AbsoluteScript` → `ParentEscapeScript` →
6044                // `NonLispExtensionScript` on
6045                // `:upgrade-from :state-change :script` 33cc830), and
6046                // before the duplicate gate so the narrower per-entry
6047                // file-type shape dominates the cross-entry uniqueness
6048                // diagnostic (a
6049                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
6050                // `:servicos` surfaces
6051                // `CodePathNonComputeUnitYamlExtension` on the first
6052                // entry rather than `CodePathDuplicate` on the pair —
6053                // peer with the 64772a9 `:bibliotecas`
6054                // `("lib/x.txt" "lib/x.txt")` ordering).
6055                match file_type {
6056                    CodePathFileType::None => {}
6057                    CodePathFileType::LispSource => {
6058                        if !is_lisp_extension(path) {
6059                            return Err(ManifestError::code_path_non_lisp_extension(slot, path));
6060                        }
6061                    }
6062                    CodePathFileType::ComputeUnitYaml => {
6063                        if !is_computeunit_yaml_extension(path) {
6064                            return Err(ManifestError::code_path_non_computeunit_yaml_extension(
6065                                slot, path,
6066                            ));
6067                        }
6068                    }
6069                }
6070                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
6071                    ManifestError::code_path_duplicate(slot, path)
6072                })?;
6073            }
6074        }
6075        Ok(())
6076    }
6077
6078    /// Reject `:etiquetas` lists with an empty entry or with two entries
6079    /// agreeing on the same string. `:etiquetas` is the universal
6080    /// registry-search-tag axis on [`Caixa`] (every kind carries the
6081    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6082    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
6083    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
6084    /// a [`std::collections::BTreeSet`] alongside the four substrate-
6085    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
6086    /// Two authoring footguns silently passed validate without this gate:
6087    ///
6088    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
6089    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
6090    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
6091    ///     `chart.metadata.keywords` admits the value without a strict
6092    ///     parser-side gate, but the empty keyword has no operational
6093    ///     meaning — it indexes nothing in the future caixa-registry
6094    ///     search axis and clutters the rendered chart with a no-op tag.
6095    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
6096    ///     copy-paste-the-wrong-tag footgun) silently passed validate
6097    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
6098    ///     at chart render — a "second wins / one silently disappears"
6099    ///     shape divergent from every peer typed-graph set gate
6100    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
6101    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
6102    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6103    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6104    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6105    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
6106    ///     on `:upgrade-from`, the per-instruction-class singularity
6107    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
6108    ///     [`crate::UpgradeError::DuplicateStateChange`] /
6109    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
6110    ///     discipline is uniform: every Vec-shaped author-supplied list
6111    ///     past validate is set-not-multiset, by construction.
6112    ///
6113    /// Past the empty arm the gate enforces the chart-keyword shape
6114    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
6115    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
6116    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
6117    /// continuation. Closes the canonical paste-from-doc footguns the
6118    /// bare empty + duplicate arms left open: paste-from-aligned-doc
6119    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
6120    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
6121    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
6122    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
6123    /// — the author meant three separate list entries), path-separator
6124    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
6125    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
6126    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
6127    /// control bytes that would silently land as malformed search tags
6128    /// in the rendered Chart.yaml `keywords:` array and break the
6129    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
6130    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
6131    /// established on the sibling universal-axis `Vec<String>` surface
6132    /// — the second universal-axis Vec<String> surface to land the
6133    /// empty-first-then-shape-then-duplicate per-entry cascade.
6134    ///
6135    /// Same empty-first cascade discipline every peer per-axis gate
6136    /// uses: the per-entry empty arm fires before the per-entry shape
6137    /// arm fires before the cross-entry duplicate arm, so an
6138    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
6139    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
6140    /// has no value" defect) before either the shape or the duplicate
6141    /// diagnostic. Walks the list in declaration order so the
6142    /// first-collision diagnostic surfaces the lexicographically-
6143    /// earliest offending position, peer with every other duplicate
6144    /// gate on this surface.
6145    ///
6146    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
6147    /// caixa-build gate alongside the peer universal gates
6148    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6149    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
6150    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6151    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6152    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6153    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6154    /// slot sets. The future caixa-registry search axis can reach for
6155    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
6156    /// chart-keyword-shaped string without re-deriving the precondition.
6157    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
6158        let mut seen = std::collections::HashSet::new();
6159        for etiqueta in self.etiquetas() {
6160            if etiqueta.is_empty() {
6161                return Err(ManifestError::EtiquetaEmpty);
6162            }
6163            crate::render::is_chart_keyword_shape(etiqueta)
6164                .map_err(|reason| ManifestError::etiqueta_invalid(etiqueta, reason))?;
6165            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
6166                ManifestError::etiqueta_duplicate(etiqueta)
6167            })?;
6168        }
6169        Ok(())
6170    }
6171
6172    /// Reject `:autores` lists with an empty entry or with two entries
6173    /// agreeing on the same string. `:autores` is the universal
6174    /// maintainer-axis on [`Caixa`] (every kind carries the
6175    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6176    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
6177    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
6178    /// to a `Maintainer { name, email: None }` without dedup). Two
6179    /// authoring footguns silently passed validate without this gate:
6180    ///
6181    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
6182    ///     blank-doc footgun) rendered as
6183    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
6184    ///     empty maintainer name has no operational meaning — it
6185    ///     identifies no one in the substrate's authorship index and
6186    ///     clutters the rendered chart with a no-op maintainer.
6187    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
6188    ///     the copy-paste-the-wrong-author footgun) silently passed
6189    ///     validate and rendered as two identical maintainer entries.
6190    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
6191    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
6192    ///     rendered `keywords:` array at chart-render time), the
6193    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
6194    ///     entries stack verbatim in the chart, divergent from every
6195    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
6196    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
6197    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6198    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6199    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6200    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
6201    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
6202    ///     `:etiquetas`).
6203    ///
6204    /// Past the empty arm the gate enforces the chart-maintainer-name
6205    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
6206    /// the structural single-line printable-UTF-8 floor every realistic
6207    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
6208    /// or trailing whitespace, no ASCII control characters anywhere,
6209    /// Unicode bytes accepted. Closes the canonical paste-from-doc
6210    /// footguns the bare empty + duplicate arms left open:
6211    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
6212    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
6213    /// pasted a multi-line block of author records into one `:autores`
6214    /// entry instead of splitting into one entry per author),
6215    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
6216    /// and the paste-from-binary-blob control bytes that would silently
6217    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
6218    /// `maintainers:` array. Mirrors the shape-predicate cascade
6219    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6220    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
6221    /// establish past their own empty arms on the sibling universal-axis
6222    /// `Option<String>` surfaces — the first universal-axis Vec<String>
6223    /// surface to land the empty-first-then-shape-then-duplicate per-entry
6224    /// cascade.
6225    ///
6226    /// Same empty-first cascade discipline every peer per-axis gate
6227    /// uses: the per-entry empty arm fires before the per-entry shape
6228    /// arm before the cross-entry duplicate arm. Walks the list in
6229    /// declaration order so the first-collision diagnostic surfaces the
6230    /// lexicographically-earliest offending position, peer with every
6231    /// other duplicate gate on this surface.
6232    ///
6233    /// Universal-axis (every kind carries `:autores`), so wired at the
6234    /// caixa-build gate alongside the peer universal gates
6235    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6236    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6237    /// [`Self::validate_code_paths`] — before the kind-coherence gates
6238    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6239    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6240    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6241    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6242    /// slot sets.
6243    pub fn validate_autores(&self) -> Result<(), ManifestError> {
6244        let mut seen = std::collections::HashSet::new();
6245        for autor in self.autores() {
6246            if autor.is_empty() {
6247                return Err(ManifestError::AutorEmpty);
6248            }
6249            crate::render::is_chart_maintainer_name_shape(autor)
6250                .map_err(|reason| ManifestError::autor_invalid(autor, reason))?;
6251            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
6252                ManifestError::autor_duplicate(autor)
6253            })?;
6254        }
6255        Ok(())
6256    }
6257
6258    /// Reject `:repositorio` values whose shape the shared
6259    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
6260    /// `repositorio: Option<String>` slot on [`Caixa`] is the
6261    /// universal git-shaped homepage axis every kind carries — the
6262    /// substrate routes the same string through two load-bearing
6263    /// consumers:
6264    ///
6265    ///   - [`caixa-helm`] folds it verbatim into the rendered
6266    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
6267    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
6268    ///     the chart `README.md` `repo = …` interpolation
6269    ///     (`caixa-helm/src/lib.rs:359`).
6270    ///   - [`caixa-flux`] folds it verbatim into the standalone
6271    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
6272    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
6273    ///     `GitRepository.spec.url` the cluster's source-controller
6274    ///     polls — the load-bearing deploy-time axis.
6275    ///
6276    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6277    /// substitute a placeholder when the slot is absent (`None` → the
6278    /// fallback fires); a `Some("")` *skips the fallback* and silently
6279    /// passes the empty string through to `Chart.yaml home: ""` /
6280    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
6281    /// controller both reject the empty URL far from the source
6282    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
6283    /// Similarly a malformed `:repositorio` (whitespace, control char,
6284    /// missing `:` separator, leading `-`) silently lands in the
6285    /// rendered artifacts and breaks at `git clone` / `helm template`
6286    /// / `flux reconcile` time.
6287    ///
6288    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
6289    /// same shared predicate the peer [`crate::DepSource::validate`]
6290    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
6291    /// gate the two `git URL`-shaped surfaces on the typed Caixa
6292    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
6293    /// structurally equivalent: every value past validate is
6294    /// guaranteed-acceptable by the predicate's union of constraints
6295    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
6296    /// control chars, ASCII only, no leading `:`, contains a `:`
6297    /// separator). The predicate accepts every documented authoring
6298    /// shape — `github:org/repo` shorthand, `https://host/path`,
6299    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
6300    /// scp-style SSH, `file:///path` — and refuses the canonical
6301    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
6302    /// injection footguns at validate time. Maps the predicate's
6303    /// `String` reason verbatim into the
6304    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
6305    /// offending value + parser-shaped reason so the diagnostic is
6306    /// self-locating (the author can grep their `caixa.lisp` for
6307    /// `:repositorio "<value>"` and fix it in one edit).
6308    ///
6309    /// `None` (the canonical "omit the slot to express no published
6310    /// homepage" shape) is accepted trivially — the gate is a no-op
6311    /// when the author didn't declare a value. `Some("")` is gated by
6312    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
6313    /// shape predicate is consulted, mirroring the empty-first cascade
6314    /// every peer per-axis identity gate uses
6315    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
6316    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
6317    /// [`crate::DepError::FonteRepoEmpty`] →
6318    /// [`crate::DepError::FonteRepoInvalid`]).
6319    ///
6320    /// Universal-axis (every kind carries `:repositorio`), so wired at
6321    /// the caixa-build gate alongside the peer universal gates
6322    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6323    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6324    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
6325    /// before the kind-coherence gates
6326    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6327    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6328    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6329    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6330    /// specific slot sets.
6331    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
6332        let Some(s) = self.repositorio() else {
6333            return Ok(());
6334        };
6335        if s.is_empty() {
6336            return Err(ManifestError::RepositorioEmpty);
6337        }
6338        is_git_repo_url(s).map_err(|reason| ManifestError::repositorio_invalid(s, reason))
6339    }
6340
6341    /// Reject `:descricao` values that are the empty string. The flat
6342    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
6343    /// free-form-prose homepage axis every kind carries — the
6344    /// substrate routes the same string through two load-bearing
6345    /// consumers in the [`caixa-helm`] renderer:
6346    ///
6347    ///   - `build_chart_yaml` folds it verbatim into the rendered
6348    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
6349    ///     field (`caixa-helm/src/lib.rs:232-235`).
6350    ///   - `build_readme` folds it verbatim into the rendered chart
6351    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
6352    ///
6353    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6354    /// substitute a `caixa.nome`-derived placeholder when the slot is
6355    /// absent (`None` → the fallback fires); a `Some("")` *skips the
6356    /// fallback* and silently passes the empty string through to
6357    /// `Chart.yaml description: ""` / a blank chart `README.md`
6358    /// header. Helm's chart spec requires a non-empty `description:`
6359    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
6360    /// `WARNING [chart.metadata.description]: description is required`),
6361    /// so the empty `Some("")` silently lands in the rendered
6362    /// artifacts and breaks at `helm lint` / `helm install` time far
6363    /// from the source `caixa.lisp`, with no field naming the
6364    /// offending `:descricao`.
6365    ///
6366    /// `None` (the canonical "omit the slot to defer to the renderer's
6367    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
6368    /// the gate is a no-op when the author didn't declare a value.
6369    /// `Some("")` is gated by the narrower
6370    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
6371    /// shape every peer per-axis empty gate uses
6372    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6373    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6374    /// [`ManifestError::RepositorioEmpty`]).
6375    ///
6376    /// Universal-axis (every kind carries `:descricao`), so wired at
6377    /// the caixa-build gate alongside the peer universal gates
6378    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6379    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6380    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6381    /// [`Self::validate_code_paths`] — before the kind-coherence
6382    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6383    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6384    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6385    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6386    /// specific slot sets.
6387    ///
6388    /// Past the empty arm the gate enforces the chart-description
6389    /// shape predicate via [`crate::render::is_chart_description_shape`]:
6390    /// the structural single-line UTF-8 floor every realistic chart
6391    /// description in the wild matches — 1..=512 bytes, no leading
6392    /// or trailing whitespace, no ASCII control characters anywhere
6393    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
6394    /// carriage return, and every other control byte), Unicode
6395    /// continuation bytes accepted (the canonical fixtures carry
6396    /// `→` and `—`). Closes the canonical paste-from-doc footguns
6397    /// the bare empty-arm gate left open: paste-from-aligned-doc
6398    /// leading / trailing whitespace (`" Checkout flow."`,
6399    /// `"Checkout flow. "`), paste-from-multiline-doc newline
6400    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
6401    /// (`"Checkout\rflow."`), tab-from-aligned-doc
6402    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
6403    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
6404    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
6405    /// [`Self::validate_edicao`] establish past their own empty arms
6406    /// on the sibling universal-axis `Option<String>` Caixa-level
6407    /// value-shape surfaces.
6408    ///
6409    /// The empty-first cascade discipline mirrors every peer per-axis
6410    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
6411    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
6412    /// diagnostic surfaces on `Some("")` rather than the broader
6413    /// shape-predicate diagnostic — peer with how
6414    /// [`ManifestError::LicencaEmpty`] runs before
6415    /// [`ManifestError::LicencaInvalid`],
6416    /// [`ManifestError::EdicaoEmpty`] runs before
6417    /// [`ManifestError::EdicaoInvalid`],
6418    /// [`ManifestError::RepositorioEmpty`] runs before
6419    /// [`ManifestError::RepositorioInvalid`].
6420    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
6421        let Some(s) = self.descricao() else {
6422            return Ok(());
6423        };
6424        if s.is_empty() {
6425            return Err(ManifestError::DescricaoEmpty);
6426        }
6427        crate::render::is_chart_description_shape(s)
6428            .map_err(|reason| ManifestError::descricao_invalid(s, reason))?;
6429        Ok(())
6430    }
6431
6432    /// Reject `:licenca` values that are the empty string. The flat
6433    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
6434    /// SPDX-shaped license-expression axis every kind carries — the
6435    /// substrate routes the same string through the [`caixa-helm`]
6436    /// renderer's `build_readme` which folds it verbatim into the
6437    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
6438    /// section (`caixa-helm/src/lib.rs:361`) via
6439    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
6440    /// fallback only fires on `None`; a `Some("")` *skips the
6441    /// fallback* and silently passes the empty string through to a
6442    /// chart `README.md` whose `License` section renders as the bare
6443    /// trailing period (`.\n`) — peer footgun with the
6444    /// `Some("")`-skips-`unwrap_or_else` shape the
6445    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
6446    /// gates close on the sibling free-form-prose and git-URL axes.
6447    ///
6448    /// `None` (the canonical "omit the slot to defer to the
6449    /// renderer's `MIT` fallback" shape every existing fixture
6450    /// carries) is accepted trivially — the gate is a no-op when the
6451    /// author didn't declare a value. `Some("")` is gated by the
6452    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
6453    /// empty-arm shape every peer per-axis empty gate uses
6454    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6455    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6456    /// [`ManifestError::RepositorioEmpty`],
6457    /// [`ManifestError::DescricaoEmpty`]).
6458    ///
6459    /// Universal-axis (every kind carries `:licenca`), so wired at
6460    /// the caixa-build gate alongside the peer universal gates
6461    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6462    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6463    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6464    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
6465    /// — before the kind-coherence gates
6466    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6467    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6468    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6469    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6470    /// specific slot sets.
6471    ///
6472    /// Past the empty arm the gate enforces the SPDX-expression shape
6473    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
6474    /// structural alphabet floor every realistic SPDX expression in
6475    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
6476    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
6477    /// single ASCII space (token separator). Closes the canonical
6478    /// paste-from-doc footguns the bare empty-arm gate left open:
6479    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
6480    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
6481    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
6482    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
6483    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
6484    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
6485    /// Apache-2.0"`), and semicolon-list-separator confusion
6486    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
6487    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
6488    /// establish past their own empty arms.
6489    ///
6490    /// The empty-first cascade discipline mirrors every peer per-axis
6491    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
6492    /// [`ManifestError::LicencaInvalid`], so the narrower empty
6493    /// diagnostic surfaces on `Some("")` rather than the broader
6494    /// shape-predicate diagnostic — peer with how
6495    /// [`ManifestError::EdicaoEmpty`] runs before
6496    /// [`ManifestError::EdicaoInvalid`],
6497    /// [`ManifestError::RepositorioEmpty`] runs before
6498    /// [`ManifestError::RepositorioInvalid`].
6499    ///
6500    /// A future tightening on this axis can extend the alphabet
6501    /// floor into a full SPDX expression parser + license-id
6502    /// allowlist (rejecting alphabet-valid values that don't name a
6503    /// real SPDX license identifier — e.g., `"NotAReal"` is
6504    /// alphabet-valid but no `NotAReal` license-id exists). That
6505    /// parser only becomes meaningful past a real SPDX-spec
6506    /// dependency; this gate establishes the structural floor by
6507    /// refusing every non-SPDX-alphabet value at validate time.
6508    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
6509        let Some(s) = self.licenca() else {
6510            return Ok(());
6511        };
6512        if s.is_empty() {
6513            return Err(ManifestError::LicencaEmpty);
6514        }
6515        crate::render::is_spdx_expression_shape(s)
6516            .map_err(|reason| ManifestError::licenca_invalid(s, reason))?;
6517        Ok(())
6518    }
6519
6520    /// Reject `:edicao` values that are the empty string. The flat
6521    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
6522    /// language-edition axis every kind carries — it determines the
6523    /// tatara-lisp macro surface + compatibility flags the substrate
6524    /// applies when building a caixa, and lands verbatim in the
6525    /// `Caixa::template` author-time scaffold (the canonical
6526    /// `:edicao "2026"` line every `feira init` emits via
6527    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
6528    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
6529    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
6530    /// `caixa-core/src/render.rs:2510`) via
6531    /// `edicao: Some("2026".into())`.
6532    ///
6533    /// `None` (the canonical "omit the slot to defer to the
6534    /// substrate's default edition" shape every existing
6535    /// [`caixa-resolver`] integration test fixture carries via
6536    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6537    /// is accepted trivially — the gate is a no-op when the author
6538    /// didn't declare a value. `Some("")` is gated by the narrower
6539    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
6540    /// shape every peer per-axis empty gate uses
6541    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6542    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6543    /// [`ManifestError::RepositorioEmpty`],
6544    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
6545    ///
6546    /// Universal-axis (every kind carries `:edicao`), so wired at
6547    /// the caixa-build gate alongside the peer universal gates
6548    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6549    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6550    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6551    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6552    /// [`Self::validate_code_paths`] — before the kind-coherence
6553    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6554    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6555    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6556    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6557    /// specific slot sets.
6558    ///
6559    /// Past the empty arm the gate enforces the canonical year-shape
6560    /// predicate: every documented tatara-lisp edition is a 4-digit
6561    /// ASCII decimal year (`"2026"` is the only edition currently
6562    /// minted; future-introduced siblings will follow the same
6563    /// shape, peer with Cargo's `[package] edition` grammar which
6564    /// every value Cargo has ever accepted matches — `"2015"`,
6565    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
6566    /// 4 ASCII decimal bytes is rejected with the narrower
6567    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
6568    /// shape-predicate cascade [`Self::validate_repositorio`]
6569    /// establishes past its own empty arm
6570    /// ([`ManifestError::RepositorioEmpty`] →
6571    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
6572    /// paste-from-doc footguns the bare empty-arm gate left open:
6573    ///
6574    ///   - leading / trailing whitespace from a paste-from-doc
6575    ///     (`"2026 "`, `" 2026"`)
6576    ///   - control characters / CRLF from a paste-from-multiline-doc
6577    ///     (`"2026\n"`)
6578    ///   - non-ASCII look-alikes from a fullwidth keyboard
6579    ///     (`"2026"`) which would silently land as a non-ASCII
6580    ///     string in the rendered caixa.lisp
6581    ///   - free-form non-year values (`"x"`, `"latest"`,
6582    ///     `"nightly"`) that have no operational meaning on the
6583    ///     substrate's build-time edition selector
6584    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
6585    ///     `"r2026"`) — common version-tag idioms that don't apply
6586    ///     to the year-shaped edition axis
6587    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
6588    ///     edition is a year, not a fractional version
6589    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
6590    ///     `"00026"`) that don't name a year
6591    ///
6592    /// `None` (the canonical "omit the slot to defer to the
6593    /// substrate's default edition" shape every existing
6594    /// [`caixa-resolver`] integration test fixture carries via
6595    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6596    /// is accepted trivially — the gate is a no-op when the author
6597    /// didn't declare a value. The empty-first cascade discipline
6598    /// mirrors every peer per-axis identity gate:
6599    /// [`ManifestError::EdicaoEmpty`] runs before
6600    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
6601    /// diagnostic surfaces on `Some("")` rather than the broader
6602    /// shape-predicate diagnostic — peer with how
6603    /// [`ManifestError::NomeEmpty`] runs before
6604    /// [`ManifestError::NomeInvalid`],
6605    /// [`ManifestError::VersaoEmpty`] runs before
6606    /// [`ManifestError::VersaoInvalid`],
6607    /// [`ManifestError::RepositorioEmpty`] runs before
6608    /// [`ManifestError::RepositorioInvalid`].
6609    ///
6610    /// A future tightening on this axis can extend the shape
6611    /// predicate into a known-edition allowlist (rejecting
6612    /// year-shaped values that don't name a tatara-lisp edition
6613    /// the substrate actually understands — e.g., `"1999"` is
6614    /// year-shaped but no `1999` edition exists). That allowlist
6615    /// only becomes meaningful past the introduction of a sibling
6616    /// edition to `"2026"`; this gate establishes the structural
6617    /// floor by refusing every non-year-shaped value at validate
6618    /// time.
6619    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
6620        let Some(s) = self.edicao() else {
6621            return Ok(());
6622        };
6623        if s.is_empty() {
6624            return Err(ManifestError::EdicaoEmpty);
6625        }
6626        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
6627            return Err(ManifestError::edicao_invalid(
6628                s,
6629                "must be a 4-digit ASCII decimal year (canonical \"2026\")",
6630            ));
6631        }
6632        Ok(())
6633    }
6634
6635    /// Compose the supervisor-related flat slots into a single
6636    /// [`SupervisorSpec`] for validation. Returns `None` when the
6637    /// caixa isn't a `:kind Supervisor`.
6638    ///
6639    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
6640    /// simple (one form, no nested `:supervisor (…)` block); this view
6641    /// is the "typed shape" the operator + supervisor reconciler
6642    /// consume.
6643    #[must_use]
6644    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
6645        if !self.kind().is_supervisor() {
6646            return None;
6647        }
6648        // Fold through the shared `supervisor::duration_codec::parse`
6649        // — the same parser the serde-routed `with = "duration_codec"`
6650        // on `SupervisorSpec::restart_window`, the `:politicas
6651        // :timeout` codec, and the `:politicas :circuit-breaker
6652        // :window` codec all consume. The prior inline f64-shaped
6653        // duplicate (`parse_window_inline`) admitted every magnitude
6654        // the integer-magnitude gate (1c55a2a) rejects on the three
6655        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
6656        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
6657        // `None` (i.e. "no reset"), divergent from the shared codec's
6658        // integer-magnitude discipline by construction. The fold
6659        // closes the divergence: every value the typed
6660        // `SupervisorSpec` carries past `supervisor_view` is in the
6661        // shared codec's accepted set. The `.ok()` here preserves the
6662        // existing soft-swallow shape on this view-construction path;
6663        // the new [`Caixa::validate_restart_window`] (sibling of
6664        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
6665        // the offending raw string at build time so authoring tools
6666        // (`feira lint`, the future layout-side wire-up) surface a
6667        // self-locating diagnostic instead of a silently dropped
6668        // window.
6669        let restart_window = self
6670            .restart_window()
6671            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
6672        Some(SupervisorSpec {
6673            // Route the author-omitted `:estrategia` arm through the
6674            // substrate-canonical
6675            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
6676            // `pub const` rather than the transitively-derived
6677            // [`RestartStrategy::default`] route the prior
6678            // `.unwrap_or_default()` fold reached for — one source of
6679            // truth for the Erlang/OTP `one_for_one` half of Learn You
6680            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
6681            // supervisor canonical default that also backs the
6682            // [`crate::supervisor::Default for RestartStrategy`] impl
6683            // and the [`crate::supervisor::Default for SupervisorSpec`]
6684            // impl's struct-literal `estrategia` field, all now routed
6685            // through the same lifted constant. Prior to the lift the
6686            // composition site carried `.unwrap_or_default()` with no
6687            // compile-time link back to the shared OTP-canonical
6688            // default that the peer paired
6689            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
6690            // arm on the sibling `:max-restarts` axis routes through —
6691            // so a future rebrand of the OTP-canonical strategy default
6692            // (a widening to `rest_for_one` once the substrate
6693            // discovers startup-order-coupled child cohorts as the more
6694            // common shape, a per-cluster overlay the operator pins
6695            // through the MESH-COMPOSITION §III.2 supervision-canary
6696            // `:estrategia-overrides` roadmap slot) would have had to
6697            // migrate the paired `MaxIntensity` + `Period` halves
6698            // through the lifted constants and the `one_for_one` half
6699            // through a `RestartStrategy::default()` route in lockstep
6700            // or the three halves of the same OTP-canonical default
6701            // would silently drift out of pairing. Byte-parity against
6702            // the lifted constant closes the split. Pinned by
6703            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
6704            // in the tests module.
6705            estrategia: self
6706                .estrategia()
6707                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
6708            // Route the author-omitted `:max-restarts` arm through the
6709            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
6710            // typed `pub const` rather than the raw `5` literal — one
6711            // source of truth for the Erlang/OTP-canonical
6712            // `{intensity, 5, 60}` `MaxIntensity` default that also
6713            // backs the serde-side wire-format author-omitted arm on
6714            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
6715            // `#[serde(default = "default_max_restarts")]` and the
6716            // [`Default for SupervisorSpec`] impl's struct-literal
6717            // default field. Prior to the lift the composition site
6718            // carried a raw `5` with no compile-time link back to the
6719            // serde-side default, so a future rebrand of the OTP-
6720            // canonical default (a tightening to Elixir's `3`, a
6721            // widening to a per-cluster overlay the operator pins
6722            // through the MESH-COMPOSITION §III.2 supervision-canary
6723            // `:supervisor :max-restarts-overrides` roadmap slot)
6724            // would have had to be threaded through both open-coded
6725            // copies in lockstep or the wire-format author-omitted arm
6726            // and this view-construction author-omitted arm would
6727            // silently disagree on which restart-budget an omitted
6728            // `:max-restarts` resolves to. Pinned by
6729            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
6730            // in the tests module.
6731            max_restarts: self
6732                .max_restarts()
6733                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
6734            restart_window,
6735            children: self.children().to_vec(),
6736        })
6737    }
6738
6739    /// A minimal starter manifest emitted by `feira init`.
6740    #[must_use]
6741    pub fn template(nome: &str) -> String {
6742        format!(
6743            "(defcaixa\n  \
6744               :nome        {nome:?}\n  \
6745               :versao      \"0.1.0\"\n  \
6746               :kind        Biblioteca\n  \
6747               :edicao      \"2026\"\n  \
6748               :descricao   \"FIXME — describe this caixa\"\n  \
6749               :autores     ()\n  \
6750               :etiquetas   ()\n  \
6751               :deps        ()\n  \
6752               :deps-dev    ()\n  \
6753               :bibliotecas (\"lib/{nome}.lisp\"))\n"
6754        )
6755    }
6756
6757    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
6758    /// back after mutation (e.g. `feira add`).
6759    ///
6760    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
6761    /// The derive-macro `compile_from_sexp` path is the inverse, so any
6762    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
6763    #[must_use]
6764    pub fn to_lisp(&self) -> String {
6765        let json = serde_json::to_value(self).expect("Caixa serialize");
6766        let sexp = tatara_lisp::domain::json_to_sexp(&json);
6767        let tatara_lisp::Sexp::List(items) = sexp else {
6768            return format!("(defcaixa {sexp})\n");
6769        };
6770        let mut out = String::from("(defcaixa");
6771        let mut i = 0;
6772        while i + 1 < items.len() {
6773            out.push_str("\n  ");
6774            out.push_str(&items[i].to_string());
6775            out.push(' ');
6776            out.push_str(&items[i + 1].to_string());
6777            i += 2;
6778        }
6779        out.push_str(")\n");
6780        out
6781    }
6782}
6783
6784/// Errors raised by top-level [`Caixa`] validators that don't fit
6785/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
6786/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
6787/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
6788/// through every substrate-side artifact's `metadata.name` /
6789/// version derivation.
6790///
6791/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
6792/// doc-comment anticipates) can hold one of each per-axis error
6793/// family without reshaping individual diagnostics; this enum is
6794/// the first such per-Caixa-identity family.
6795#[derive(Debug, Error, PartialEq, Eq)]
6796pub enum ManifestError {
6797    #[error(
6798        ":nome is empty (every caixa must name itself; the value flows \
6799         into every K8s artifact's `metadata.name` derivation and into \
6800         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
6801    )]
6802    NomeEmpty,
6803    #[error(
6804        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
6805         apiserver enforces this rule on every `metadata.name` the \
6806         caixa's substrate-side renderers derive from `:nome` — the \
6807         `lareira-<nome>` Helm chart name, the programs.yaml entry \
6808         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
6809         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
6810         name; use a lowercase alphanumeric + hyphen identifier like \
6811         `\"checkout\"` or `\"cart-v2\"`)"
6812    )]
6813    NomeInvalid { nome: String, reason: String },
6814    #[error(
6815        ":nome {nome:?} overflows the joint-length budget on the canonical \
6816         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
6817         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
6818         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
6819         `chart:` slot, `caixa-tatara`'s `release_name` + \
6820         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
6821         joint name through the canonical `lareira_chart_name` helper, and \
6822         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
6823         DNS-1123 label cap on every chart-name-derived `metadata.name` \
6824         reject any joint name exceeding 63 bytes; the narrower \
6825         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
6826         arm gates the chart-name budget downstream renderers inherit)"
6827    )]
6828    NomeChartNameBudgetExceeded { nome: String, reason: String },
6829    #[error(
6830        ":versao is empty (every caixa must pin its own version; the value flows \
6831         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
6832         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
6833         `:latest` tags, the lacre closure's `concrete_versao`, and the \
6834         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
6835    )]
6836    VersaoEmpty,
6837    #[error(
6838        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
6839         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
6840         with optional `-prerelease` and `+build` — across every artifact derived \
6841         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
6842         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
6843         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
6844         and the `:upgrade-from :from` peers that match against this exact shape; \
6845         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
6846         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
6847         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
6848    )]
6849    VersaoInvalid { versao: String, reason: String },
6850    #[error(
6851        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
6852         substrate consumes this string through the shared \
6853         `supervisor::duration_codec` — the same parser routed via `with = \
6854         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
6855         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
6856         the canonical authoring form is `<integer><unit>` where the unit is one \
6857         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
6858         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
6859         Without this gate a malformed `:restart-window` silently produced a \
6860         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
6861         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
6862         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
6863         layer with the offending value named verbatim. Omit the slot entirely to \
6864         express \"no reset\"; carry a positive integer duration to express the \
6865         sliding window)"
6866    )]
6867    RestartWindowMalformed {
6868        restart_window: String,
6869        reason: String,
6870    },
6871    #[error(
6872        "{slot} entry is an empty path string — every {slot} entry must name \
6873         a file relative to the caixa root; omit the entry to omit the file \
6874         (the layout checker's `root.join(\"\")` resolves to the caixa root \
6875         itself, so an empty entry silently aliases the project root as a \
6876         declared {slot} file, then fails downstream at parse / existence \
6877         time with a diagnostic that names the root rather than the offending \
6878         entry)"
6879    )]
6880    CodePathEmpty { slot: &'static str },
6881    #[error(
6882        "{slot} entry {} is an absolute path — entries must be relative to \
6883         the caixa root, since `Path::join` replaces the base with an absolute \
6884         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
6885         outside the caixa root sandbox; rewrite the entry as a relative path \
6886         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
6887         `\"servicos/<name>.computeunit.yaml\"`)",
6888        path.display()
6889    )]
6890    CodePathAbsolute { slot: &'static str, path: PathBuf },
6891    #[error(
6892        "{slot} entry {} contains a `..` component — entries must not traverse \
6893         above the caixa root (the layout's `starts_with(<dir>)` fence on \
6894         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
6895         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
6896         has no such fence, so a leading `..` escapes unconditionally if the \
6897         resolved target happens to exist)",
6898        path.display()
6899    )]
6900    CodePathParentEscape { slot: &'static str, path: PathBuf },
6901    #[error(
6902        "{slot} entry {} does not terminate in the `.lisp` extension — every \
6903         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
6904         loop reads through `tatara_lisp::read` at parse time, so any other \
6905         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
6906         structurally a parser error far from the source caixa.lisp, with \
6907         no field naming the offending `:bibliotecas` entry. Pin a relative \
6908         path under the caixa root whose terminating extension is \
6909         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
6910         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
6911         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
6912         (33cc830) axes already carry through the same lifted \
6913         `is_lisp_extension` predicate",
6914        path.display()
6915    )]
6916    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
6917    #[error(
6918        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
6919         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
6920         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
6921         through `serde_yaml::from_str` at chart / FluxCD bundle render \
6922         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
6923         off-by-one-segment `.computeunit-yaml`, the editor-backup \
6924         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
6925         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
6926         source caixa.lisp, with no field naming the offending `:servicos` \
6927         entry. Pin a relative path under the caixa root whose terminating \
6928         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
6929         `\"servicos/<name>.computeunit.yaml\"`, \
6930         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
6931         contract the sibling `:bibliotecas` axis (64772a9) already carries \
6932         on the tatara-lisp-source axis through the peer lifted \
6933         `is_lisp_extension` predicate, here on the compound-suffix axis \
6934         `Path::extension` can't express on its own through the lifted \
6935         `is_computeunit_yaml_extension` predicate",
6936        path.display()
6937    )]
6938    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
6939    #[error(
6940        "{slot} entry {} appears more than once (the code-path list is \
6941         a set, not a multiset; every peer Vec-shaped author-supplied \
6942         list past validate is set-not-multiset — `:membros :caixa`, \
6943         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6944         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
6945         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
6946         code-path lists are the last Vec-shaped author-supplied slots on \
6947         the typed Caixa surface still admitting a duplicate entry. \
6948         `:bibliotecas` duplicates re-parse the same file at \
6949         `feira build` time and silently mask the author's intent to \
6950         declare a *second* biblioteca; `:exe` duplicates collide on the \
6951         flake `packages.<name>` derivation key at the future \
6952         `caixa-flake` materializer; `:servicos` duplicates surface as the \
6953         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
6954         rejection far from the source `caixa.lisp`. Drop the duplicate \
6955         or rename it to the actual second file intended)",
6956        path.display()
6957    )]
6958    CodePathDuplicate { slot: &'static str, path: PathBuf },
6959    #[error(
6960        ":etiquetas entry is empty (every tag must carry a non-empty \
6961         registry-search identifier; the empty entry has no operational \
6962         meaning — it indexes nothing in the future caixa-registry search \
6963         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
6964         with a no-op tag; omit the entry to express \"no tag on this \
6965         position\")"
6966    )]
6967    EtiquetaEmpty,
6968    #[error(
6969        ":etiquetas entry {etiqueta:?} appears more than once (the \
6970         registry-search tag set is a set, not a multiset; duplicate \
6971         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
6972         at chart render — a \"second wins / one silently disappears\" \
6973         shape divergent from every peer typed-graph set gate \
6974         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
6975         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
6976         duplicate or rename it to the actual tag intended)"
6977    )]
6978    EtiquetaDuplicate { etiqueta: String },
6979    #[error(
6980        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
6981         {reason} (the substrate consumes this string through the shared \
6982         `crate::render::is_chart_keyword_shape` predicate — the same \
6983         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
6984         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
6985         continuation. The canonical authoring shapes are short kebab-case \
6986         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
6987         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
6988         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
6989         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
6990         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
6991         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
6992         `\"mesh,http,grpc\"` — the author meant to author three separate \
6993         list entries; path-separator confusion `\"caixa/servico\"`; \
6994         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
6995         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
6996         `\"café\"` — every legitimate search tag is strict ASCII; \
6997         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
6998         passed `from_lisp` + `validate_etiquetas` + \
6999         `StandardLayout::verify` and landed in the rendered \
7000         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
7001         malformed search tag — Artifact Hub's keyword index + the future \
7002         caixa-registry's keyword index would either silently drop the \
7003         tag or fail to index it far from the source caixa.lisp; the gate \
7004         moves the diagnostic to the manifest layer with the offending \
7005         value named verbatim)"
7006    )]
7007    EtiquetaInvalid { etiqueta: String, reason: String },
7008    #[error(
7009        ":autores entry is empty (every maintainer must carry a non-empty \
7010         identifier; the empty entry has no operational meaning — it \
7011         identifies no one in the substrate's authorship index and renders \
7012         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
7013         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
7014         omit the entry to express \"no maintainer on this position\")"
7015    )]
7016    AutorEmpty,
7017    #[error(
7018        ":autores entry {autor:?} appears more than once (the maintainer \
7019         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
7020         `maintainers:` rendering does *no* dedup — duplicate entries \
7021         stack verbatim in `Chart.yaml` as two identical \
7022         `Maintainer {{ name, email: None }}` records, divergent from every \
7023         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
7024         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
7025         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
7026         rename it to the actual author intended)"
7027    )]
7028    AutorDuplicate { autor: String },
7029    #[error(
7030        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
7031         {reason} (the substrate consumes this string through the shared \
7032         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
7033         single-line-UTF-8 floor every realistic chart maintainer name carries: \
7034         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
7035         characters anywhere, Unicode bytes accepted. The canonical authoring \
7036         shapes are short single-line identifiers like `\"pleme-io\"`, \
7037         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
7038         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
7039         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
7040         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
7041         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
7042         records into one entry instead of splitting into one entry per author; \
7043         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
7044         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
7045         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7046         `validate_autores` + `StandardLayout::verify` and landed in the \
7047         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
7048         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
7049         round-trip — every chart-aware UI (`helm list`, `helm search`, \
7050         Artifact Hub maintainer index) would render the maintainer name in a \
7051         single-line column far from the source caixa.lisp; the gate moves the \
7052         diagnostic to the manifest layer with the offending value named \
7053         verbatim)"
7054    )]
7055    AutorInvalid { autor: String, reason: String },
7056    #[error(
7057        ":repositorio is the empty string (every published caixa names its \
7058         git source via a non-empty `:repositorio` locator — the value \
7059         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
7060         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
7061         `GitRepository.spec.url` via `caixa-flux`'s \
7062         `ClusterBundleOpts::for_caixa`; both consumers' \
7063         `Option::unwrap_or_else` fallbacks only fire when the slot is \
7064         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
7065         `url: \"\"` in the rendered artifacts and breaks at `helm \
7066         template` / FluxCD source-controller reconcile time far from the \
7067         source caixa.lisp; omit the slot entirely to defer to the \
7068         renderer's `https://github.com/pleme-io/<nome>` / \
7069         `caixa.nome`-derived fallback, or carry a canonical authoring \
7070         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
7071         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
7072         `\"file:///path\"`)"
7073    )]
7074    RepositorioEmpty,
7075    #[error(
7076        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
7077         (the substrate consumes this string through the shared \
7078         `crate::render::is_git_repo_url` predicate — the same parser the \
7079         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
7080         value through via `DepSource::validate`; the canonical authoring \
7081         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
7082         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
7083         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
7084         scp-style SSH form. Without this gate a malformed `:repositorio` \
7085         (whitespace from a paste-from-doc; control characters / CRLF \
7086         from a paste-from-multiline-doc; a leading `-` from a \
7087         CLI-argument-injection footgun; a missing `:` separator from a \
7088         bare `org/repo` shape git treats as a relative filesystem path) \
7089         silently landed in the rendered `Chart.yaml home:` and the \
7090         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
7091         FluxCD reconcile time far from the source caixa.lisp; the gate \
7092         moves the diagnostic to the manifest layer with the offending \
7093         value named verbatim)"
7094    )]
7095    RepositorioInvalid { repositorio: String, reason: String },
7096    #[error(
7097        ":descricao is the empty string (every published caixa names \
7098         its purpose via a non-empty `:descricao` summary — the value \
7099         flows verbatim into the rendered `lareira-<nome>` Helm \
7100         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
7101         `build_chart_yaml` and into the chart `README.md` header via \
7102         `build_readme`; both consumers' `Option::unwrap_or_else` \
7103         `caixa.nome`-derived fallbacks only fire when the slot is \
7104         `None`, so an empty `Some(\"\")` silently lands as \
7105         `description: \"\"` / a blank `README.md` header in the \
7106         rendered artifacts and breaks at `helm lint` time \
7107         (`WARNING [chart.metadata.description]: description is \
7108         required` on `apiVersion: v2` charts) far from the source \
7109         caixa.lisp; omit the slot entirely to defer to the \
7110         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
7111         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
7112         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
7113         Servico.\"`)"
7114    )]
7115    DescricaoEmpty,
7116    #[error(
7117        ":descricao {descricao:?} is not a valid chart-description shape: \
7118         {reason} (the substrate consumes this string through the shared \
7119         `crate::render::is_chart_description_shape` predicate — the same \
7120         single-line-UTF-8 floor every realistic chart description carries: \
7121         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
7122         characters anywhere, Unicode prose bytes accepted. The canonical \
7123         authoring shapes are short single-line summaries like `\"Canonical \
7124         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
7125         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
7126         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
7127         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
7128         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
7129         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
7130         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
7131         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7132         `validate_descricao` + `StandardLayout::verify` and landed in the \
7133         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
7134         field + `README.md` header paragraph as a YAML-illegal multi-line \
7135         scalar or a silently-trimmed whitespace round-trip — every \
7136         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
7137         render the description in a single-line column far from the source \
7138         caixa.lisp; the gate moves the diagnostic to the manifest layer \
7139         with the offending value named verbatim)"
7140    )]
7141    DescricaoInvalid { descricao: String, reason: String },
7142    #[error(
7143        ":licenca is the empty string (every published caixa names \
7144         its license via a non-empty `:licenca` SPDX expression — the \
7145         value flows verbatim into the rendered `lareira-<nome>` Helm \
7146         chart's `README.md` `## License` section via `caixa-helm`'s \
7147         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
7148         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
7149         only fires when the slot is `None`, so an empty `Some(\"\")` \
7150         silently lands as a bare trailing period in the rendered \
7151         chart `README.md` `License` section far from the source \
7152         caixa.lisp; omit the slot entirely to defer to the \
7153         renderer's `MIT` fallback, or carry a canonical SPDX \
7154         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
7155         `\"Apache-2.0 OR MIT\"`)"
7156    )]
7157    LicencaEmpty,
7158    #[error(
7159        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
7160         (the substrate consumes this string through the shared \
7161         `crate::render::is_spdx_expression_shape` predicate — the same \
7162         alphabet-floor parser every peer per-axis value-shape gate routes \
7163         its value through; the canonical authoring shapes are single \
7164         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
7165         compound expressions like `\"Apache-2.0 OR MIT\"`, \
7166         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
7167         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
7168         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
7169         like `\"LicenseRef-MyLicense\"` / \
7170         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
7171         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
7172         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
7173         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
7174         a smart-quote paste; underscore-instead-of-hyphen typo \
7175         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
7176         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
7177         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
7178         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
7179         `README.md` `## License` section + a future SPDX-aware \
7180         `Chart.yaml license:` emitter would refuse the value at \
7181         `helm lint` time far from the source caixa.lisp; the gate moves \
7182         the diagnostic to the manifest layer with the offending value \
7183         named verbatim)"
7184    )]
7185    LicencaInvalid { licenca: String, reason: String },
7186    #[error(
7187        ":edicao is the empty string (every published caixa names \
7188         its language edition via a non-empty `:edicao` value — the \
7189         edition determines the tatara-lisp macro surface + \
7190         compatibility flags the substrate applies when building \
7191         the caixa; the canonical `Caixa::template` scaffold every \
7192         `feira init` emits carries `:edicao \"2026\"` verbatim and \
7193         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
7194         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
7195         construction, so an empty `Some(\"\")` silently lands as a \
7196         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
7197         a future renderer-side consumer that folds it through \
7198         `Option::unwrap_or_else` will skip the fallback and pass the \
7199         empty edition through to the substrate's build-time edition \
7200         selector far from the source caixa.lisp; omit the slot \
7201         entirely to defer to the substrate's default edition, or \
7202         carry a canonical edition like `\"2026\"`)"
7203    )]
7204    EdicaoEmpty,
7205    #[error(
7206        ":edicao {edicao:?} is not a valid edition: {reason} (every \
7207         documented tatara-lisp edition is a 4-digit ASCII decimal \
7208         year — `\"2026\"` is the only edition currently minted; \
7209         future-introduced siblings will follow the same shape, peer \
7210         with Cargo's `[package] edition` grammar which every value \
7211         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
7212         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
7213         paste-from-doc footguns silently passed: a trailing space \
7214         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
7215         from a paste-from-multiline-doc, a fullwidth-keyboard \
7216         look-alike (`\"2026\"`), a free-form non-year value \
7217         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
7218         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
7219         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
7220         wrong-length numeric value (`\"26\"`, `\"202\"`, \
7221         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
7222         rendered caixa.lisp and broke at the substrate's \
7223         build-time edition selector far from the source caixa.lisp; \
7224         omit the slot entirely to defer to the substrate's default \
7225         edition, or carry a canonical 4-digit ASCII decimal year \
7226         like `\"2026\"`)"
7227    )]
7228    EdicaoInvalid { edicao: String, reason: String },
7229}
7230
7231// Fold the five `Err(ManifestError::CodePath{Absolute,ParentEscape,
7232// NonLispExtension,NonComputeUnitYamlExtension,Duplicate} { slot,
7233// path: path.to_path_buf() })` four-line struct-variant wire-up sites at
7234// [`Caixa::validate_code_path_lists`]'s per-slot per-entry cascade onto
7235// one substrate-primitive family on the `ManifestError` envelope — the
7236// five open-coded ctor sites remaining on the `:bibliotecas` / `:exe` /
7237// `:servicos` code-path-list value-shape trajectory this envelope carries,
7238// and the family sibling of the peer [`crate::behavior::behavior_slot_path_ctors!`]
7239// (67c31ec) two-slot `{ slot: &'static str, path: PathBuf }` envelope on
7240// the [`crate::BehaviorError`] surface that keys off the exact same
7241// `(slot: &'static str, path: &Path)` argument tuple.
7242//
7243// The five wire-up sites this fold closes are the sandbox-shape
7244// absolute-path arm (`return Err(ManifestError::CodePathAbsolute { slot,
7245// path: path.to_path_buf() })` on the [`is_sandboxed_relative_path`]
7246// `PathShapeViolation::Absolute` branch), the sandbox-shape
7247// parent-escape arm (`return Err(ManifestError::CodePathParentEscape {
7248// slot, path: path.to_path_buf() })` on the sibling
7249// `PathShapeViolation::ParentEscape` branch), the LispSource
7250// terminating-extension arm (`return Err(ManifestError::CodePathNonLispExtension {
7251// slot, path: path.to_path_buf() })` on the `!is_lisp_extension(path)`
7252// branch of the `:bibliotecas` file-type gate), the ComputeUnitYaml
7253// compound-suffix arm (`return Err(ManifestError::CodePathNonComputeUnitYamlExtension
7254// { slot, path: path.to_path_buf() })` on the
7255// `!is_computeunit_yaml_extension(path)` branch of the `:servicos`
7256// file-type gate), and the cross-entry duplicate arm
7257// (`ManifestError::CodePathDuplicate { slot, path: path.to_path_buf() }`
7258// inside the closure passed to [`crate::render::insert_first_seen`]) —
7259// each opened the identical `ManifestError::CodePath* { slot,
7260// path: path.to_path_buf() }` four-line struct-literal against the same
7261// `(slot: &'static str, path: &Path)` local tuple, the exact "same
7262// block re-inlined at every consumer" shape the PRIME DIRECTIVE names
7263// as a bug. The variant discriminator is the only thing that varies
7264// between the five sites; the rest of the struct-literal is a
7265// byte-for-byte re-inline.
7266//
7267// The macro below generates one `#[must_use]` inherent constructor per
7268// variant of shape `fn <ctor>(slot: &'static str, path: &std::path::Path)
7269// -> Self`, so every wire-up site collapses onto one dispatch:
7270// `ManifestError::<ctor>(slot, path)`, byte-equal to the pre-lift
7271// struct-literal on the same `(&'static str, &Path)` fixture. The
7272// uniform two-field construction (`slot` verbatim as `&'static str`,
7273// `path.to_path_buf()`) is spelled once — inside the macro — rather
7274// than at every wire-up site. The `slot` parameter stays `&'static str`
7275// (not `&str`) so every arm continues to carry a program-lifetime
7276// `:bibliotecas` / `:exe` / `:servicos` author-key label — one of the
7277// three `&'static str` literals threaded through the outer per-slot
7278// iterator at [`Caixa::validate_code_path_lists`] — matching the
7279// enum-field type. A runtime-borrowed `&str` would silently downgrade
7280// the label lifetime and let a caller stash a non-`'static` borrow into
7281// the returned error. The `&Path` parameter accepts both
7282// `&Path` and `&PathBuf` (via Deref coercion), so every existing
7283// wire-up — each already binds `let path = Path::new(entry);` from the
7284// per-entry loop — threads through the ctor without a pre-conversion.
7285//
7286// Every future consumer that wants to construct one of these five
7287// variants outside the five in-crate wire-up sites (a deferred
7288// `feira validate --code-paths` per-caixa admission verb re-checking
7289// each declared `:bibliotecas` / `:exe` / `:servicos` entry against the
7290// same sandbox-shape + file-type + duplicate cascade, a future
7291// caixa-registry per-lacre code-path re-validator at lacre-resolve
7292// time, a per-`Caixa` overlay resolver rejecting an author-supplied
7293// code-path against a cluster-local snapshot) now reaches each variant
7294// through one call rather than re-inlining the four-line struct-literal
7295// in lockstep with the five in-crate wire-up sites.
7296macro_rules! manifest_code_path_slot_path_ctors {
7297    ($($ctor:ident => $variant:ident),* $(,)?) => {
7298        impl ManifestError {
7299            $(
7300                #[doc = concat!(
7301                    "Construct a [`ManifestError::",
7302                    stringify!($variant),
7303                    "`] naming the offending `:bibliotecas` / `:exe` / ",
7304                    "`:servicos` code-path list `slot` label and the ",
7305                    "offending entry `path`. Folds the uniform `Self::",
7306                    stringify!($variant),
7307                    " { slot, path: path.to_path_buf() }` two-field ",
7308                    "struct-literal onto one substrate primitive so ",
7309                    "every wire-up on this variant at ",
7310                    "[`Caixa::validate_code_path_lists`] reads through ",
7311                    "one dispatch rather than the pre-lift four-line ",
7312                    "open-coded block. The `slot` label threads verbatim ",
7313                    "from the outer per-slot iterator (one of the three ",
7314                    "code-path author-key `&'static str` consts) and the ",
7315                    "`path` from the per-entry inner iterator's ",
7316                    "`Path::new(entry)` binding."
7317                )]
7318                #[must_use]
7319                pub fn $ctor(slot: &'static str, path: &std::path::Path) -> Self {
7320                    Self::$variant {
7321                        slot,
7322                        path: path.to_path_buf(),
7323                    }
7324                }
7325            )*
7326        }
7327    };
7328}
7329
7330manifest_code_path_slot_path_ctors! {
7331    code_path_absolute => CodePathAbsolute,
7332    code_path_parent_escape => CodePathParentEscape,
7333    code_path_non_lisp_extension => CodePathNonLispExtension,
7334    code_path_non_computeunit_yaml_extension => CodePathNonComputeUnitYamlExtension,
7335    code_path_duplicate => CodePathDuplicate,
7336}
7337
7338// Fold the ten `ManifestError::{Nome, NomeChartNameBudgetExceeded, Versao,
7339// Etiqueta, Autor, Repositorio, Descricao, Licenca, Edicao}Invalid +
7340// RestartWindowMalformed
7341// { <field>: <val>.to_string() | <val>.clone(), reason: <expr> }` wire-up
7342// sites at the per-axis [`Caixa::validate_*`] cascade onto one substrate-
7343// primitive family per typed variant — the direct sibling on the
7344// [`ManifestError`] envelope of the peer
7345// [`crate::aplicacao::aplicacao_field_reason_ctors!`] (981060b, 7 variants
7346// on `AplicacaoError` at `MembroCaixaInvalid` / `EntradaParaInvalid` /
7347// `EntradaHostInvalid` / `EntradaPathInvalid` / `PlacementClusterInvalid` /
7348// `PlacementAffinityInvalid` / `ShardKeyInvalid`) on the M3 mesh side, and
7349// of the peer [`crate::dep::dep_nome_axis_reason_ctors!`] (5621f8a,
7350// 3 variants on `DepError` at `VersaoInvalid` / `FonteRepoShape` /
7351// `CaracteristicaInvalid`) on the sibling `:deps` envelope's mirror-
7352// symmetric `{ nome: String, <axis>: String, reason: String }` three-slot
7353// shape (the `nome` axis added at the per-dep-owned altitude). Every one
7354// of the peer four-family `LayoutError` ctor set
7355// ([`crate::layout::layout_violation_ctors!`] 131ca0d — 16 variants on
7356// `{ caixa, issue }`, [`crate::layout::layout_slot_kind_ctors!`] 0419438
7357// — 4 variants on `{ caixa, kind, slots }`,
7358// [`crate::LayoutError::missing_entry`] 1b09f9d — 1 variant on
7359// `{ kind, path }`, [`crate::layout::layout_nome_only_ctors!`] 3fe3dd7 —
7360// 6 variants on `<Variant>(String)`) and the peer three
7361// [`crate::limits::limits_codec_value_*_ctors!`] codec families (81c856c)
7362// each carry the same discipline on their sibling envelopes.
7363//
7364// The ten variants share the identical `{ <field>: String,
7365// reason: String }` two-slot shape:
7366//   - `NomeInvalid { nome, reason }` at [`Caixa::validate_nome`]
7367//     (`|reason| ManifestError::NomeInvalid { nome: nome.to_string(),
7368//     reason }` inside [`crate::render::require_valid_dns_1123_label`]'s
7369//     `on_invalid` bracket-closure slot);
7370//   - `NomeChartNameBudgetExceeded { nome, reason }` at
7371//     [`Caixa::validate_nome_chart_name_budget`]
7372//     (`|reason| ManifestError::NomeChartNameBudgetExceeded { nome:
7373//     nome.to_string(), reason }` after
7374//     [`crate::render::is_lareira_chart_name_shape`] rejects the offending
7375//     `:nome`);
7376//   - `VersaoInvalid { versao, reason }` at [`Caixa::validate_versao`]
7377//     (`|e| ManifestError::VersaoInvalid { versao: versao.to_string(),
7378//     reason: e.to_string() }` after [`semver::Version::parse`] rejects
7379//     the offending `:versao`);
7380//   - `EtiquetaInvalid { etiqueta, reason }` at
7381//     [`Caixa::validate_etiquetas`]
7382//     (`|reason| ManifestError::EtiquetaInvalid { etiqueta:
7383//     etiqueta.clone(), reason }` after
7384//     [`crate::render::is_chart_keyword_shape`] rejects the offending
7385//     `:etiquetas` entry);
7386//   - `AutorInvalid { autor, reason }` at [`Caixa::validate_autores`]
7387//     (`|reason| ManifestError::AutorInvalid { autor: autor.clone(),
7388//     reason }` after [`crate::render::is_chart_maintainer_name_shape`]
7389//     rejects the offending `:autores` entry);
7390//   - `RepositorioInvalid { repositorio, reason }` at
7391//     [`Caixa::validate_repositorio`]
7392//     (`|reason| ManifestError::RepositorioInvalid { repositorio:
7393//     s.to_string(), reason }` after
7394//     [`crate::render::is_git_repo_url`] rejects the offending
7395//     `:repositorio`);
7396//   - `DescricaoInvalid { descricao, reason }` at
7397//     [`Caixa::validate_descricao`]
7398//     (`|reason| ManifestError::DescricaoInvalid { descricao:
7399//     s.to_string(), reason }` after
7400//     [`crate::render::is_chart_description_shape`] rejects the offending
7401//     `:descricao`);
7402//   - `LicencaInvalid { licenca, reason }` at [`Caixa::validate_licenca`]
7403//     (`|reason| ManifestError::LicencaInvalid { licenca: s.to_string(),
7404//     reason }` after [`crate::render::is_spdx_expression_shape`] rejects
7405//     the offending `:licenca`);
7406//   - `EdicaoInvalid { edicao, reason }` at [`Caixa::validate_edicao`]
7407//     (`return Err(ManifestError::EdicaoInvalid { edicao: s.to_string(),
7408//     reason: "must be a 4-digit ASCII decimal year (canonical
7409//     \"2026\")".to_string() })` on the direct year-shape arm);
7410//   - `RestartWindowMalformed { restart_window, reason }` at
7411//     [`Caixa::validate_restart_window`]
7412//     (`|reason| ManifestError::RestartWindowMalformed { restart_window:
7413//     s.to_string(), reason }` after
7414//     [`crate::supervisor::duration_codec::parse`] rejects the offending
7415//     `:restart-window` raw string).
7416//
7417// Each opened the identical four-line
7418// `ManifestError::<Variant> { <field>: <val>.to_string() | .clone(),
7419// reason: <expr> }` struct-literal against the caller-side `<field>: &str`
7420// / `<field>: &String` local — the exact "same block re-inlined at every
7421// consumer" shape the PRIME DIRECTIVE names as a bug, on the same altitude
7422// the peer `aplicacao_field_reason_ctors!` / `dep_nome_axis_reason_ctors!`
7423// / `LayoutError` / `LimitsError` / `BehaviorError` / `UpgradeError`
7424// families each closed on their sibling envelopes.
7425//
7426// The macro below generates one `#[must_use]` inherent constructor per
7427// variant of shape `fn <ctor>(<field>: &str, reason: impl Into<String>)
7428// -> Self`, collapsing every site onto one dispatch per arm:
7429// `ManifestError::<ctor>(<val>, <reason>)`, byte-equal to the pre-lift
7430// struct-literal on the same `(<field>, reason)` pair. The uniform
7431// two-field construction (`<field>: <field>.to_string()`,
7432// `reason: reason.into()`) is spelled once — inside the macro — rather
7433// than at every wire-up site. The `reason: impl Into<String>` bound
7434// accepts owned `String` (the parser-shaped reason every predicate
7435// returns via `Result<(), String>`; the `e.to_string()` result the
7436// `semver::Version::parse` arm passes; the literal `"…".to_string()` the
7437// `EdicaoInvalid` direct arm passes), `&str` literals, and `format!(…)`
7438// outputs verbatim so no wire-up site changes its per-arm diagnostic
7439// shape at the lift, matching the peer
7440// [`crate::aplicacao::aplicacao_field_reason_ctors!`] and
7441// [`crate::dep::dep_nome_axis_reason_ctors!`] bounds on the sibling
7442// two- and three-slot envelopes. The `<field>: &str` parameter accepts
7443// both `&str` (from the [`Caixa::nome`] / [`Caixa::versao`] /
7444// [`Caixa::repositorio`] / [`Caixa::descricao`] / [`Caixa::licenca`] /
7445// [`Caixa::edicao`] accessors) and `&String` (from the
7446// [`Caixa::etiquetas`] / [`Caixa::autores`] slice iterators) via Deref
7447// coercion, so every existing wire-up threads through the ctor without a
7448// pre-conversion. `#[must_use]` fires a compile warning at any wire-up
7449// that mistakenly discards the constructed error rather than routing it
7450// through `return Err(…)` / `.map_err(…)` / a closure return.
7451//
7452// Every future consumer that wants to construct one of these ten
7453// variants outside the current in-crate wire-up sites (a deferred
7454// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-manifest-axis
7455// admission validators re-checking each declared identity / metadata
7456// axis against a cluster-local snapshot, a future `feira validate
7457// --manifest` per-caixa admission verb re-running the same
7458// value-shape gates on demand, a per-lacre overlay resolver rejecting
7459// an author-supplied manifest override against a cluster-local snapshot
7460// the M4 CR materializer projects, a future
7461// `caixa-registry` per-lacre re-validator at lacre-resolve time
7462// re-checking each declared axis against the same predicates) now
7463// reaches each variant through one call rather than re-inlining the
7464// four-line struct-literal in lockstep with the ten in-crate wire-up
7465// sites.
7466macro_rules! manifest_field_reason_ctors {
7467    ($($ctor:ident => $variant:ident { $field:ident }),* $(,)?) => {
7468        impl ManifestError {
7469            $(
7470                #[doc = concat!(
7471                    "Construct a [`ManifestError::",
7472                    stringify!($variant),
7473                    "`] naming the offending `",
7474                    stringify!($field),
7475                    "` under the given `reason`. Folds the uniform ",
7476                    "`Self::",
7477                    stringify!($variant),
7478                    " { ",
7479                    stringify!($field),
7480                    ": ",
7481                    stringify!($field),
7482                    ".to_string(), reason: reason.into() }` two-slot ",
7483                    "construction onto one substrate primitive so every ",
7484                    "wire-up on this variant reads through one dispatch ",
7485                    "rather than the pre-lift four-line struct-literal ",
7486                    "block. `reason` accepts owned `String`, `&str` ",
7487                    "literals, and `format!(…)` outputs through the ",
7488                    "`impl Into<String>` bound; the `",
7489                    stringify!($field),
7490                    ": &str` parameter accepts both `&str` and `&String` ",
7491                    "via Deref coercion."
7492                )]
7493                #[must_use]
7494                pub fn $ctor($field: &str, reason: impl Into<String>) -> Self {
7495                    Self::$variant {
7496                        $field: $field.to_string(),
7497                        reason: reason.into(),
7498                    }
7499                }
7500            )*
7501        }
7502    };
7503}
7504
7505manifest_field_reason_ctors! {
7506    nome_invalid => NomeInvalid { nome },
7507    nome_chart_name_budget_exceeded => NomeChartNameBudgetExceeded { nome },
7508    versao_invalid => VersaoInvalid { versao },
7509    etiqueta_invalid => EtiquetaInvalid { etiqueta },
7510    autor_invalid => AutorInvalid { autor },
7511    repositorio_invalid => RepositorioInvalid { repositorio },
7512    descricao_invalid => DescricaoInvalid { descricao },
7513    licenca_invalid => LicencaInvalid { licenca },
7514    edicao_invalid => EdicaoInvalid { edicao },
7515    restart_window_malformed => RestartWindowMalformed { restart_window },
7516}
7517
7518// Fold the two `ManifestError::{EtiquetaDuplicate, AutorDuplicate}
7519// { <field>: <val>.clone() }` single-`String`-slot wire-up sites at
7520// [`Caixa::validate_etiquetas`] and [`Caixa::validate_autores`] onto one
7521// substrate-primitive family per typed variant — the direct sibling on
7522// the [`ManifestError`] envelope of the peer
7523// [`crate::aplicacao::aplicacao_caixa_only_ctors!`] (d9f6867, 4 variants
7524// on `AplicacaoError` at `ContratoMemberMissing` / `MembroVersaoEmpty` /
7525// `MembroDuplicate` / `MembroIsSelfAplicacao` on the `{ caixa: String }`
7526// shape) and [`crate::aplicacao::aplicacao_path_only_ctors!`] (3ba8de6,
7527// 2 variants on `AplicacaoError` at `EntradaPathNotAbsolute` /
7528// `EntradaPathDuplicate` on the `{ path: String }` shape) on the sibling
7529// M3 mesh `AplicacaoError` envelope, and of the peer
7530// [`crate::supervisor::supervisor_caixa_only_ctors!`] (db09650, 3 variants
7531// on the sibling M2 `SupervisorError` envelope's `{ caixa: String }`
7532// shape), [`crate::dep::dep_nome_only_ctors!`] (792aa92, 5 variants on
7533// `DepError { nome: String }`), and [`crate::upgrade::upgrade_script_only_ctors!`]
7534// (7468ca9, 3 variants on `UpgradeError { script: PathBuf }`) folds on
7535// the sibling envelopes — the last two open-coded single-slot
7536// `{ <field>: String }` struct-literal sites on `ManifestError` fold
7537// onto one substrate primitive per typed variant, matching the
7538// "one substrate primitive per typed variant on the single-slot
7539// `{ <ident>: String }` envelope shape" fold discipline every peer
7540// per-Caixa-identity family already carries.
7541//
7542// Both wire-up sites — one at [`Caixa::validate_etiquetas`]'s per-entry
7543// [`crate::render::insert_first_seen`] dedup closure
7544// (`|| ManifestError::EtiquetaDuplicate { etiqueta: etiqueta.clone() }`
7545// against the per-`:etiquetas` `&String` loop head) and one at
7546// [`Caixa::validate_autores`]'s per-entry [`crate::render::insert_first_seen`]
7547// dedup closure (`|| ManifestError::AutorDuplicate
7548// { autor: autor.clone() }` against the per-`:autores` `&String` loop
7549// head) — opened the identical `ManifestError::<Variant>Duplicate
7550// { <field>: <val>.clone() }` three-line struct-literal against a
7551// caller-side `&String`, the exact "same block re-inlined at every
7552// consumer" shape the PRIME DIRECTIVE names as a bug. The two variants
7553// share one `{ <field>: String }` shape, so the fold routes each wire-up
7554// site through one dispatch per typed variant.
7555//
7556// The macro below generates one `#[must_use]` inherent constructor per
7557// variant of shape `fn <ctor>(<field>: &str) -> ManifestError`, so every
7558// wire-up site collapses onto one dispatch:
7559// `ManifestError::<ctor>(<&str>)`, byte-equal to the pre-lift
7560// struct-literal on the same `&str` fixture. The uniform one-field
7561// construction (`<field>: <field>.to_string()`) is spelled once — inside
7562// the macro — rather than at every wire-up site. The `<field>: &str`
7563// parameter accepts both `&str` and `&String` (via Deref coercion), so
7564// each existing dedup-closure wire-up threading `<val>.as_str()` — or a
7565// bare `&String` head — through the ctor routes through one dispatch
7566// without a pre-conversion, and the `.clone()` the pre-lift wire-up
7567// carried at the closure body folds into the ctor's canonical
7568// `.to_string()` (byte-equal on the same underlying bytes). Every
7569// constructor is `#[must_use]` so a caller who mistakenly discards the
7570// constructed error trips a compile warning at the wire-up site.
7571//
7572// Every future consumer that wants to construct one of these two
7573// variants outside the current in-crate wire-up sites — a deferred
7574// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission webhook
7575// re-checking one added/renamed `:etiquetas` / `:autores` entry against
7576// the same dedup axis, a future `feira validate --etiquetas` /
7577// `--autores` per-caixa admission verb re-running the same per-entry
7578// dedup gate on demand, a per-lacre overlay resolver rejecting an
7579// author-supplied duplicate `:etiquetas` / `:autores` entry against a
7580// cluster-local snapshot the M4 CR materializer projects, a future
7581// `caixa-registry` per-lacre re-validator at lacre-resolve time
7582// re-checking each declared list against the same dedup predicate — now
7583// reaches each variant through one call rather than re-inlining the
7584// three-line struct-literal in lockstep with the two in-crate wire-up
7585// sites.
7586macro_rules! manifest_field_only_ctors {
7587    ($($ctor:ident => $variant:ident { $field:ident }),* $(,)?) => {
7588        impl ManifestError {
7589            $(
7590                #[doc = concat!(
7591                    "Construct a [`ManifestError::",
7592                    stringify!($variant),
7593                    "`] naming the offending `",
7594                    stringify!($field),
7595                    "` entry. Folds the uniform `Self::",
7596                    stringify!($variant),
7597                    " { ",
7598                    stringify!($field),
7599                    ": ",
7600                    stringify!($field),
7601                    ".to_string() }` one-field struct-literal onto one ",
7602                    "substrate primitive so every wire-up on this variant ",
7603                    "reads through one dispatch rather than the pre-lift ",
7604                    "three-line open-coded struct-literal block. The `",
7605                    stringify!($field),
7606                    ": &str` parameter accepts both `&str` and `&String` ",
7607                    "via Deref coercion."
7608                )]
7609                #[must_use]
7610                pub fn $ctor($field: &str) -> Self {
7611                    Self::$variant {
7612                        $field: $field.to_string(),
7613                    }
7614                }
7615            )*
7616        }
7617    };
7618}
7619
7620manifest_field_only_ctors! {
7621    etiqueta_duplicate => EtiquetaDuplicate { etiqueta },
7622    autor_duplicate => AutorDuplicate { autor },
7623}
7624
7625#[cfg(test)]
7626mod tests {
7627    use super::*;
7628
7629    #[test]
7630    fn template_round_trips() {
7631        let src = Caixa::template("demo");
7632        let c = Caixa::from_lisp(&src).expect("template must parse");
7633        assert_eq!(c.nome, "demo");
7634        assert_eq!(c.versao, "0.1.0");
7635        assert_eq!(c.kind, CaixaKind::Biblioteca);
7636        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
7637        assert!(c.deps.is_empty());
7638        assert!(c.deps_dev.is_empty());
7639    }
7640
7641    #[test]
7642    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
7643        // Fail-before-pass-after pin on [`Caixa::nome`] +
7644        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
7645        // accessor projects the top-level manifest's per-`:nome` /
7646        // per-`:versao` [`String`] storage through the `pub const fn`
7647        // [`String::as_str`] (const-stable since Rust 1.87, well within
7648        // the workspace MSRV) — any future accidental downgrade to
7649        // non-`const` fails the corresponding `<name>_via_const_fn`
7650        // wrapper at caixa-core build time with E0015 (`cannot call
7651        // non-const method`), strictly stronger than a runtime
7652        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
7653        // scalar-accessor family pins on the sibling `const`-eval-
7654        // surface passes ([`crate::CaixaVersion::as_str`] at the
7655        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
7656        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
7657        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
7658        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
7659        // axis, [`crate::supervisor::ChildSpec::nome`] /
7660        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
7661        // M2 supervisor-tree axis,
7662        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
7663        // upgrade axis, [`crate::dep::Dep::nome`] /
7664        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
7665        // axis, and the per-`:contratos`
7666        // [`crate::aplicacao::WitContract::source`] /
7667        // [`crate::aplicacao::WitContract::destination`] /
7668        // [`crate::aplicacao::WitContract::world_ref`] trio the
7669        // sibling pin at 279823b already anchors).
7670        const fn nome_via_const_fn(c: &Caixa) -> &str {
7671            c.nome()
7672        }
7673        const fn versao_via_const_fn(c: &Caixa) -> &str {
7674            c.versao()
7675        }
7676        let src = Caixa::template("demo");
7677        let c = Caixa::from_lisp(&src).expect("template must parse");
7678        assert_eq!(nome_via_const_fn(&c), c.nome());
7679        assert_eq!(versao_via_const_fn(&c), c.versao());
7680        assert_eq!(c.nome(), "demo");
7681        assert_eq!(c.versao(), "0.1.0");
7682    }
7683
7684    #[test]
7685    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
7686        // Fail-before-pass-after pin on the five per-`Caixa`
7687        // `Option<String> → Option<&str>` scalar accessors
7688        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
7689        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
7690        // manifest's optional universal-axis surface, plus
7691        // [`Caixa::restart_window`] on the M2 supervisor-tree
7692        // per-`SupervisorSpec` peer raw-window-string projection axis).
7693        // Each accessor destructures the typed slot's `Option<String>`
7694        // storage through the `match &self.<field> { Some(s) =>
7695        // Some(s.as_str()), None => None }` shape — routing through
7696        // [`String::as_str`] (const-stable since Rust 1.87, well within
7697        // the workspace MSRV) rather than the non-const
7698        // [`Option::as_deref`] the pre-lift bodies carried — and any
7699        // future accidental downgrade to non-`const` fails the
7700        // corresponding `<name>_via_const_fn` wrapper at caixa-core
7701        // build time with E0015 (`cannot call non-const method`),
7702        // strictly stronger than a runtime `assert!` and strictly
7703        // stronger than a module-scope `const _: () = assert!(…)` pin
7704        // (which cannot be formed on a `&Caixa` fixture because the
7705        // type's `String` / `Option<String>` carriers rule out
7706        // `const`-context value construction; the `const fn` wrapper
7707        // is the load-bearing shape that side-steps the destructor-in-
7708        // const restriction on the value axis while still pinning the
7709        // `const`-fn posture on the callee — mirror of the sibling
7710        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7711        // pin's discipline verbatim on the peer non-`Option`
7712        // `String → &str` axis at the same struct).
7713        //
7714        // Peer of the sibling per-M2/M3-slot `Option<String> →
7715        // Option<&str>` accessor family pin
7716        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
7717        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
7718        // [`WitContract::subject`] / [`WitContract::slot`] on the
7719        // per-`:contratos` payload-carrier trio,
7720        // [`Placement::shard_key`] / [`Placement::affinity`] on the
7721        // per-`:placement` optional-scalar pair).
7722        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
7723            c.licenca()
7724        }
7725        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
7726            c.repositorio()
7727        }
7728        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
7729            c.descricao()
7730        }
7731        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
7732            c.edicao()
7733        }
7734        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
7735            c.restart_window()
7736        }
7737        // Sweep both the `Some`-carrying arm (author-declared slot,
7738        // the byte-string projection payload) and the `None`-carrying
7739        // arm (author-omitted slot, the default-path projection) on
7740        // every accessor so the `const fn` wrapper family pins each
7741        // axis's canonical two-arm partition through the same const
7742        // dispatch as the runtime path.
7743        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7744        c1.licenca = Some("MIT".to_string());
7745        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
7746        c1.descricao = Some("demo caixa".to_string());
7747        c1.edicao = Some("2024".to_string());
7748        c1.restart_window = Some("60s".to_string());
7749        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
7750        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
7751        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
7752        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
7753        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
7754        assert_eq!(c1.licenca(), Some("MIT"));
7755        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
7756        assert_eq!(c1.descricao(), Some("demo caixa"));
7757        assert_eq!(c1.edicao(), Some("2024"));
7758        assert_eq!(c1.restart_window(), Some("60s"));
7759        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7760        c2.licenca = None;
7761        c2.repositorio = None;
7762        c2.descricao = None;
7763        c2.edicao = None;
7764        c2.restart_window = None;
7765        assert_eq!(licenca_via_const_fn(&c2), None);
7766        assert_eq!(repositorio_via_const_fn(&c2), None);
7767        assert_eq!(descricao_via_const_fn(&c2), None);
7768        assert_eq!(edicao_via_const_fn(&c2), None);
7769        assert_eq!(restart_window_via_const_fn(&c2), None);
7770    }
7771
7772    #[test]
7773    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
7774        // Fail-before-pass-after pin on the two outer-[`Caixa`]
7775        // `Copy`-return accessors — [`Caixa::kind`] on the required
7776        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
7777        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
7778        // axis. Both accessors project a `Copy`-carrier field
7779        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
7780        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
7781        // `Option<RestartStrategy>: Copy`) by value through a bare
7782        // `self.<field>` field-access — no dispatch, no destructor, no
7783        // heap. Any future accidental downgrade to non-`const` fails
7784        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
7785        // build time with E0015 (`cannot call non-const method`),
7786        // strictly stronger than a runtime `assert!` and strictly
7787        // stronger than a module-scope `const _: () = assert!(…)` pin
7788        // (which cannot be formed on a `&Caixa` fixture because the
7789        // type's `String` / `Vec` / `Option<Composite>` carriers rule
7790        // out `const`-context value construction; the `const fn`
7791        // wrapper is the load-bearing shape that side-steps the
7792        // destructor-in-const restriction on the value axis while still
7793        // pinning the `const`-fn posture on the callee — mirror of the
7794        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7795        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
7796        // pins' discipline verbatim on the peer outer-`Caixa`
7797        // `String → &str` + `Option<String> → Option<&str>` axes at the
7798        // same struct).
7799        //
7800        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
7801        // family on the inner-altitude nested-spec typed-slot
7802        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
7803        // + [`crate::supervisor::ChildSpec::restart`] on the M2
7804        // supervisor-tree axis (pinned at 152c868), and
7805        // [`crate::aplicacao::Placement::estrategia`] +
7806        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
7807        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
7808        // unlifted altitude for the `Copy`-return-accessor family.
7809        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
7810            c.kind()
7811        }
7812        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
7813            c.estrategia()
7814        }
7815        // Sweep every arm of both discriminant partitions the accessors
7816        // fan on — every [`CaixaKind`] variant the six-arm required
7817        // discriminant carries (Biblioteca / Binario / Servico /
7818        // Supervisor / Aplicacao / Acao) and both arms of the
7819        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
7820        // (`Some(<strategy>)` on an author-declared supervisor and
7821        // `None` on the author-omitted default arm every non-Supervisor
7822        // caixa carries by `#[serde(default)]`) — so the `const fn`
7823        // wrapper family pins the closed-set partition through the
7824        // same const dispatch as the runtime path.
7825        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7826        c1.kind = CaixaKind::Servico;
7827        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
7828        assert_eq!(kind_via_const_fn(&c1), c1.kind());
7829        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
7830        assert_eq!(c1.kind(), CaixaKind::Servico);
7831        assert_eq!(
7832            c1.estrategia(),
7833            Some(crate::supervisor::RestartStrategy::OneForAll)
7834        );
7835        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7836        c2.kind = CaixaKind::Aplicacao;
7837        c2.estrategia = None;
7838        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
7839        assert_eq!(estrategia_via_const_fn(&c2), None);
7840        // Anchor the remaining discriminant arms so any future
7841        // reordering of [`CaixaKind`]'s six-variant enum surfaces
7842        // through the wrapper dispatch, not just through the direct
7843        // method call.
7844        for kind in [
7845            CaixaKind::Biblioteca,
7846            CaixaKind::Binario,
7847            CaixaKind::Servico,
7848            CaixaKind::Supervisor,
7849            CaixaKind::Aplicacao,
7850            CaixaKind::Acao,
7851        ] {
7852            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7853            c.kind = kind;
7854            assert_eq!(kind_via_const_fn(&c), kind);
7855        }
7856    }
7857
7858    #[test]
7859    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
7860        // Fail-before-pass-after pin on the five outer-[`Caixa`]
7861        // `Vec<String> → &[String]` slice-return accessors on the
7862        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
7863        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
7864        // Each body is a bare `self.<field>.as_slice()` dispatch through
7865        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7866        // the workspace MSRV). Any future accidental downgrade to
7867        // non-`const` fails the corresponding `<name>_via_const_fn`
7868        // wrapper at caixa-core build time with E0015 (`cannot call
7869        // non-const method`) — mirror of the sibling
7870        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
7871        // discipline on the peer outer-`Caixa` `Copy`-return accessor
7872        // axis, and peer of the sibling composite-carrier slice-return
7873        // pin below on the peer outer-`Caixa` composite-slice axis.
7874        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
7875            c.autores()
7876        }
7877        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
7878            c.etiquetas()
7879        }
7880        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
7881            c.bibliotecas()
7882        }
7883        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
7884            c.exe()
7885        }
7886        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
7887            c.servicos()
7888        }
7889        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
7890        // `servicos` — the template's `Vec::new()` default) and the
7891        // populated arm (mutated below) on every accessor so the
7892        // `const fn` wrapper family pins each axis's two-arm partition
7893        // through the same const dispatch as the runtime path.
7894        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
7895        // so that arm's "empty" fixture is the populated arm the
7896        // mutation sweep covers.
7897        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7898        assert!(autores_via_const_fn(&c_empty).is_empty());
7899        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
7900        assert!(exe_via_const_fn(&c_empty).is_empty());
7901        assert!(servicos_via_const_fn(&c_empty).is_empty());
7902        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7903        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
7904        c_full.etiquetas = vec!["compounding".to_string()];
7905        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
7906        c_full.exe = vec!["exe/cli.lisp".to_string()];
7907        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
7908        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
7909        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
7910        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
7911        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
7912        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
7913        assert_eq!(
7914            bibliotecas_via_const_fn(&c_full),
7915            &["lib/one.lisp", "lib/two.lisp"]
7916        );
7917        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
7918        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
7919        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
7920        assert_eq!(
7921            servicos_via_const_fn(&c_full),
7922            &["servicos/one.computeunit.yaml"]
7923        );
7924    }
7925
7926    #[test]
7927    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
7928        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
7929        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
7930        // / [`Caixa::deps_dev`] on the dep-graph axis,
7931        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
7932        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
7933        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
7934        // bare `self.<field>.as_slice()` dispatch through
7935        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7936        // the workspace MSRV) — peer of the sibling `String`-payload
7937        // slice-return pin above on the peer outer-`Caixa` universal-
7938        // axis surface, and peer of the sibling inner-composite-
7939        // altitude reference-return pin family
7940        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
7941        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
7942        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
7943        // (all pinned at 0b23e0f).
7944        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
7945            c.deps()
7946        }
7947        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
7948            c.deps_dev()
7949        }
7950        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
7951            c.upgrade_from()
7952        }
7953        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
7954            c.children()
7955        }
7956        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
7957            c.membros()
7958        }
7959        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
7960            c.contratos()
7961        }
7962        // Empty-arm sweep on all six composite-carrier axes — every
7963        // `Caixa::template` starts with `Vec::new()` on each.
7964        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7965        assert!(deps_via_const_fn(&c_empty).is_empty());
7966        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
7967        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
7968        assert!(children_via_const_fn(&c_empty).is_empty());
7969        assert!(membros_via_const_fn(&c_empty).is_empty());
7970        assert!(contratos_via_const_fn(&c_empty).is_empty());
7971        // Populate `:membros` / `:contratos` directly via struct literals
7972        // — the parser-side validation path fans on `:kind`-gated cross-
7973        // slot invariants irrelevant to the accessor dispatch under test.
7974        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7975        c_full.membros = vec![
7976            crate::aplicacao::Membro {
7977                caixa: "demo-a".to_string(),
7978                versao: "^0.1.0".to_string(),
7979            },
7980            crate::aplicacao::Membro {
7981                caixa: "demo-b".to_string(),
7982                versao: "^0.2.0".to_string(),
7983            },
7984        ];
7985        c_full.contratos = vec![crate::aplicacao::WitContract {
7986            de: "demo-a".to_string(),
7987            para: "demo-b".to_string(),
7988            wit: "wasi:http/proxy".to_string(),
7989            endpoint: Some("/edge".to_string()),
7990            subject: None,
7991            slot: None,
7992        }];
7993        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
7994        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
7995        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
7996        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
7997        // Alias-borrow check on the four remaining composite-carrier
7998        // slice-return arms — the wrapper's return borrow must alias the
7999        // caller's borrow so any future accessor re-routing that skips
8000        // the storage field surfaces through the assertion.
8001        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
8002        assert!(std::ptr::eq(
8003            deps_dev_via_const_fn(&c_full),
8004            c_full.deps_dev()
8005        ));
8006        assert!(std::ptr::eq(
8007            upgrade_from_via_const_fn(&c_full),
8008            c_full.upgrade_from()
8009        ));
8010        assert!(std::ptr::eq(
8011            children_via_const_fn(&c_full),
8012            c_full.children()
8013        ));
8014    }
8015
8016    #[test]
8017    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
8018        // Fail-before-pass-after pin on the six outer-[`Caixa`]
8019        // `Option<Composite> → Option<&Composite>` reference-return
8020        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
8021        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
8022        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
8023        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
8024        // Each body is a bare `self.<field>.as_ref()` dispatch through
8025        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
8026        // the workspace MSRV of 1.89). Any future accidental downgrade
8027        // to non-`const` fails the corresponding `<name>_via_const_fn`
8028        // wrapper at caixa-core build time with E0015 (`cannot call
8029        // non-const method`), strictly stronger than a runtime `assert!`
8030        // and strictly stronger than a module-scope `const _: () =
8031        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
8032        // because the type's `String` / `Vec` / `Option<Composite>`
8033        // carriers rule out `const`-context value construction; the
8034        // `const fn` wrapper is the load-bearing shape that side-steps
8035        // the destructor-in-const restriction on the value axis while
8036        // still pinning the `const`-fn posture on the callee — mirror
8037        // of the sibling
8038        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
8039        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
8040        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
8041        // pins' discipline verbatim on the peer outer-`Caixa` axes at
8042        // the same struct).
8043        //
8044        // Closes the outer-`Caixa` `Option<&Composite>` composite-
8045        // reference-return sub-family — the last unlifted altitude on
8046        // the outer-`Caixa` accessor-family const-eval surface after
8047        // the sibling `Copy`-return / universal-axis-`&str` /
8048        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
8049        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
8050        // 231a968 (the last of these pins the `Vec<T> → &[T]`
8051        // composite-slice arm the six accessors here close as their
8052        // `Option<Composite> → Option<&Composite>` peer). Peer of the
8053        // sibling inner-altitude nested-spec composite-reference-return
8054        // pin family — [`crate::AplicacaoSpec::politicas`] /
8055        // [`crate::AplicacaoSpec::placement`] /
8056        // [`crate::AplicacaoSpec::entrada`] on the inner
8057        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
8058        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
8059        // the same shape so both altitudes of the reference-return
8060        // discipline (per-`Caixa` outer-slot presence + per-
8061        // `AplicacaoSpec` inner-slot presence) route through one typed
8062        // const dispatch on the substrate primitive.
8063        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
8064            c.limits()
8065        }
8066        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
8067            c.behavior()
8068        }
8069        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
8070            c.politicas()
8071        }
8072        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
8073            c.placement()
8074        }
8075        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
8076            c.entrada()
8077        }
8078        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
8079            c.ci()
8080        }
8081        // Both-arm sweep on every accessor: the `None` author-omitted
8082        // arm (template default — no M2/M3/CI slot declared) and the
8083        // `Some(<composite>)` authored arm (mutated below via struct-
8084        // literal seeds, side-stepping the parser-side `:kind`-gated
8085        // cross-slot invariants irrelevant to the accessor dispatch
8086        // under test). Both arms route through the `const fn` wrapper
8087        // family so the two-arm `Option` partition is pinned through
8088        // the same const dispatch as the runtime path.
8089        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
8090        assert!(limits_via_const_fn(&c_empty).is_none());
8091        assert!(behavior_via_const_fn(&c_empty).is_none());
8092        assert!(politicas_via_const_fn(&c_empty).is_none());
8093        assert!(placement_via_const_fn(&c_empty).is_none());
8094        assert!(entrada_via_const_fn(&c_empty).is_none());
8095        assert!(ci_via_const_fn(&c_empty).is_none());
8096        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
8097        c_full.limits = Some(LimitsSpec::default());
8098        c_full.behavior = Some(crate::BehaviorSpec::default());
8099        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
8100        c_full.placement = Some(crate::aplicacao::Placement::default());
8101        c_full.entrada = Some(crate::aplicacao::Entrada {
8102            host: "demo.quero.cloud".to_string(),
8103            para: "demo".to_string(),
8104            paths: Vec::new(),
8105            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
8106        });
8107        c_full.ci = Some(canteiro_types::CiRun {
8108            workspace: "pleme-io".into(),
8109            repo: "caixa".into(),
8110            nodes: vec![],
8111        });
8112        assert!(limits_via_const_fn(&c_full).is_some());
8113        assert!(behavior_via_const_fn(&c_full).is_some());
8114        assert!(politicas_via_const_fn(&c_full).is_some());
8115        assert!(placement_via_const_fn(&c_full).is_some());
8116        assert!(entrada_via_const_fn(&c_full).is_some());
8117        assert!(ci_via_const_fn(&c_full).is_some());
8118        // Alias-borrow check on every arm: the wrapper's inner-`Option`
8119        // reference must alias the caller's borrow so any future accessor
8120        // re-routing that skips the storage field surfaces through the
8121        // assertion.
8122        assert!(std::ptr::eq(
8123            limits_via_const_fn(&c_full).unwrap(),
8124            c_full.limits().unwrap()
8125        ));
8126        assert!(std::ptr::eq(
8127            behavior_via_const_fn(&c_full).unwrap(),
8128            c_full.behavior().unwrap()
8129        ));
8130        assert!(std::ptr::eq(
8131            politicas_via_const_fn(&c_full).unwrap(),
8132            c_full.politicas().unwrap()
8133        ));
8134        assert!(std::ptr::eq(
8135            placement_via_const_fn(&c_full).unwrap(),
8136            c_full.placement().unwrap()
8137        ));
8138        assert!(std::ptr::eq(
8139            entrada_via_const_fn(&c_full).unwrap(),
8140            c_full.entrada().unwrap()
8141        ));
8142        assert!(std::ptr::eq(
8143            ci_via_const_fn(&c_full).unwrap(),
8144            c_full.ci().unwrap()
8145        ));
8146    }
8147
8148    #[test]
8149    fn register_populates_registry() {
8150        Caixa::register().expect("first register call in this test process must succeed");
8151        let kws = tatara_lisp::domain::registered_keywords();
8152        assert!(kws.contains(&"defcaixa"));
8153    }
8154
8155    #[test]
8156    fn to_lisp_round_trips() {
8157        let src = Caixa::template("demo");
8158        let c1 = Caixa::from_lisp(&src).unwrap();
8159        let emitted = c1.to_lisp();
8160        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
8161        assert_eq!(c1, c2);
8162    }
8163
8164    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
8165    //
8166    // The compounding pin: the variant stores only the typed
8167    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
8168    // (canonical keyword, description, consumer) routes through the enum's
8169    // own accessors at Display time. Prior to that closure the variant
8170    // carried each accessor's return value as a stored `&'static str`
8171    // snapshot alongside `dialeto`; a caller could construct the variant
8172    // with a snapshot that drifted from what `dialeto`'s accessors would
8173    // return, and every downstream user-facing projection would silently
8174    // disagree with the classification. Storing only the axis makes the
8175    // drift structurally impossible.
8176
8177    #[test]
8178    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
8179        // Single-field construction is the whole compounding shape — a
8180        // future re-introduction of a snapshot field (a `palavra_canonica:
8181        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
8182        // would re-open the drift surface and this construction would fail
8183        // to compile with "missing field" until every snapshot was seeded
8184        // at the call site again. The compile-time guarantee is the
8185        // invariant; the assertion below only witnesses that the
8186        // construction is well-formed after the closure.
8187        let err = LeituraError::DialetoEstrangeiro {
8188            dialeto: crate::dialeto::CaixaDialeto::Molde,
8189        };
8190        assert!(matches!(
8191            err,
8192            LeituraError::DialetoEstrangeiro {
8193                dialeto: crate::dialeto::CaixaDialeto::Molde,
8194            }
8195        ));
8196    }
8197
8198    #[test]
8199    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
8200        // For every foreign-dialect classification the variant surfaces —
8201        // [`crate::dialeto::CaixaDialeto::Molde`] and
8202        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
8203        // variants [`Caixa::from_lisp`] raises this error for — the
8204        // rendered [`std::fmt::Display`] byte-string must interpolate each
8205        // typed accessor's return verbatim. A future re-introduction of a
8206        // stored `&'static str` snapshot alongside `dialeto` that Display
8207        // read instead of the accessor would fail this pin as soon as the
8208        // two disagreed; a future accessor rebrand (a per-dialect
8209        // consumer rename, a canonical-keyword shift once the substrate
8210        // migration named in [`crate::dialeto`] completes) reaches every
8211        // consumer through one typed dispatch and this pin verifies the
8212        // display path is one of them.
8213        for d in [
8214            crate::dialeto::CaixaDialeto::Molde,
8215            crate::dialeto::CaixaDialeto::MoldePosicional,
8216        ] {
8217            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
8218            assert!(
8219                rendered.contains(d.palavra_canonica()),
8220                "Display must interpolate `dialeto.palavra_canonica()` \
8221                 verbatim — a stored snapshot would silently drift from \
8222                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
8223            );
8224            assert!(
8225                rendered.contains(d.descricao()),
8226                "Display must interpolate `dialeto.descricao()` verbatim. \
8227                 dialect: {d}, rendered: {rendered:?}"
8228            );
8229            assert!(
8230                rendered.contains(d.consumidor()),
8231                "Display must interpolate `dialeto.consumidor()` verbatim. \
8232                 dialect: {d}, rendered: {rendered:?}"
8233            );
8234        }
8235    }
8236
8237    #[test]
8238    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
8239        // The end-to-end pin the compounding closure defends: a
8240        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
8241        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
8242        // rendered Display byte-string names the Molde accessors'
8243        // returns verbatim. Any future path that constructed the variant
8244        // with a mismatched snapshot (a stored `palavra_canonica:
8245        // "defcaixa"` on a `Molde` classification) would land Display
8246        // pointing at `defcaixa` while the typed axis said `Molde` — the
8247        // exact drift the closure removes.
8248        let src = r#"
8249          (defcaixa
8250            :name "x"
8251            :kind :Biblioteca
8252            :ecosystem :rust-single-crate
8253            :package {:name "x" :version "0.1.0"})
8254        "#;
8255        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
8256        match err {
8257            LeituraError::DialetoEstrangeiro { dialeto } => {
8258                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
8259                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
8260                assert!(rendered.contains(dialeto.palavra_canonica()));
8261                assert!(rendered.contains(dialeto.consumidor()));
8262                assert!(rendered.contains(dialeto.descricao()));
8263            }
8264            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
8265        }
8266    }
8267
8268    #[test]
8269    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
8270        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
8271        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
8272        // positional-arity `defmolde` form written under a `(defcaixa …)`
8273        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
8274        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
8275        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
8276        // so no test exercised the positional-arity path through
8277        // `Caixa::from_lisp` specifically; the sibling
8278        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
8279        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
8280        // two arms route through the lifted
8281        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
8282        // typed predicate — the same predicate the pre-lift `foreign =>`
8283        // wildcard resolved to today — and this pin makes the
8284        // positional-arity arm's byte-shape at the gate explicit rather
8285        // than implied by wildcard-absorption. A future regression that
8286        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
8287        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
8288        // from the two-arity closure) would fail this pin at caixa-core
8289        // test time rather than surfacing far from the change as a
8290        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
8291        // …)` silently parsing past the derive.
8292        let src = r#"
8293          (defcaixa todoku-go
8294            :kind :Biblioteca
8295            :ecosystem :go
8296            :package {:name "todoku-go" :version "0.3.0"})
8297        "#;
8298        let err =
8299            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
8300        match err {
8301            LeituraError::DialetoEstrangeiro { dialeto } => {
8302                assert_eq!(
8303                    dialeto,
8304                    crate::dialeto::CaixaDialeto::MoldePosicional,
8305                    "DialetoEstrangeiro must carry the MoldePosicional \
8306                     variant verbatim — the positional-arity `defmolde` \
8307                     form under a `(defcaixa …)` head is the \
8308                     `MoldePosicional` arm's canonical byte-shape"
8309                );
8310                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
8311                assert!(
8312                    rendered.contains(dialeto.palavra_canonica()),
8313                    "Display must interpolate `dialeto.palavra_canonica()` \
8314                     verbatim on the MoldePosicional arm; rendered: \
8315                     {rendered:?}"
8316                );
8317                assert!(
8318                    rendered.contains(dialeto.consumidor()),
8319                    "Display must interpolate `dialeto.consumidor()` \
8320                     verbatim on the MoldePosicional arm; rendered: \
8321                     {rendered:?}"
8322                );
8323                assert!(
8324                    rendered.contains(dialeto.descricao()),
8325                    "Display must interpolate `dialeto.descricao()` \
8326                     verbatim on the MoldePosicional arm; rendered: \
8327                     {rendered:?}"
8328                );
8329            }
8330            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
8331        }
8332    }
8333
8334    #[test]
8335    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
8336        // Load-bearing byte-parity pin: for every arm in
8337        // [`crate::dialeto::CaixaDialeto::ALL`], the
8338        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
8339        // partition must agree with the lifted
8340        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
8341        // typed predicate — i.e. from_lisp raises
8342        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
8343        // `d.is_molde_family()` returns `true`, and does NOT raise
8344        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
8345        // predicate returns `false` (the arm's source falls through to
8346        // the derive — parses cleanly on
8347        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
8348        // [`LeituraError::Leitura`] on
8349        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
8350        //
8351        // Pre-lift the gate hand-rolled a three-arm match
8352        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
8353        // whose `foreign =>` wildcard expressed no compile-time link
8354        // back to the substrate primitive's arm-family; a future fifth
8355        // dialect the [`crate::dialeto`] module doc's "third dialect"
8356        // hazard actualises would fall silently onto the wildcard
8357        // regardless of whether it belonged to the `defmolde` family or
8358        // to a distinct `defcaixa`-family. Post-lift the partition
8359        // resolves through
8360        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
8361        // typed dispatch, and this pin refuses any future regression
8362        // that silently split the from_lisp partition from the typed
8363        // predicate — the two paths now migrate as one on any future
8364        // arm addition.
8365        //
8366        // Sibling in shape to the peer
8367        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
8368        // (e9d2315) that pins the same byte-parity between
8369        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
8370        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
8371        // `== "defmolde"` classifier — extends the discipline from the
8372        // two paths within the [`crate::dialeto`] primitive onto the
8373        // third external consumer of the `defmolde`-family partition
8374        // (the [`Caixa::from_lisp`] gate that raises
8375        // [`LeituraError::DialetoEstrangeiro`]).
8376        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
8377            (
8378                crate::dialeto::CaixaDialeto::Pacote,
8379                r#"
8380                  (defcaixa
8381                    :nome   "checkout"
8382                    :versao "0.1.0"
8383                    :kind   Biblioteca
8384                    :edicao "2026"
8385                    :descricao "canonical Pacote source"
8386                    :autores ()
8387                    :etiquetas ()
8388                    :deps ()
8389                    :deps-dev ()
8390                    :bibliotecas ("lib/checkout.lisp"))
8391                "#,
8392            ),
8393            (
8394                crate::dialeto::CaixaDialeto::Molde,
8395                r#"
8396                  (defcaixa
8397                    :name "base64"
8398                    :kind :Biblioteca
8399                    :ecosystem :rust-single-crate
8400                    :package {:name "base64" :version "0.22.1"}
8401                    :workflows [:auto-release])
8402                "#,
8403            ),
8404            (
8405                crate::dialeto::CaixaDialeto::MoldePosicional,
8406                r#"
8407                  (defcaixa todoku-go
8408                    :kind :Biblioteca
8409                    :ecosystem :go
8410                    :package {:name "todoku-go" :version "0.3.0"})
8411                "#,
8412            ),
8413            (
8414                crate::dialeto::CaixaDialeto::Desconhecido,
8415                r#"(defcaixa :licenca "MIT")"#,
8416            ),
8417        ];
8418
8419        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
8420        // must appear in the fixture table so the pin's arm-set stays
8421        // synchronised with the enum's arm-set. Fails at test time if a
8422        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
8423        // (with a corresponding `is_molde_family` return) forgot to
8424        // extend this fixture table with a canonical source for the new
8425        // arm — the pin cannot cover an arm it has no source for.
8426        for &expected in crate::dialeto::CaixaDialeto::ALL {
8427            assert!(
8428                fixtures.iter().any(|(d, _)| *d == expected),
8429                "fixture table must carry a canonical source for every \
8430                 CaixaDialeto arm; missing: {expected:?}"
8431            );
8432        }
8433
8434        for &(expected_dialect, src) in fixtures {
8435            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
8436                panic!(
8437                    "fixture source for {expected_dialect:?} must classify \
8438                     cleanly, got err: {err:?}"
8439                )
8440            });
8441            assert_eq!(
8442                classified, expected_dialect,
8443                "fixture source for {expected_dialect:?} must classify as \
8444                 {expected_dialect:?} (drift here defeats the byte-parity \
8445                 pin below — a source labelled for one arm but classifying \
8446                 as another would silently satisfy or violate the pin for \
8447                 the wrong reason)"
8448            );
8449
8450            let outcome = Caixa::from_lisp(src);
8451            match (expected_dialect.is_molde_family(), &outcome) {
8452                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
8453                    assert_eq!(
8454                        *dialeto, expected_dialect,
8455                        "DialetoEstrangeiro must carry the same typed arm \
8456                         the classifier returned — a drift here would let \
8457                         from_lisp raise the error while pointing at the \
8458                         wrong dialect (e.g. rejecting a \
8459                         MoldePosicional source as Molde). arm: \
8460                         {expected_dialect:?}"
8461                    );
8462                }
8463                (true, other) => panic!(
8464                    "arm {expected_dialect:?} has is_molde_family() = true \
8465                     so from_lisp must raise DialetoEstrangeiro carrying \
8466                     {expected_dialect:?}; got: {other:?}"
8467                ),
8468                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
8469                    "arm {expected_dialect:?} has is_molde_family() = false \
8470                     so from_lisp must NOT raise DialetoEstrangeiro; got \
8471                     one carrying: {dialeto:?}. This means the typed \
8472                     predicate and the from_lisp partition disagree on \
8473                     this arm — exactly the drift this pin refuses."
8474                ),
8475                (false, _) => {
8476                    // A non-molde arm's source falls through to the
8477                    // derive: Pacote sources parse to Ok(_); Desconhecido
8478                    // sources surface as LeituraError::Leitura from the
8479                    // derive's own unknown-keyword rejection. Either
8480                    // shape is acceptable here — the pin's promise is
8481                    // narrower: "no DialetoEstrangeiro on
8482                    // is_molde_family() == false".
8483                }
8484            }
8485        }
8486    }
8487
8488    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
8489
8490    #[test]
8491    fn limits_round_trip_via_json() {
8492        use crate::LimitsSpec;
8493        use std::time::Duration;
8494        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8495        c.limits = Some(LimitsSpec {
8496            memory: Some(64 * 1024 * 1024),
8497            fuel: Some(1_000_000),
8498            wall_clock: Some(Duration::from_secs(30)),
8499            cpu: Some(500),
8500        });
8501        let json = serde_json::to_string(&c).unwrap();
8502        assert!(json.contains("\"limits\""));
8503        assert!(json.contains("\"64MiB\""));
8504        assert!(json.contains("\"30s\""));
8505        assert!(json.contains("\"500m\""));
8506        let back: Caixa = serde_json::from_str(&json).unwrap();
8507        assert_eq!(c.limits, back.limits);
8508    }
8509
8510    #[test]
8511    fn behavior_round_trip_via_json() {
8512        use crate::BehaviorSpec;
8513        use std::path::PathBuf;
8514        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8515        c.behavior = Some(BehaviorSpec {
8516            on_init: Some(PathBuf::from("lib/init.lisp")),
8517            on_call: Some(PathBuf::from("lib/handlers.lisp")),
8518            ..Default::default()
8519        });
8520        let json = serde_json::to_string(&c).unwrap();
8521        let back: Caixa = serde_json::from_str(&json).unwrap();
8522        assert_eq!(c.behavior, back.behavior);
8523    }
8524
8525    #[test]
8526    fn upgrade_from_round_trip_via_json() {
8527        use crate::{UpgradeFromEntry, UpgradeInstruction};
8528        use std::path::PathBuf;
8529        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8530        c.upgrade_from = vec![UpgradeFromEntry {
8531            from: "0.1.0".into(),
8532            instructions: vec![
8533                UpgradeInstruction::LoadModule {
8534                    module: "demo".into(),
8535                },
8536                UpgradeInstruction::StateChange {
8537                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
8538                },
8539                UpgradeInstruction::SoftPurge {
8540                    module: "demo-old".into(),
8541                },
8542            ],
8543        }];
8544        let json = serde_json::to_string(&c).unwrap();
8545        let back: Caixa = serde_json::from_str(&json).unwrap();
8546        assert_eq!(c.upgrade_from, back.upgrade_from);
8547    }
8548
8549    #[test]
8550    fn supervisor_view_returns_typed_shape() {
8551        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8552        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8553        c.kind = CaixaKind::Supervisor;
8554        c.bibliotecas.clear();
8555        c.estrategia = Some(RestartStrategy::OneForOne);
8556        c.max_restarts = Some(5);
8557        c.restart_window = Some("60s".into());
8558        c.children = vec![ChildSpec {
8559            caixa: "worker".into(),
8560            versao: "^0.1".into(),
8561            restart: RestartPolicy::Permanent,
8562        }];
8563        let view = c.supervisor_view().expect("Supervisor kind has a view");
8564        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
8565        assert_eq!(view.max_restarts, 5);
8566        assert_eq!(
8567            view.restart_window,
8568            Some(std::time::Duration::from_secs(60))
8569        );
8570        assert_eq!(view.children.len(), 1);
8571        view.validate().unwrap();
8572    }
8573
8574    #[test]
8575    fn supervisor_view_none_for_non_supervisor_kinds() {
8576        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8577        assert!(c.supervisor_view().is_none());
8578    }
8579
8580    #[test]
8581    fn declared_mesh_slots_empty_for_bare_caixa() {
8582        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8583        assert!(c.declared_mesh_slots().is_empty());
8584    }
8585
8586    #[test]
8587    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
8588        use crate::{Entrada, Membro};
8589        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8590        // Set a non-adjacent pair (:membros + :entrada) to pin that the
8591        // canonical declaration order is preserved regardless of which
8592        // subset is populated.
8593        c.membros = vec![Membro {
8594            caixa: "a".into(),
8595            versao: "^0.1".into(),
8596        }];
8597        c.entrada = Some(Entrada {
8598            host: "x.example.com".into(),
8599            para: "a".into(),
8600            paths: vec![],
8601            port: 8080,
8602        });
8603        assert_eq!(
8604            c.declared_mesh_slots(),
8605            vec![
8606                crate::render::M3_AUTHOR_KEY_MEMBROS,
8607                crate::render::M3_AUTHOR_KEY_ENTRADA,
8608            ]
8609        );
8610    }
8611
8612    #[test]
8613    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8614        // Scalar-value pin: the five author-facing kebab-case labels the
8615        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
8616        // mesh slot axis, one arm per typed slot. Mirrors the peer
8617        // scalar-value pin the sibling
8618        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8619        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8620        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
8621        // carry (f49c8b0), so both altitudes of the typed-slot algebra
8622        // (per-Servico M2 + per-Aplicacao M3) share the same
8623        // "one canonical byte-string per arm" discipline. A future
8624        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
8625        // `:politicas` → `:policies`, `:placement` → `:distribution`,
8626        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
8627        // and every consumer that reaches for the label picks it up at
8628        // build time rather than at runtime as a downstream mismatch.
8629        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
8630        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
8631        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
8632        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
8633        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
8634    }
8635
8636    #[test]
8637    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
8638        // Production-through-const pin: the five per-arm labels the
8639        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
8640        // `Vec` route through the lifted
8641        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
8642        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
8643        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
8644        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
8645        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
8646        // declaration order. A future re-order or drift at the tagger
8647        // (a rename that reaches the tagger but not the const, or vice
8648        // versa) surfaces here at build time rather than at runtime as
8649        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
8650        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8651        // commit. Mirror of the peer
8652        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8653        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
8654        // axis.
8655        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
8656        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8657        c.membros = vec![Membro {
8658            caixa: "a".into(),
8659            versao: "^0.1".into(),
8660        }];
8661        c.contratos = vec![WitContract {
8662            de: "a".into(),
8663            para: "a".into(),
8664            wit: "wasi:http/proxy".into(),
8665            endpoint: Some("/x".into()),
8666            subject: None,
8667            slot: None,
8668        }];
8669        c.politicas = Some(MeshPolicy::default());
8670        c.placement = Some(Placement {
8671            estrategia: PlacementStrategy::Replicated,
8672            clusters: vec!["rio".into()],
8673            affinity: None,
8674            shard_key: None,
8675        });
8676        c.entrada = Some(Entrada {
8677            host: "x.example.com".into(),
8678            para: "a".into(),
8679            paths: vec![],
8680            port: 8080,
8681        });
8682        assert_eq!(
8683            c.declared_mesh_slots(),
8684            vec![
8685                crate::render::M3_AUTHOR_KEY_MEMBROS,
8686                crate::render::M3_AUTHOR_KEY_CONTRATOS,
8687                crate::render::M3_AUTHOR_KEY_POLITICAS,
8688                crate::render::M3_AUTHOR_KEY_PLACEMENT,
8689                crate::render::M3_AUTHOR_KEY_ENTRADA,
8690            ]
8691        );
8692    }
8693
8694    #[test]
8695    fn declared_supervisor_slots_empty_for_bare_caixa() {
8696        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8697        assert!(c.declared_supervisor_slots().is_empty());
8698    }
8699
8700    #[test]
8701    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
8702        use crate::RestartStrategy;
8703        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8704        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
8705        // that the canonical declaration order is preserved regardless
8706        // of which subset is populated.
8707        c.estrategia = Some(RestartStrategy::OneForOne);
8708        c.restart_window = Some("60s".into());
8709        assert_eq!(
8710            c.declared_supervisor_slots(),
8711            vec![
8712                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8713                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8714            ]
8715        );
8716    }
8717
8718    #[test]
8719    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8720        // Scalar-value pin: the four author-facing kebab-case labels the
8721        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
8722        // supervision-tree slot axis, one arm per typed slot. Mirrors the
8723        // peer scalar-value pins the sibling
8724        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
8725        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
8726        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
8727        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
8728        // top-level M3 slot consts carry, so all three kind-scoped
8729        // typed-slot-family author-facing-label axes route through one
8730        // canonical per-arm declaration. A future rebrand
8731        // (`:estrategia` → `:strategy` for English uniformity,
8732        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
8733        // `MaxIntensity` name, `:restart-window` → `:period` matching
8734        // OTP's `Period` name, `:children` → `:workers` matching Elixir
8735        // idiom) lands as an edit to exactly one const, and every
8736        // consumer that reaches for the label picks it up at build time
8737        // rather than at runtime as a downstream mismatch.
8738        assert_eq!(
8739            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8740            ":estrategia"
8741        );
8742        assert_eq!(
8743            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8744            ":max-restarts"
8745        );
8746        assert_eq!(
8747            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8748            ":restart-window"
8749        );
8750        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
8751    }
8752
8753    #[test]
8754    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
8755        // Production-through-const pin: the four per-arm labels the
8756        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
8757        // return `Vec` route through the lifted
8758        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
8759        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
8760        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
8761        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
8762        // canonical declaration order. A future re-order or drift at the
8763        // tagger (a rename that reaches the tagger but not the const, or
8764        // vice versa) surfaces here at build time rather than at runtime
8765        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
8766        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8767        // commit. Mirror of the peer
8768        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8769        // (f49c8b0) and
8770        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
8771        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
8772        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8773        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8774        c.estrategia = Some(RestartStrategy::OneForOne);
8775        c.max_restarts = Some(5);
8776        c.restart_window = Some("60s".into());
8777        c.children = vec![ChildSpec {
8778            caixa: "worker".into(),
8779            versao: "^0.1".into(),
8780            restart: RestartPolicy::Permanent,
8781        }];
8782        assert_eq!(
8783            c.declared_supervisor_slots(),
8784            vec![
8785                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8786                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8787                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8788                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
8789            ]
8790        );
8791    }
8792
8793    #[test]
8794    fn declared_servico_slots_empty_for_bare_caixa() {
8795        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8796        assert!(c.declared_servico_slots().is_empty());
8797    }
8798
8799    #[test]
8800    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
8801        use crate::{UpgradeFromEntry, UpgradeInstruction};
8802        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8803        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
8804        // the canonical declaration order is preserved regardless of
8805        // which subset is populated.
8806        c.limits = Some(crate::LimitsSpec {
8807            fuel: Some(1_000_000),
8808            ..Default::default()
8809        });
8810        c.upgrade_from = vec![UpgradeFromEntry {
8811            from: "0.1.0".into(),
8812            instructions: vec![UpgradeInstruction::Restart],
8813        }];
8814        assert_eq!(
8815            c.declared_servico_slots(),
8816            vec![
8817                crate::render::M2_AUTHOR_KEY_LIMITS,
8818                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8819            ]
8820        );
8821    }
8822
8823    #[test]
8824    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8825        // Scalar-value pin: the three author-facing kebab-case labels
8826        // the `(defcaixa … :<slot> (…))` surface admits on the M2
8827        // top-level slot axis, one arm per typed slot. Mirrors the peer
8828        // scalar-value pin the sibling renderer-side
8829        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
8830        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
8831        // consts carry, so both halves of the M2 top-level slot dual
8832        // axis (author-facing kebab-case label + renderer-side
8833        // camelCase overlay-container wire key) route through one
8834        // canonical per-arm declaration. A future rebrand
8835        // (`:limits` → `:sandbox` matching Lunatic per-process
8836        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
8837        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
8838        // matching Erlang's verbatim appup name) lands as an edit to
8839        // exactly one const, and every consumer that reaches for the
8840        // label picks it up at build time rather than at runtime as a
8841        // downstream mismatch.
8842        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
8843        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
8844        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
8845    }
8846
8847    #[test]
8848    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
8849        // Production-through-const pin: the three per-arm labels the
8850        // [`Caixa::declared_servico_slots`] tagger pushes onto its
8851        // return `Vec` route through the lifted
8852        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8853        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8854        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
8855        // declaration order. A future re-order or drift at the tagger
8856        // (a rename that reaches the tagger but not the const, or vice
8857        // versa) surfaces here at build time rather than at runtime as
8858        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
8859        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8860        // commit. Mirror of the peer
8861        // [`crate::behavior::BehaviorSpec::declared_slots`] production
8862        // tagger pin (889dc18) on the sibling per-callback axis.
8863        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
8864        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8865        c.limits = Some(crate::LimitsSpec {
8866            fuel: Some(1_000_000),
8867            ..Default::default()
8868        });
8869        c.behavior = Some(BehaviorSpec {
8870            on_init: Some(PathBuf::from("lib/init.lisp")),
8871            ..Default::default()
8872        });
8873        c.upgrade_from = vec![UpgradeFromEntry {
8874            from: "0.1.0".into(),
8875            instructions: vec![UpgradeInstruction::Restart],
8876        }];
8877        assert_eq!(
8878            c.declared_servico_slots(),
8879            vec![
8880                crate::render::M2_AUTHOR_KEY_LIMITS,
8881                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
8882                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8883            ]
8884        );
8885    }
8886
8887    #[test]
8888    fn existing_manifests_unaffected_by_new_optional_slots() {
8889        // Regression test: a caixa.lisp authored before M2 typed slots
8890        // should still parse + serialize cleanly. The bare `defcaixa`
8891        // emitted by `Caixa::template` has none of the new fields.
8892        let src = Caixa::template("legacy");
8893        let c = Caixa::from_lisp(&src).unwrap();
8894        assert!(c.limits.is_none());
8895        assert!(c.behavior.is_none());
8896        assert!(c.upgrade_from.is_empty());
8897        assert!(c.estrategia.is_none());
8898        assert!(c.children.is_empty());
8899
8900        // And to_lisp emits a manifest with the new slots in the
8901        // empty/default state — round-trippable.
8902        let emitted = c.to_lisp();
8903        let back = Caixa::from_lisp(&emitted).unwrap();
8904        assert_eq!(c, back);
8905    }
8906
8907    #[test]
8908    fn validate_deps_accepts_canonical_caixa() {
8909        // Positive control: the bare template — zero deps, zero
8910        // deps_dev — passes the gate trivially. A future axis added to
8911        // `Dep::validate` mustn't regress an empty-deps caixa to a
8912        // build error.
8913        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8914        c.validate_deps().unwrap();
8915    }
8916
8917    #[test]
8918    fn validate_deps_rejects_invalid_versao_in_deps() {
8919        // Fail-before-pass-after pin: a malformed `:deps :versao`
8920        // surfaces at validate_deps() time, not at lacre-resolve time.
8921        // Mirrors `rejects_invalid_membro_versao_requirement` and
8922        // `validate_rejects_invalid_child_versao_requirement` on the
8923        // other two `:versao` axes.
8924        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8925        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
8926        let err = c.validate_deps().unwrap_err();
8927        assert!(
8928            matches!(
8929                err,
8930                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8931                    if nome == "caixa-teia" && versao == "^bad-version"
8932            ),
8933            "got {err:?}"
8934        );
8935    }
8936
8937    #[test]
8938    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
8939        // Parity pin: `:deps-dev` must run through the same per-entry
8940        // validator as `:deps` — a typo in either axis surfaces the
8941        // same diagnostic. Without this leg, `:deps-dev` would be a
8942        // second-class citizen of the typed surface and an author
8943        // could land a build that passes validate_deps but fails at
8944        // `feira lock`-time when the dev-dep is resolved for a test
8945        // build.
8946        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8947        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
8948        let err = c.validate_deps().unwrap_err();
8949        assert!(
8950            matches!(
8951                err,
8952                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8953                    if nome == "tatara-check" && versao == "^^0.1"
8954            ),
8955            "got {err:?}"
8956        );
8957    }
8958
8959    #[test]
8960    fn validate_deps_runs_deps_before_deps_dev() {
8961        // Order pin: when both lists carry typos, the `:deps`
8962        // diagnostic surfaces first. The author's mental model is
8963        // "runtime deps are load-bearing; dev deps are scaffolding";
8964        // surfacing the runtime axis first matches that hierarchy.
8965        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8966        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
8967        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
8968        let err = c.validate_deps().unwrap_err();
8969        assert!(
8970            matches!(
8971                err,
8972                crate::dep::DepError::VersaoInvalid { ref nome, .. }
8973                    if nome == "runtime-dep"
8974            ),
8975            "expected `:deps` typo to surface first, got {err:?}"
8976        );
8977    }
8978
8979    #[test]
8980    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
8981        // Positive control sweep across both lists. Pin every
8982        // canonical Cargo-shaped form so a future tightening of the
8983        // accepted set surfaces here as a test failure (parity with
8984        // `accepts_canonical_membro_versao_forms` and
8985        // `validate_accepts_canonical_child_versao_forms`).
8986        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8987        c.deps = vec![
8988            Dep::simple("caret", "^0.1"),
8989            Dep::simple("tilde", "~0.1.2"),
8990            Dep::simple("exact", "0.1.0"),
8991            Dep::simple("wildcard", "*"),
8992            Dep::simple("multi-range", ">=0.1, <2"),
8993        ];
8994        c.deps_dev = vec![
8995            Dep::simple("dev-caret", "^0.1"),
8996            Dep::simple("dev-wildcard", "*"),
8997        ];
8998        c.validate_deps().unwrap();
8999    }
9000
9001    #[test]
9002    fn validate_deps_diagnostic_carries_offending_dep() {
9003        // Diagnostic-shape pin: the error names the offending entry's
9004        // `:nome` + `:versao` verbatim and carries a non-empty
9005        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
9006        // run can render the diagnostic without re-parsing.
9007        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9008        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
9009        let err = c.validate_deps().unwrap_err();
9010        let crate::dep::DepError::VersaoInvalid {
9011            nome,
9012            versao,
9013            reason,
9014        } = err
9015        else {
9016            panic!("expected VersaoInvalid, got other variant");
9017        };
9018        assert_eq!(nome, "caixa-teia");
9019        assert_eq!(versao, "not-a-req");
9020        assert!(
9021            !reason.is_empty(),
9022            "VersaoInvalid `reason` must carry the parser's wording verbatim"
9023        );
9024    }
9025
9026    #[test]
9027    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
9028        // Cross-axis pin: `validate_deps` walks both :deps and
9029        // :deps-dev through `Dep::validate`, and the new fonte gate
9030        // (`:tag` + `:branch` both set — the canonical "pin drift"
9031        // footgun) must surface from the :deps-dev arm with the
9032        // offending entry's :nome named. Pin the :deps-dev arm
9033        // explicitly so a future shortcut that only walks :deps
9034        // surfaces here as a regression.
9035        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9036        c.deps_dev = vec![Dep {
9037            nome: "dev-only".into(),
9038            versao: "^0.1".into(),
9039            fonte: Some(crate::DepSource::Git {
9040                repo: "github:p/x".into(),
9041                tag: Some("v1".into()),
9042                rev: None,
9043                branch: Some("main".into()),
9044            }),
9045            opcional: false,
9046            caracteristicas: vec![],
9047        }];
9048        let err = c.validate_deps().unwrap_err();
9049        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
9050            panic!("expected FontePinAmbiguous from :deps-dev walk");
9051        };
9052        assert_eq!(nome, "dev-only");
9053        assert!(pins.contains(":tag") && pins.contains(":branch"));
9054    }
9055
9056    #[test]
9057    fn validate_deps_rejects_empty_repo_in_deps() {
9058        // Parity pin on the :deps arm: an empty :repo on the runtime
9059        // deps list surfaces the same FonteRepoEmpty diagnostic the
9060        // dep.rs per-entry tests pin, naming the offending entry.
9061        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9062        c.deps = vec![Dep {
9063            nome: "runtime".into(),
9064            versao: "^0.1".into(),
9065            fonte: Some(crate::DepSource::Git {
9066                repo: String::new(),
9067                tag: Some("v1".into()),
9068                rev: None,
9069                branch: None,
9070            }),
9071            opcional: false,
9072            caracteristicas: vec![],
9073        }];
9074        let err = c.validate_deps().unwrap_err();
9075        assert!(
9076            matches!(
9077                err,
9078                crate::dep::DepError::FonteRepoEmpty { ref nome }
9079                    if nome == "runtime"
9080            ),
9081            "got {err:?}"
9082        );
9083    }
9084
9085    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
9086
9087    #[test]
9088    fn validate_deps_rejects_duplicate_nome_in_deps() {
9089        // Fail-before-pass-after pin: two `:deps` entries naming the same
9090        // caixa carry two `:versao` / `:fonte` / feature triples that the
9091        // caixa-resolver's lacre pipeline collapses (the second silently
9092        // overwrites the first at `concrete_versao`-resolve time). The
9093        // gate surfaces the duplicate at validate-time, naming the
9094        // offending caixa + the list, before the resolver-side silent
9095        // drop. Mirrors the peer typed-graph duplicate gates
9096        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
9097        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9098        c.deps = vec![
9099            Dep::simple("caixa-teia", "^0.1"),
9100            Dep::simple("caixa-teia", "^0.2"),
9101        ];
9102        let err = c.validate_deps().unwrap_err();
9103        assert!(
9104            matches!(
9105                err,
9106                crate::dep::DepError::DuplicateNome { ref nome, list }
9107                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9108            ),
9109            "got {err:?}"
9110        );
9111    }
9112
9113    #[test]
9114    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
9115        // Parity pin: `:deps-dev` runs through the same per-list
9116        // duplicate check as `:deps` — neither axis is a second-class
9117        // citizen of the set-not-multiset discipline.
9118        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9119        c.deps_dev = vec![
9120            Dep::simple("tatara-check", "*"),
9121            Dep::simple("tatara-check", "^0.1"),
9122        ];
9123        let err = c.validate_deps().unwrap_err();
9124        assert!(
9125            matches!(
9126                err,
9127                crate::dep::DepError::DuplicateNome { ref nome, list }
9128                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
9129            ),
9130            "got {err:?}"
9131        );
9132    }
9133
9134    #[test]
9135    fn validate_deps_accepts_cross_list_same_nome() {
9136        // The Cargo `[dependencies]` + `[dev-dependencies]` override
9137        // convention is preserved: a name appearing in *both* lists is
9138        // valid (the dev-pin overrides at test/dev time). Only
9139        // within-list duplicates are structurally incoherent — pin the
9140        // permissive cross-list semantics so a future shortcut that
9141        // collapses the two seen-sets into one surfaces here as a test
9142        // failure.
9143        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9144        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
9145        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
9146        c.validate_deps().unwrap();
9147    }
9148
9149    #[test]
9150    fn validate_deps_accepts_distinct_nome_in_both_lists() {
9151        // Positive control: distinct names within each list pass — the
9152        // gate's identity element on the canonical authoring shape.
9153        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9154        c.deps = vec![
9155            Dep::simple("caixa-teia", "^0.1"),
9156            Dep::simple("pleme-mesh", "*"),
9157        ];
9158        c.deps_dev = vec![
9159            Dep::simple("tatara-check", "*"),
9160            Dep::simple("dev-shim", "^0.1"),
9161        ];
9162        c.validate_deps().unwrap();
9163    }
9164
9165    #[test]
9166    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
9167        // Diagnostic-precedence pin: a malformed `:versao` on the
9168        // duplicating entry surfaces its narrower `VersaoInvalid`
9169        // diagnostic first, before the cross-entry duplicate gate fires
9170        // — the canonical "per-entry shape before cross-entry uniqueness"
9171        // precedence every peer set-not-multiset gate establishes
9172        // (`*_invalid_fires_before_duplicate_check` pins on
9173        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
9174        // `validate_upgrade_from`).
9175        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9176        c.deps = vec![
9177            Dep::simple("caixa-teia", "^0.1"),
9178            Dep::simple("caixa-teia", "^bad-version"),
9179        ];
9180        let err = c.validate_deps().unwrap_err();
9181        assert!(
9182            matches!(
9183                err,
9184                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
9185                    if nome == "caixa-teia" && versao == "^bad-version"
9186            ),
9187            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
9188        );
9189    }
9190
9191    #[test]
9192    fn validate_deps_duplicate_diagnostic_names_first_collision() {
9193        // First-collision determinism pin: with three entries naming the
9194        // same caixa, the first colliding pair surfaces — not the last.
9195        // Mirrors the peer first-collision posture on every
9196        // duplicate-target gate
9197        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
9198        // — the second entry is the first collision; this gate uses the
9199        // same shape: the second entry's `:nome` lands in the diagnostic
9200        // because `seen.insert(first.nome)` already populated the set).
9201        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9202        c.deps = vec![
9203            Dep::simple("caixa-teia", "^0.1"),
9204            Dep::simple("caixa-teia", "^0.2"),
9205            Dep::simple("caixa-teia", "^0.3"),
9206        ];
9207        let err = c.validate_deps().unwrap_err();
9208        // The diagnostic carries the offending caixa name; the
9209        // implementation surfaces on the *second* entry (the first
9210        // collision), so the test pins the `:nome` value.
9211        assert!(
9212            matches!(
9213                err,
9214                crate::dep::DepError::DuplicateNome { ref nome, list }
9215                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9216            ),
9217            "got {err:?}"
9218        );
9219    }
9220
9221    #[test]
9222    fn validate_deps_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
9223        // Cross-list precedence pin: when both lists carry duplicates,
9224        // the `:deps` diagnostic surfaces first — same author-mental-
9225        // model ordering the `validate_deps_runs_deps_before_deps_dev`
9226        // pin establishes for malformed `:versao` (runtime axis before
9227        // dev axis).
9228        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9229        c.deps = vec![
9230            Dep::simple("runtime-dep", "^0.1"),
9231            Dep::simple("runtime-dep", "^0.2"),
9232        ];
9233        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
9234        let err = c.validate_deps().unwrap_err();
9235        assert!(
9236            matches!(
9237                err,
9238                crate::dep::DepError::DuplicateNome { ref nome, list }
9239                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9240            ),
9241            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
9242        );
9243    }
9244
9245    #[test]
9246    fn validate_deps_empty_lists_pass_duplicate_gate() {
9247        // Empty-set identity pin: the bare template (zero deps, zero
9248        // deps_dev) passes the duplicate gate as the gate's identity
9249        // element. A future tighten that conflates "empty" with
9250        // "missing" would regress this baseline.
9251        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9252        c.validate_deps().unwrap();
9253    }
9254
9255    #[test]
9256    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
9257        // Diagnostic-shape pin: the `list:` field tags which list the
9258        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
9259        // `feira lint` run can route the author to the right block in
9260        // their caixa.lisp without re-deriving the list from context.
9261        // Same self-locating shape every peer per-axis diagnostic
9262        // already exposes.
9263        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9264        c.deps_dev = vec![
9265            Dep::simple("dev-thing", "*"),
9266            Dep::simple("dev-thing", "^0.1"),
9267        ];
9268        let err = c.validate_deps().unwrap_err();
9269        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
9270            panic!("expected DuplicateNome from :deps-dev walk");
9271        };
9272        assert_eq!(nome, "dev-thing");
9273        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
9274    }
9275
9276    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
9277
9278    #[test]
9279    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
9280        // Thread-through pin on `:deps`: the per-entry
9281        // `Dep::validate_caracteristicas` gate fires inside
9282        // `Caixa::validate_deps`'s linear walk, so a malformed feature
9283        // list on any `:deps` entry surfaces as a `DepError` from
9284        // `validate_deps` — the same reachability shape every per-entry
9285        // `Dep::validate` arm threads through. Without this pin a future
9286        // shortcut that skips the per-entry `Dep::validate` call on the
9287        // cross-entry-uniqueness path would mask the within-entry
9288        // `:caracteristicas` gates.
9289        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9290        c.deps = vec![Dep {
9291            nome: "caixa-teia".into(),
9292            versao: "^0.1".into(),
9293            fonte: None,
9294            opcional: false,
9295            caracteristicas: vec!["http".into(), "http".into()],
9296        }];
9297        let err = c.validate_deps().unwrap_err();
9298        let crate::dep::DepError::CaracteristicaDuplicate {
9299            nome,
9300            caracteristica,
9301        } = err
9302        else {
9303            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
9304        };
9305        assert_eq!(nome, "caixa-teia");
9306        assert_eq!(caracteristica, "http");
9307    }
9308
9309    #[test]
9310    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
9311        // Peer thread-through pin on `:deps-dev`: same reachability as
9312        // the `:deps` arm above, on the dev-only authoring axis. Pins
9313        // that the `validate_deps` walk visits both lists' per-entry
9314        // gates uniformly. The empty-feature arm carries here so both
9315        // new `:caracteristicas` arms are surfaced via at least one
9316        // `validate_deps` thread-through.
9317        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9318        c.deps_dev = vec![Dep {
9319            nome: "caixa-teia".into(),
9320            versao: "^0.1".into(),
9321            fonte: None,
9322            opcional: false,
9323            caracteristicas: vec![String::new()],
9324        }];
9325        let err = c.validate_deps().unwrap_err();
9326        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
9327            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
9328        };
9329        assert_eq!(nome, "caixa-teia");
9330    }
9331
9332    #[test]
9333    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
9334        // Thread-through pin on `:deps`: the per-entry
9335        // `Dep::validate_caracteristicas` value-shape gate (lifted via
9336        // `crate::render::is_cargo_feature_name`) fires inside
9337        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
9338        // a structurally invalid feature name on any `:deps` entry
9339        // surfaces as `DepError::CaracteristicaInvalid` from
9340        // `validate_deps` — the same reachability shape every per-entry
9341        // `Dep::validate` arm threads through. Without this pin a
9342        // future shortcut that skips the per-entry `Dep::validate` call
9343        // on the cross-entry-uniqueness path would mask the within-
9344        // entry `:caracteristicas` value-shape gate.
9345        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9346        c.deps = vec![Dep {
9347            nome: "caixa-teia".into(),
9348            versao: "^0.1".into(),
9349            fonte: None,
9350            opcional: false,
9351            caracteristicas: vec!["+http".into()],
9352        }];
9353        let err = c.validate_deps().unwrap_err();
9354        let crate::dep::DepError::CaracteristicaInvalid {
9355            nome,
9356            caracteristica,
9357            ..
9358        } = err
9359        else {
9360            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
9361        };
9362        assert_eq!(nome, "caixa-teia");
9363        assert_eq!(caracteristica, "+http");
9364    }
9365
9366    #[test]
9367    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
9368        // Peer thread-through pin on `:deps-dev`: same reachability as
9369        // the `:deps` arm above, on the dev-only authoring axis. The
9370        // `http/json` shape carries here so the segment-separator
9371        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
9372        // confusion footgun) is surfaced via the cross-entry walk too —
9373        // pinning that the `:deps-dev` list visits the same per-entry
9374        // value-shape gate as the `:deps` list.
9375        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9376        c.deps_dev = vec![Dep {
9377            nome: "caixa-teia".into(),
9378            versao: "^0.1".into(),
9379            fonte: None,
9380            opcional: false,
9381            caracteristicas: vec!["http/json".into()],
9382        }];
9383        let err = c.validate_deps().unwrap_err();
9384        let crate::dep::DepError::CaracteristicaInvalid {
9385            nome,
9386            caracteristica,
9387            ..
9388        } = err
9389        else {
9390            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
9391        };
9392        assert_eq!(nome, "caixa-teia");
9393        assert_eq!(caracteristica, "http/json");
9394    }
9395
9396    #[test]
9397    fn to_lisp_preserves_deps() {
9398        let src = r#"
9399(defcaixa
9400  :nome "x"
9401  :versao "0.1.0"
9402  :kind Biblioteca
9403  :deps ((:nome "a" :versao "^0.1")
9404         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
9405"#;
9406        let c1 = Caixa::from_lisp(src).unwrap();
9407        let emitted = c1.to_lisp();
9408        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
9409        assert_eq!(c1.deps, c2.deps);
9410    }
9411
9412    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
9413
9414    fn caixa_with_nome(nome: &str) -> Caixa {
9415        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
9416        c.nome = nome.to_string();
9417        c
9418    }
9419
9420    #[test]
9421    fn validate_nome_accepts_canonical_template() {
9422        // Positive control: the bare `feira init`-style template's
9423        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
9424        // not regress this baseline shape. A future tightening of the
9425        // accepted set surfaces here as a test failure first.
9426        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9427        c.validate_nome().unwrap();
9428    }
9429
9430    #[test]
9431    fn validate_nome_accepts_canonical_forms() {
9432        // Positive-set sweep: each realistic caixa-name shape the K8s
9433        // apiserver accepts as a `metadata.name` label must pass —
9434        // single-word, hyphen-joined, version-suffixed, single-char,
9435        // two-char, digit-start (DNS-1123 allows this; the stricter
9436        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
9437        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
9438        // the peer member-name axis.
9439        for nome in [
9440            "checkout",
9441            "cart-v2",
9442            "a",
9443            "db",
9444            "3rd-party-shim",
9445            "payment-retry",
9446            "0",
9447        ] {
9448            caixa_with_nome(nome)
9449                .validate_nome()
9450                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
9451        }
9452    }
9453
9454    #[test]
9455    fn validate_nome_rejects_empty() {
9456        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9457        // an empty `:nome` (the derive macro stores the raw String);
9458        // the gate's empty arm names the offending axis with a narrower
9459        // diagnostic than the `NomeInvalid` parse arm would emit.
9460        let c = caixa_with_nome("");
9461        let err = c.validate_nome().unwrap_err();
9462        assert_eq!(err, ManifestError::NomeEmpty);
9463    }
9464
9465    #[test]
9466    fn validate_nome_rejects_uppercase() {
9467        // The canonical "I copied the TitleCase display name verbatim"
9468        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
9469        // admission on every derived artifact (Helm chart, ComputeUnit,
9470        // CNP, HTTPRoute, label values); the gate moves the diagnostic
9471        // to the source `caixa.lisp` and the reason suggests the
9472        // lowercased fix verbatim.
9473        let c = caixa_with_nome("MyApp");
9474        let err = c.validate_nome().unwrap_err();
9475        let ManifestError::NomeInvalid { nome, reason } = err else {
9476            panic!("expected NomeInvalid for uppercase :nome");
9477        };
9478        assert_eq!(nome, "MyApp");
9479        assert!(
9480            reason.contains("uppercase") && reason.contains("myapp"),
9481            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
9482        );
9483    }
9484
9485    #[test]
9486    fn validate_nome_rejects_underscore() {
9487        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
9488        // `_`; the apiserver rejects on admission across every derived
9489        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
9490        // and `:children :caixa` (31bfa43).
9491        let c = caixa_with_nome("my_app");
9492        let err = c.validate_nome().unwrap_err();
9493        assert!(
9494            matches!(
9495                err,
9496                ManifestError::NomeInvalid { ref nome, ref reason }
9497                    if nome == "my_app" && reason.contains('_')
9498            ),
9499            "got {err:?}"
9500        );
9501    }
9502
9503    #[test]
9504    fn validate_nome_rejects_dot() {
9505        // A `:nome` is a single DNS-1123 label, not a subdomain. The
9506        // "I want to namespace with `.`" footgun the gate redirects to
9507        // `-` via the shared predicate's reason wording.
9508        let c = caixa_with_nome("team.app");
9509        let err = c.validate_nome().unwrap_err();
9510        assert!(
9511            matches!(
9512                err,
9513                ManifestError::NomeInvalid { ref nome, ref reason }
9514                    if nome == "team.app" && reason.contains('.')
9515            ),
9516            "got {err:?}"
9517        );
9518    }
9519
9520    #[test]
9521    fn validate_nome_rejects_leading_hyphen() {
9522        // DNS-1123 boundary rule: the label must start with an ASCII
9523        // alphanumeric. Pin the leading-`-` arm explicitly.
9524        let c = caixa_with_nome("-app");
9525        let err = c.validate_nome().unwrap_err();
9526        assert!(
9527            matches!(
9528                err,
9529                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
9530            ),
9531            "got {err:?}"
9532        );
9533    }
9534
9535    #[test]
9536    fn validate_nome_rejects_trailing_hyphen() {
9537        // Symmetric arm of the boundary rule, pinned separately so a
9538        // future relaxation that only checks the leading position
9539        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
9540        // and `_with_trailing_hyphen` on the supervisor / aplicacao
9541        // axes.
9542        let c = caixa_with_nome("app-");
9543        let err = c.validate_nome().unwrap_err();
9544        assert!(
9545            matches!(
9546                err,
9547                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
9548            ),
9549            "got {err:?}"
9550        );
9551    }
9552
9553    #[test]
9554    fn validate_nome_rejects_unicode() {
9555        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
9556        // bytes are rejected by the K8s apiserver on every name axis.
9557        let c = caixa_with_nome("café");
9558        let err = c.validate_nome().unwrap_err();
9559        assert!(
9560            matches!(
9561                err,
9562                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
9563            ),
9564            "got {err:?}"
9565        );
9566    }
9567
9568    #[test]
9569    fn validate_nome_rejects_whitespace() {
9570        // The paste-from-sketch / paste-from-spec footgun. Internal
9571        // whitespace is rejected by every K8s name axis.
9572        let c = caixa_with_nome("my app");
9573        let err = c.validate_nome().unwrap_err();
9574        assert!(
9575            matches!(
9576                err,
9577                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
9578            ),
9579            "got {err:?}"
9580        );
9581    }
9582
9583    #[test]
9584    fn validate_nome_rejects_too_long() {
9585        // 64-byte boundary pin: the K8s apiserver rejects any
9586        // `metadata.name` over 63 bytes at admission; the diagnostic
9587        // names both the 63-byte cap and the actual length so the
9588        // author can shorten in one edit. Mirrors `_too_long` on the
9589        // peer member-/cluster-/child-name axes.
9590        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
9591        let c = caixa_with_nome(&over);
9592        let err = c.validate_nome().unwrap_err();
9593        let ManifestError::NomeInvalid { nome, reason } = err else {
9594            panic!("expected NomeInvalid for over-cap :nome");
9595        };
9596        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
9597        assert!(
9598            reason.contains("63") && reason.contains("64"),
9599            "diagnostic must name the cap + actual length, got {reason:?}"
9600        );
9601    }
9602
9603    #[test]
9604    fn nome_max_length_validates() {
9605        // The 63-byte cap exactly — the boundary-accepting case pinned
9606        // alongside `validate_nome_rejects_too_long` so a future cap
9607        // shift surfaces both arms simultaneously. Mirrors
9608        // `membro_caixa_max_length_validates`,
9609        // `placement_cluster_max_length_validates`,
9610        // `child_caixa_max_length_validates`.
9611        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9612        caixa_with_nome(&at_cap).validate_nome().unwrap();
9613    }
9614
9615    #[test]
9616    fn nome_empty_takes_precedence_over_invalid() {
9617        // Order pin: the empty arm fires before the predicate is
9618        // consulted. Empty < invalid in self-locating-ness — the
9619        // narrower `NomeEmpty` diagnostic doesn't carry a useless
9620        // `nome: ""` reference into the parser-shaped reason. Mirrors
9621        // `membro_caixa_empty_takes_precedence_over_invalid` on the
9622        // peer axis (3f9d7a0).
9623        let c = caixa_with_nome("");
9624        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
9625    }
9626
9627    #[test]
9628    fn nome_invalid_diagnostic_carries_offending_nome() {
9629        // Diagnostic-shape pin: the error names the offending `:nome`
9630        // verbatim with a non-empty parser-shaped reason, so a `feira
9631        // lint` run can render the diagnostic without re-parsing.
9632        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
9633        let c = caixa_with_nome("MyApp");
9634        let err = c.validate_nome().unwrap_err();
9635        let ManifestError::NomeInvalid { nome, reason } = err else {
9636            panic!("expected NomeInvalid variant");
9637        };
9638        assert_eq!(nome, "MyApp");
9639        assert!(
9640            !reason.is_empty(),
9641            "NomeInvalid `reason` must carry the predicate's wording verbatim"
9642        );
9643    }
9644
9645    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
9646    //
9647    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
9648    // via DNS-1123; this second-axis gate caps the joint
9649    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
9650    // canonical [`crate::lareira_chart_name`] helper's doc comment
9651    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
9652    // "the M4 admission webhook will pin the joint-length invariant
9653    // when it lands". These tests pin it at the manifest-validate
9654    // layer instead, fail-before-pass-after on the 56-byte boundary.
9655
9656    #[test]
9657    fn validate_nome_chart_name_budget_accepts_canonical_template() {
9658        // Positive control: the bare `feira init`-style template's
9659        // `:nome` ("demo") sits far below the cap; the gate must not
9660        // regress this baseline. Same shape every peer
9661        // value-shape-gate baseline pin uses.
9662        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9663        c.validate_nome_chart_name_budget().unwrap();
9664    }
9665
9666    #[test]
9667    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
9668        // Positive-set sweep across the canonical author surface every
9669        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
9670        // `worker`, the `checkout-aplicacao` example members, the
9671        // `example-attest` caixa-tatara fixture). Every value sits
9672        // far below the 55-byte per-`:nome` budget. Same shape every
9673        // peer per-axis baseline pin uses.
9674        for nome in [
9675            "hello-rio",
9676            "cart",
9677            "checkout",
9678            "worker",
9679            "example-attest",
9680            "demo",
9681            "a",
9682        ] {
9683            caixa_with_nome(nome)
9684                .validate_nome_chart_name_budget()
9685                .unwrap_or_else(|e| {
9686                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
9687                });
9688        }
9689    }
9690
9691    #[test]
9692    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
9693        // Boundary-accepting case at the 55-byte per-`:nome` budget —
9694        // the joint chart name is exactly 63 bytes, the DNS-1123 label
9695        // cap. Pinned alongside the rejecting-arm test so a future cap
9696        // shift surfaces both arms simultaneously. Mirrors
9697        // `nome_max_length_validates` on the peer bare-`:nome` axis.
9698        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
9699        caixa_with_nome(&at_cap)
9700            .validate_nome_chart_name_budget()
9701            .unwrap();
9702    }
9703
9704    #[test]
9705    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
9706        // Fail-before-pass-after pin on the 56-byte boundary: the
9707        // smallest `:nome` length that overflows the joint chart-name
9708        // cap. The inner [`is_dns_1123_label`] gate
9709        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
9710        // this gate it silently passed the manifest-validate cascade
9711        // and surfaced as a `helm lint` / apiserver rejection on the
9712        // rendered chart name far from the source `caixa.lisp`, with
9713        // no field naming the overflow. With this gate the diagnostic
9714        // names the offending `:nome` verbatim alongside the rendered
9715        // chart name and the budget, so the author can shorten in one
9716        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
9717        // bare-`:nome` axis.
9718        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9719        let c = caixa_with_nome(&over);
9720        let err = c.validate_nome_chart_name_budget().unwrap_err();
9721        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9722            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
9723        };
9724        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9725        assert_eq!(nome, over);
9726        assert!(
9727            reason.contains("63") && reason.contains("64") && reason.contains("55"),
9728            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
9729             and the per-`:nome` budget (55), got {reason:?}"
9730        );
9731    }
9732
9733    #[test]
9734    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
9735        // The 63-byte `:nome` boundary — passes the bare-`:nome`
9736        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
9737        // joint chart name that overflows the DNS-1123 label cap
9738        // structurally. The most stringent fail-before-pass-after
9739        // surface: every `:nome` in the 56..=63-byte range passed the
9740        // prior cascade and broke at admission.
9741        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9742        let c = caixa_with_nome(&bare_max);
9743        // The bare-`:nome` gate accepts the 63-byte length.
9744        c.validate_nome().unwrap();
9745        // The new joint-length gate rejects it.
9746        let err = c.validate_nome_chart_name_budget().unwrap_err();
9747        assert!(
9748            matches!(
9749                err,
9750                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
9751                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
9752            ),
9753            "got {err:?}"
9754        );
9755    }
9756
9757    #[test]
9758    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
9759        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
9760        // name appears verbatim in the diagnostic so the author sees
9761        // exactly the string the apiserver / `helm lint` would have
9762        // rejected — no re-derivation required to grep the source.
9763        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
9764        // on the bare-`:nome` axis.
9765        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
9766        let c = caixa_with_nome(&over);
9767        let err = c.validate_nome_chart_name_budget().unwrap_err();
9768        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9769            panic!("expected NomeChartNameBudgetExceeded variant");
9770        };
9771        assert_eq!(nome, over);
9772        let expected_chart = crate::lareira_chart_name(&over);
9773        assert!(
9774            reason.contains(&expected_chart),
9775            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
9776             got {reason:?}"
9777        );
9778        assert!(
9779            reason.contains("lareira-"),
9780            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
9781        );
9782    }
9783
9784    #[test]
9785    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
9786        // Order pin on the layout cascade: the narrower
9787        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
9788        // joint-length budget. A structurally-malformed `:nome` (here:
9789        // uppercase) surfaces its specific shape error rather than
9790        // the chart-name-budget error, even when the joint length
9791        // would also overflow — the narrower diagnostic is more
9792        // self-locating. Mirrors the cascade-precedence pins peer
9793        // gates already use (e.g. `EntradaParaEmpty` before
9794        // `EntradaParaInvalid`).
9795        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9796        let c = caixa_with_nome(&over);
9797        // The bare-shape gate fires first.
9798        let err = c.validate_nome().unwrap_err();
9799        assert!(
9800            matches!(err, ManifestError::NomeInvalid { .. }),
9801            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
9802        );
9803        // And the layout verify cascade surfaces that diagnostic, not
9804        // the budget arm. Inject a path-exists oracle so the cascade
9805        // gets past the manifest-presence check and into the
9806        // value-shape gates.
9807        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9808        let err = crate::LayoutInvariants::verify(
9809            &layout,
9810            &c,
9811            std::path::Path::new("/tmp/caixa-test-fake-root"),
9812        )
9813        .unwrap_err();
9814        let issue = err.to_string();
9815        assert!(
9816            issue.contains("DNS-1123") || issue.contains("uppercase"),
9817            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
9818             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
9819        );
9820    }
9821
9822    #[test]
9823    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
9824        // Cross-axis envelope pin: the layout cascade wraps both
9825        // bare-`:nome` and joint-length-`:nome` failures through the
9826        // same [`LayoutError::NomeViolation`] envelope, since both
9827        // arms are on the `:nome` axis. The user's diagnostic stays
9828        // self-locating ("which axis"), and a future consumer that
9829        // dispatches on the layout-error variant (e.g. a `feira lint`
9830        // exit-code mapping) sees a single per-axis envelope. The
9831        // wrapped `issue:` carries the full inner diagnostic.
9832        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9833        let c = caixa_with_nome(&over);
9834        // The bare-shape gate accepts.
9835        c.validate_nome().unwrap();
9836        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9837        let err = crate::LayoutInvariants::verify(
9838            &layout,
9839            &c,
9840            std::path::Path::new("/tmp/caixa-test-fake-root"),
9841        )
9842        .unwrap_err();
9843        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
9844            panic!("expected LayoutError::NomeViolation, got {err:?}");
9845        };
9846        assert_eq!(caixa, over);
9847        assert!(
9848            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
9849            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
9850        );
9851    }
9852
9853    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
9854
9855    fn caixa_with_versao(versao: &str) -> Caixa {
9856        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9857        c.versao = versao.to_string();
9858        c
9859    }
9860
9861    #[test]
9862    fn validate_versao_accepts_canonical_template() {
9863        // Positive control: the bare `feira init`-style template's
9864        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
9865        // must not regress this baseline shape. A future tightening of
9866        // the accepted set surfaces here as a test failure first.
9867        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9868        c.validate_versao().unwrap();
9869    }
9870
9871    #[test]
9872    fn validate_versao_accepts_canonical_forms() {
9873        // Positive-set sweep: each realistic SemVer-2 shape the
9874        // substrate's downstream consumers accept must pass — bare
9875        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
9876        // build metadata (`+build.42`), the combined form, and the
9877        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
9878        // the peer `:nome` axis (6c992f8).
9879        for versao in [
9880            "0.1.0",
9881            "0.0.0",
9882            "1.0.0",
9883            "0.2.0-rc.1",
9884            "1.0.0-alpha.0",
9885            "1.0.0+build.42",
9886            "1.0.0-rc.1+build.42",
9887            "10.20.30",
9888        ] {
9889            caixa_with_versao(versao)
9890                .validate_versao()
9891                .unwrap_or_else(|e| {
9892                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
9893                });
9894        }
9895    }
9896
9897    #[test]
9898    fn validate_versao_rejects_empty() {
9899        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9900        // an empty `:versao` (the derive macro stores the raw String);
9901        // the gate's empty arm names the offending axis with a narrower
9902        // diagnostic than the `VersaoInvalid` parse arm would emit.
9903        // Mirrors `validate_nome_rejects_empty` (6c992f8).
9904        let c = caixa_with_versao("");
9905        let err = c.validate_versao().unwrap_err();
9906        assert_eq!(err, ManifestError::VersaoEmpty);
9907    }
9908
9909    #[test]
9910    fn validate_versao_rejects_git_tag_shape() {
9911        // The canonical "I copied the git tag verbatim" footgun —
9912        // `feira publish` *emits* `v<versao>` git tags, so a leaked
9913        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
9914        // shift every downstream consumer's version axis. `semver`
9915        // rejects the leading `v` at parse time; the gate moves the
9916        // diagnostic to the source `caixa.lisp`.
9917        let c = caixa_with_versao("v0.1.0");
9918        let err = c.validate_versao().unwrap_err();
9919        let ManifestError::VersaoInvalid { versao, reason } = err else {
9920            panic!("expected VersaoInvalid for git-tag-shape :versao");
9921        };
9922        assert_eq!(versao, "v0.1.0");
9923        assert!(
9924            !reason.is_empty(),
9925            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
9926        );
9927    }
9928
9929    #[test]
9930    fn validate_versao_rejects_missing_patch() {
9931        // The canonical "I shortened it" footgun — SemVer-2 requires
9932        // three parts. Cargo's `version =` field accepts the shortened
9933        // form as a requirement, conflating the two leaks across the
9934        // typed `:deps :versao` vs top-level `:versao` axes; the gate
9935        // pins the top-level axis to the strict three-part shape.
9936        let c = caixa_with_versao("0.1");
9937        let err = c.validate_versao().unwrap_err();
9938        assert!(
9939            matches!(
9940                err,
9941                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
9942            ),
9943            "got {err:?}"
9944        );
9945    }
9946
9947    #[test]
9948    fn validate_versao_rejects_requirement_shape() {
9949        // The canonical "I leaked a requirement into a version" footgun —
9950        // the typed `:deps :versao` / `:membros :versao` axes accept
9951        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
9952        // concrete `Version`. Without this gate the two typed surfaces
9953        // would silently overlap, and a top-level `^0.1` would surface
9954        // at `helm install` time as a Chart.yaml version rejection far
9955        // from the source `caixa.lisp`.
9956        let c = caixa_with_versao("^0.1");
9957        let err = c.validate_versao().unwrap_err();
9958        assert!(
9959            matches!(
9960                err,
9961                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
9962            ),
9963            "got {err:?}"
9964        );
9965    }
9966
9967    #[test]
9968    fn validate_versao_rejects_docker_tag_shape() {
9969        // The "I confused it with a docker tag" footgun — `latest`,
9970        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
9971        // SemVer rejects at parse time; the gate moves the diagnostic
9972        // to the source `caixa.lisp`.
9973        for bad in ["latest", "main", "stable"] {
9974            let c = caixa_with_versao(bad);
9975            let err = c.validate_versao().unwrap_err();
9976            assert!(
9977                matches!(
9978                    err,
9979                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
9980                ),
9981                "got {err:?} for {bad:?}"
9982            );
9983        }
9984    }
9985
9986    #[test]
9987    fn validate_versao_rejects_four_part_form() {
9988        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
9989        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
9990        // semver crate rejects the extra `.0` at parse time.
9991        let c = caixa_with_versao("0.1.0.0");
9992        let err = c.validate_versao().unwrap_err();
9993        assert!(
9994            matches!(
9995                err,
9996                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
9997            ),
9998            "got {err:?}"
9999        );
10000    }
10001
10002    #[test]
10003    fn versao_empty_takes_precedence_over_invalid() {
10004        // Order pin: the empty arm fires before the parser is consulted.
10005        // Empty < invalid in self-locating-ness — the narrower
10006        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
10007        // reference into the parser-shaped reason. Mirrors
10008        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
10009        // peer axis.
10010        let c = caixa_with_versao("");
10011        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
10012    }
10013
10014    #[test]
10015    fn versao_invalid_diagnostic_carries_offending_versao() {
10016        // Diagnostic-shape pin: the error names the offending `:versao`
10017        // verbatim with a non-empty parser-shaped reason, so a `feira
10018        // lint` run can render the diagnostic without re-parsing.
10019        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
10020        let c = caixa_with_versao("v0.1.0");
10021        let err = c.validate_versao().unwrap_err();
10022        let ManifestError::VersaoInvalid { versao, reason } = err else {
10023            panic!("expected VersaoInvalid variant");
10024        };
10025        assert_eq!(versao, "v0.1.0");
10026        assert!(
10027            !reason.is_empty(),
10028            "VersaoInvalid `reason` must carry the parser's wording verbatim"
10029        );
10030    }
10031
10032    #[test]
10033    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
10034        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
10035        // for `:upgrade-from :from` must also pass `validate_versao` —
10036        // the two `:versao`-typed surfaces (top-level `:versao`,
10037        // `:upgrade-from :from`) consume the *same* `semver::Version`
10038        // parser, so they must agree on the accepted set. Without this
10039        // pin, a future tightening of one axis could silently diverge
10040        // from the other. Mirrors the `:versao` requirement-axis
10041        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
10042        // commits established.
10043        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
10044            // From the canonical UpgradeFromEntry round-trip fixture
10045            // (`upgrade::tests::round_trip_load_module` peers).
10046            let entry = crate::UpgradeFromEntry {
10047                from: versao.to_string(),
10048                instructions: Vec::new(),
10049            };
10050            entry
10051                .validate()
10052                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
10053            caixa_with_versao(versao)
10054                .validate_versao()
10055                .unwrap_or_else(|e| {
10056                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
10057                });
10058        }
10059    }
10060
10061    // ── Caixa::validate_restart_window — supervisor restart-window
10062    //    folds through the shared `supervisor::duration_codec` ────────
10063
10064    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
10065        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
10066        c.kind = CaixaKind::Supervisor;
10067        c.restart_window = window.map(str::to_string);
10068        c
10069    }
10070
10071    #[test]
10072    fn validate_restart_window_accepts_none() {
10073        // The canonical "omit the slot to express no reset" shape — a
10074        // `None` raw string is the absence of the typed
10075        // `:restart-window` slot, which is exactly the SupervisorSpec
10076        // "never reset" semantics. The gate must be a no-op here; a
10077        // future tightening that rejected `None` would force every
10078        // supervisor caixa to authoring-time pin a window even when
10079        // the OTP semantics call for none.
10080        caixa_with_restart_window(None)
10081            .validate_restart_window()
10082            .unwrap();
10083    }
10084
10085    #[test]
10086    fn validate_restart_window_accepts_canonical_forms() {
10087        // Positive-set sweep across the canonical authoring units the
10088        // shared `supervisor::duration_codec::parse` accepts —
10089        // matches the codec-side `parse_accepts_integer_canonical_units`
10090        // pin in supervisor::tests so a future codec-side tightening
10091        // surfaces simultaneously on both axes.
10092        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
10093            caixa_with_restart_window(Some(window))
10094                .validate_restart_window()
10095                .unwrap_or_else(|e| {
10096                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
10097                });
10098        }
10099    }
10100
10101    #[test]
10102    fn validate_restart_window_rejects_fractional_seconds() {
10103        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
10104        // as f64 to 1.5 → renders back as `"1500ms"` on first
10105        // serialize). Prior to the fold + this gate, the inline
10106        // `parse_window_inline` accepted f64 magnitudes and silently
10107        // produced a `Duration::from_secs_f64(1.5)`, divergent from
10108        // the shared codec's integer-magnitude discipline on the
10109        // serde-routed siblings. The gate now surfaces a self-locating
10110        // diagnostic at the manifest layer.
10111        let err = caixa_with_restart_window(Some("1.5s"))
10112            .validate_restart_window()
10113            .unwrap_err();
10114        let ManifestError::RestartWindowMalformed {
10115            restart_window,
10116            reason,
10117        } = err
10118        else {
10119            panic!("expected RestartWindowMalformed for fractional seconds");
10120        };
10121        assert_eq!(restart_window, "1.5s");
10122        assert!(
10123            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
10124            "diagnostic must carry shared-codec wording, got {reason:?}"
10125        );
10126    }
10127
10128    #[test]
10129    fn validate_restart_window_rejects_decimal_shaped_integer() {
10130        // The `"1.0s"` class — numerically `1s` exactly, but the
10131        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
10132        // gets the same canonical-form diagnostic.
10133        let err = caixa_with_restart_window(Some("1.0s"))
10134            .validate_restart_window()
10135            .unwrap_err();
10136        assert!(
10137            matches!(
10138                err,
10139                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10140                    if restart_window == "1.0s"
10141            ),
10142            "got {err:?}"
10143        );
10144    }
10145
10146    #[test]
10147    fn validate_restart_window_rejects_half_unit_minute() {
10148        // `"0.5m"` is the unit-fraction footgun — author writes a
10149        // human-readable half-minute, the prior inline parser silently
10150        // produced `Duration::from_secs_f64(30.0)` and serde
10151        // re-emitted as `"30s"`, rewriting author intent. The gate
10152        // closes the loop at the manifest layer.
10153        let err = caixa_with_restart_window(Some("0.5m"))
10154            .validate_restart_window()
10155            .unwrap_err();
10156        let ManifestError::RestartWindowMalformed {
10157            restart_window,
10158            reason,
10159        } = err
10160        else {
10161            panic!("expected RestartWindowMalformed");
10162        };
10163        assert_eq!(restart_window, "0.5m");
10164        assert!(
10165            reason.contains("\"30s\""),
10166            "diagnostic must point at the canonical-form remediation, got {reason:?}"
10167        );
10168    }
10169
10170    #[test]
10171    fn validate_restart_window_rejects_leading_sign() {
10172        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
10173        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
10174        // and was caught by the `num < 0.0` arm which silently
10175        // returned `None`, dropping the author-supplied window). The
10176        // shared codec's digit-only gate rejects both with a unified
10177        // canonical-form diagnostic; the manifest-layer wrapper names
10178        // the offending value.
10179        for bad in ["+30s", "-30s"] {
10180            let err = caixa_with_restart_window(Some(bad))
10181                .validate_restart_window()
10182                .unwrap_err();
10183            assert!(
10184                matches!(
10185                    err,
10186                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
10187                        if restart_window == bad
10188                ),
10189                "got {err:?} for {bad:?}"
10190            );
10191        }
10192    }
10193
10194    #[test]
10195    fn validate_restart_window_rejects_unknown_unit() {
10196        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
10197        // unit dispatch surfaces an `unknown duration unit` reason;
10198        // the manifest-layer wrapper names the offending value.
10199        let err = caixa_with_restart_window(Some("30x"))
10200            .validate_restart_window()
10201            .unwrap_err();
10202        let ManifestError::RestartWindowMalformed {
10203            restart_window,
10204            reason,
10205        } = err
10206        else {
10207            panic!("expected RestartWindowMalformed for unknown unit");
10208        };
10209        assert_eq!(restart_window, "30x");
10210        assert!(
10211            reason.contains("unknown duration unit"),
10212            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
10213        );
10214    }
10215
10216    #[test]
10217    fn validate_restart_window_rejects_garbage() {
10218        // Pure non-numeric magnitude (`"abc"`) falls through to the
10219        // shared codec's narrower `"bad duration magnitude"` arm. Same
10220        // diagnostic shape as the codec-side
10221        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
10222        let err = caixa_with_restart_window(Some("abc"))
10223            .validate_restart_window()
10224            .unwrap_err();
10225        let ManifestError::RestartWindowMalformed {
10226            restart_window,
10227            reason,
10228        } = err
10229        else {
10230            panic!("expected RestartWindowMalformed for garbage");
10231        };
10232        assert_eq!(restart_window, "abc");
10233        assert!(
10234            reason.contains("bad duration magnitude"),
10235            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
10236        );
10237    }
10238
10239    #[test]
10240    fn validate_restart_window_rejects_empty_string() {
10241        // The empty-after-trim edge case — distinct from the `None`
10242        // canonical "omit the slot" shape. The shared codec's
10243        // digit-only gate refuses an empty magnitude; the manifest
10244        // layer names the offending `""` so the author can grep for
10245        // the literal empty value in their `caixa.lisp` and either
10246        // remove the slot (the canonical "no reset" shape) or pin a
10247        // positive duration.
10248        let err = caixa_with_restart_window(Some(""))
10249            .validate_restart_window()
10250            .unwrap_err();
10251        assert!(
10252            matches!(
10253                err,
10254                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10255                    if restart_window.is_empty()
10256            ),
10257            "got {err:?}"
10258        );
10259    }
10260
10261    #[test]
10262    fn validate_restart_window_diagnostic_carries_offending_value() {
10263        // Diagnostic-shape pin (peer with
10264        // `nome_invalid_diagnostic_carries_offending_nome` /
10265        // `versao_invalid_diagnostic_carries_offending_versao`): the
10266        // error names the offending raw `:restart-window` verbatim
10267        // with a non-empty shared-codec-shaped reason, so a `feira
10268        // lint` run can render the diagnostic without re-parsing.
10269        let err = caixa_with_restart_window(Some("1.5s"))
10270            .validate_restart_window()
10271            .unwrap_err();
10272        let ManifestError::RestartWindowMalformed {
10273            restart_window,
10274            reason,
10275        } = err
10276        else {
10277            panic!("expected RestartWindowMalformed variant");
10278        };
10279        assert_eq!(restart_window, "1.5s");
10280        assert!(
10281            !reason.is_empty(),
10282            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
10283        );
10284    }
10285
10286    #[test]
10287    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
10288        // Behavioral parity pin after the fold (`parse_window_inline`
10289        // deletion): the canonical `"60s"` still produces
10290        // `Duration::from_secs(60)` on the typed view — the fold is
10291        // semantically equivalent to the prior inline parser on the
10292        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
10293        // pin, narrowed to the parser-side contract.
10294        let c = caixa_with_restart_window(Some("60s"));
10295        let view = c.supervisor_view().expect("Supervisor kind has a view");
10296        assert_eq!(
10297            view.restart_window,
10298            Some(std::time::Duration::from_secs(60))
10299        );
10300    }
10301
10302    #[test]
10303    fn supervisor_view_soft_swallows_what_validate_rejects() {
10304        // Parity pin between the view-construction path and the
10305        // manifest-level validator: the same `"1.5s"` that surfaces
10306        // `RestartWindowMalformed` at `validate_restart_window` time
10307        // becomes `restart_window: None` on the typed view (the fold
10308        // preserves the existing best-effort shape of `supervisor_view`).
10309        // The contract is: a layout-verifier / `feira lint` flow that
10310        // cares about the malformed-window axis MUST consult
10311        // `validate_restart_window` — relying solely on the view's
10312        // `None` swallows the diagnostic silently. This pin makes the
10313        // expectation a typed invariant.
10314        let c = caixa_with_restart_window(Some("1.5s"));
10315        let view = c.supervisor_view().expect("Supervisor kind has a view");
10316        assert_eq!(
10317            view.restart_window, None,
10318            "view-construction path soft-swallows the parse error to None"
10319        );
10320        // And the manifest-level validator does NOT soft-swallow:
10321        assert!(
10322            matches!(
10323                c.validate_restart_window().unwrap_err(),
10324                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10325                    if restart_window == "1.5s"
10326            ),
10327            "validator must surface the offending value",
10328        );
10329    }
10330
10331    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
10332
10333    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
10334        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10335        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
10336        c.exe = exe.into_iter().map(String::from).collect();
10337        c.servicos = servicos.into_iter().map(String::from).collect();
10338        c
10339    }
10340
10341    #[test]
10342    fn validate_code_paths_accepts_canonical_template() {
10343        // The bare `Caixa::template` shape is the gate's identity element
10344        // on the canonical authoring shape — `:bibliotecas
10345        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
10346        // that the gate is non-disruptive against every existing caixa.
10347        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10348        c.validate_code_paths().unwrap();
10349    }
10350
10351    #[test]
10352    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
10353        // Positive control sweep: a canonical-shaped path on every slot
10354        // passes. Mirrors the peer
10355        // `behavior::validate_every_slot_relative_is_ok` pin.
10356        let c = caixa_with_code_paths(
10357            vec!["lib/demo.lisp", "lib/helpers.lisp"],
10358            vec!["exe/demo", "exe/tool"],
10359            vec!["servicos/demo.computeunit.yaml"],
10360        );
10361        c.validate_code_paths().unwrap();
10362    }
10363
10364    #[test]
10365    fn validate_code_paths_accepts_all_empty_lists() {
10366        // The empty-list identity element: every Caixa with no declared
10367        // code paths trivially passes (Supervisor / Aplicacao kinds rely
10368        // on this — the OwnCode gate already rejected them before the
10369        // path-shape gate runs in the layout, but the validator itself
10370        // must accept the empty shape).
10371        let c = caixa_with_code_paths(vec![], vec![], vec![]);
10372        c.validate_code_paths().unwrap();
10373    }
10374
10375    #[test]
10376    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
10377        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10378        let err = c.validate_code_paths().unwrap_err();
10379        assert!(
10380            matches!(
10381                err,
10382                ManifestError::CodePathEmpty {
10383                    slot: ":bibliotecas"
10384                }
10385            ),
10386            "got {err:?}",
10387        );
10388    }
10389
10390    #[test]
10391    fn validate_code_paths_rejects_empty_exe_entry() {
10392        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
10393        let err = c.validate_code_paths().unwrap_err();
10394        assert!(
10395            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
10396            "got {err:?}",
10397        );
10398    }
10399
10400    #[test]
10401    fn validate_code_paths_rejects_empty_servicos_entry() {
10402        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
10403        let err = c.validate_code_paths().unwrap_err();
10404        assert!(
10405            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
10406            "got {err:?}",
10407        );
10408    }
10409
10410    #[test]
10411    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
10412        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
10413        // so an absolute path that resolves on disk silently passes the
10414        // layout's existence check — the canonical sandbox-escape on
10415        // the biblioteca axis.
10416        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10417        let err = c.validate_code_paths().unwrap_err();
10418        let ManifestError::CodePathAbsolute { slot, path } = err else {
10419            panic!("expected CodePathAbsolute, got {err:?}");
10420        };
10421        assert_eq!(slot, ":bibliotecas");
10422        assert_eq!(path, PathBuf::from("/etc/passwd"));
10423    }
10424
10425    #[test]
10426    fn validate_code_paths_rejects_absolute_exe_entry() {
10427        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
10428        let err = c.validate_code_paths().unwrap_err();
10429        let ManifestError::CodePathAbsolute { slot, path } = err else {
10430            panic!("expected CodePathAbsolute, got {err:?}");
10431        };
10432        assert_eq!(slot, ":exe");
10433        assert_eq!(path, PathBuf::from("/usr/bin/env"));
10434    }
10435
10436    #[test]
10437    fn validate_code_paths_rejects_absolute_servicos_entry() {
10438        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
10439        let err = c.validate_code_paths().unwrap_err();
10440        let ManifestError::CodePathAbsolute { slot, path } = err else {
10441            panic!("expected CodePathAbsolute, got {err:?}");
10442        };
10443        assert_eq!(slot, ":servicos");
10444        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
10445    }
10446
10447    #[test]
10448    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
10449        // Canonical "I want a lib from a sibling caixa" footgun on the
10450        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
10451        // downstream, so a leading `..` traverses to the parent of the
10452        // caixa root with no diagnostic at layout time if the resolved
10453        // target exists.
10454        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
10455        let err = c.validate_code_paths().unwrap_err();
10456        let ManifestError::CodePathParentEscape { slot, path } = err else {
10457            panic!("expected CodePathParentEscape, got {err:?}");
10458        };
10459        assert_eq!(slot, ":bibliotecas");
10460        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
10461    }
10462
10463    #[test]
10464    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
10465        // Mid-path `..` defeats the layout's component-aware
10466        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
10467        // `starts_with(<root>/exe)` is true, but the canonical resolution
10468        // lives outside the caixa root. Caught regardless of where the
10469        // `..` sits — mirrors the peer
10470        // `behavior::validate_rejects_parent_escape_mid_path` pin.
10471        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
10472        let err = c.validate_code_paths().unwrap_err();
10473        let ManifestError::CodePathParentEscape { slot, path } = err else {
10474            panic!("expected CodePathParentEscape, got {err:?}");
10475        };
10476        assert_eq!(slot, ":exe");
10477        assert_eq!(path, PathBuf::from("exe/../../escape"));
10478    }
10479
10480    #[test]
10481    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
10482        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
10483        let err = c.validate_code_paths().unwrap_err();
10484        let ManifestError::CodePathParentEscape { slot, path } = err else {
10485            panic!("expected CodePathParentEscape, got {err:?}");
10486        };
10487        assert_eq!(slot, ":servicos");
10488        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
10489    }
10490
10491    #[test]
10492    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
10493        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
10494        // `:servicos`. A manifest with malformed entries on all three
10495        // surfaces surfaces the `:bibliotecas` defect first, mirroring
10496        // the canonical declaration order
10497        // `Caixa::declared_foreign_code_slots` already establishes for
10498        // the foreign-code-slot diagnostic.
10499        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
10500        let err = c.validate_code_paths().unwrap_err();
10501        assert!(
10502            matches!(
10503                err,
10504                ManifestError::CodePathEmpty {
10505                    slot: ":bibliotecas"
10506                }
10507            ),
10508            "got {err:?}",
10509        );
10510    }
10511
10512    #[test]
10513    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
10514        // Within-slot precedence pin: empty → absolute → parent-escape,
10515        // matching the [`PathShapeViolation`] arm-ordering every peer
10516        // `is_sandboxed_relative_path` caller follows (b0c8389
10517        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
10518        // `:bibliotecas` list whose first entry is empty *and* whose
10519        // later entries are absolute/parent-escape surfaces the empty
10520        // arm first, on the lexicographically-earliest offending entry.
10521        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
10522        let err = c.validate_code_paths().unwrap_err();
10523        assert!(
10524            matches!(
10525                err,
10526                ManifestError::CodePathEmpty {
10527                    slot: ":bibliotecas"
10528                }
10529            ),
10530            "got {err:?}",
10531        );
10532    }
10533
10534    #[test]
10535    fn validate_code_paths_first_offender_per_slot_wins() {
10536        // Within a single slot, the first declaration-order offender
10537        // surfaces — pins that the gate is left-to-right deterministic
10538        // (peer of every `*_first_collision_*` pin on duplicate gates).
10539        let c = caixa_with_code_paths(
10540            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
10541            vec![],
10542            vec![],
10543        );
10544        let err = c.validate_code_paths().unwrap_err();
10545        let ManifestError::CodePathAbsolute { slot, path } = err else {
10546            panic!("expected CodePathAbsolute, got {err:?}");
10547        };
10548        assert_eq!(slot, ":bibliotecas");
10549        assert_eq!(path, PathBuf::from("/etc/escape"));
10550    }
10551
10552    #[test]
10553    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
10554        // Diagnostic-shape pin (peer with
10555        // `nome_invalid_diagnostic_carries_offending_nome` /
10556        // `versao_invalid_diagnostic_carries_offending_versao`): the
10557        // error's Display surfaces both the offending `:slot` tag and
10558        // the offending path verbatim, so a `feira lint` run can render
10559        // the diagnostic without re-parsing.
10560        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10561        let rendered = c.validate_code_paths().unwrap_err().to_string();
10562        assert!(
10563            rendered.contains(":bibliotecas"),
10564            "diagnostic must name the offending slot: {rendered}",
10565        );
10566        assert!(
10567            rendered.contains("/etc/passwd"),
10568            "diagnostic must quote the offending path: {rendered}",
10569        );
10570    }
10571
10572    #[test]
10573    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
10574        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
10575        // axis. Without the gate `feira build` re-parses the same lib
10576        // twice, wasting work and silently masking the author's intent
10577        // to declare a *second* biblioteca.
10578        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
10579        let err = c.validate_code_paths().unwrap_err();
10580        let ManifestError::CodePathDuplicate { slot, path } = err else {
10581            panic!("expected CodePathDuplicate, got {err:?}");
10582        };
10583        assert_eq!(slot, ":bibliotecas");
10584        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
10585    }
10586
10587    #[test]
10588    fn validate_code_paths_rejects_duplicate_exe_entry() {
10589        // Same footgun on the Binario surface. The future `caixa-flake`
10590        // emitter that materializes each `:exe` entry as a flake
10591        // `packages.<name>` derivation would collide on the duplicate
10592        // package key — surfaced here at the typed-validate layer with a
10593        // self-locating diagnostic instead.
10594        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
10595        let err = c.validate_code_paths().unwrap_err();
10596        let ManifestError::CodePathDuplicate { slot, path } = err else {
10597            panic!("expected CodePathDuplicate, got {err:?}");
10598        };
10599        assert_eq!(slot, ":exe");
10600        assert_eq!(path, PathBuf::from("exe/cli"));
10601    }
10602
10603    #[test]
10604    fn validate_code_paths_rejects_duplicate_servicos_entry() {
10605        // Same footgun on the Servico surface. The peer caixa-helm /
10606        // caixa-flux renderers refuse `:servicos.len() != 1` with the
10607        // narrower `UnsupportedServicoCount` diagnostic, but that
10608        // diagnostic surfaces "too many servicos" without naming
10609        // "duplicate entry" — the typed self-locating framing only lands
10610        // at this gate.
10611        let c = caixa_with_code_paths(
10612            vec![],
10613            vec![],
10614            vec![
10615                "servicos/demo.computeunit.yaml",
10616                "servicos/demo.computeunit.yaml",
10617            ],
10618        );
10619        let err = c.validate_code_paths().unwrap_err();
10620        let ManifestError::CodePathDuplicate { slot, path } = err else {
10621            panic!("expected CodePathDuplicate, got {err:?}");
10622        };
10623        assert_eq!(slot, ":servicos");
10624        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
10625    }
10626
10627    #[test]
10628    fn validate_code_paths_accepts_same_path_across_slots() {
10629        // Per-list scope pin: a `:bibliotecas` entry that happens to
10630        // collide with an `:exe` or `:servicos` entry as a *string* is
10631        // not a duplicate by this gate (each list gets its own HashSet),
10632        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
10633        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
10634        // shape on the dep axis). The structural `starts_with(<exe |
10635        // servicos>_dir)` fence at layout time prevents the realistic
10636        // cross-slot collision case from existing on disk, but the gate's
10637        // per-list scope is correct independent of that downstream fence.
10638        let c = caixa_with_code_paths(
10639            vec!["lib/x.lisp"],
10640            vec!["exe/x"],
10641            vec!["servicos/x.computeunit.yaml"],
10642        );
10643        c.validate_code_paths().unwrap();
10644    }
10645
10646    #[test]
10647    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
10648        // Within-slot ordering pin: structural defects (empty / absolute
10649        // / parent-escape) fire before the duplicate gate on the same
10650        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
10651        // surfaces the narrower `CodePathEmpty` for the empty entry
10652        // first, not the duplicate on the later pair — same arm-ordering
10653        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
10654        // `:autores` 86c769b, `:deps` 359fba5).
10655        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
10656        let err = c.validate_code_paths().unwrap_err();
10657        assert!(
10658            matches!(
10659                err,
10660                ManifestError::CodePathEmpty {
10661                    slot: ":bibliotecas"
10662                }
10663            ),
10664            "got {err:?}",
10665        );
10666    }
10667
10668    #[test]
10669    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
10670        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
10671        // duplicates surface before `:exe` duplicates, matching the
10672        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
10673        // order every peer per-slot diagnostic on this surface follows.
10674        let c = caixa_with_code_paths(
10675            vec!["lib/x.lisp", "lib/x.lisp"],
10676            vec!["exe/y", "exe/y"],
10677            vec![],
10678        );
10679        let err = c.validate_code_paths().unwrap_err();
10680        let ManifestError::CodePathDuplicate { slot, path } = err else {
10681            panic!("expected CodePathDuplicate, got {err:?}");
10682        };
10683        assert_eq!(slot, ":bibliotecas");
10684        assert_eq!(path, PathBuf::from("lib/x.lisp"));
10685    }
10686
10687    #[test]
10688    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
10689        // Diagnostic-shape pin (peer with
10690        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10691        // on the structural arm): the duplicate-arm Display surfaces both
10692        // the offending `:slot` tag and the offending path verbatim, so a
10693        // `feira lint` run can render the diagnostic without re-parsing.
10694        let c = caixa_with_code_paths(
10695            vec![],
10696            vec![],
10697            vec![
10698                "servicos/demo.computeunit.yaml",
10699                "servicos/demo.computeunit.yaml",
10700            ],
10701        );
10702        let rendered = c.validate_code_paths().unwrap_err().to_string();
10703        assert!(
10704            rendered.contains(":servicos"),
10705            "diagnostic must name the offending slot: {rendered}",
10706        );
10707        assert!(
10708            rendered.contains("servicos/demo.computeunit.yaml"),
10709            "diagnostic must quote the offending path: {rendered}",
10710        );
10711    }
10712
10713    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
10714    //
10715    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
10716    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
10717    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
10718    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
10719    // at parse time — the same downstream consumer the peer `:behavior
10720    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
10721    // `:upgrade-from :state-change :script` (33cc830,
10722    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
10723    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
10724    // nix-built executable surface (`"exe/<name>"` shape per the canonical
10725    // [`crate::LayoutError::ExeOutsideDir`] error message and every
10726    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
10727    // is the `.computeunit.yaml` ComputeUnit-CR axis.
10728
10729    #[test]
10730    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
10731        // Canonical "I dragged the wrong file from the workspace tree"
10732        // footgun on the biblioteca axis. Without the gate `feira build`
10733        // hands the extensionless path to `tatara_lisp::read` and fails
10734        // with a parser-shaped diagnostic far from the source caixa.lisp,
10735        // with no field naming the offending `:bibliotecas` entry.
10736        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
10737            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10738            let err = c.validate_code_paths().unwrap_err();
10739            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10740                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10741            };
10742            assert_eq!(slot, ":bibliotecas");
10743            assert_eq!(path, PathBuf::from(relpath));
10744        }
10745    }
10746
10747    #[test]
10748    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
10749        // Wrong-extension sweep across common authoring footguns. Same
10750        // sweep posture as the peer
10751        // `behavior::validate_rejects_wrong_extension` (c97815a) and
10752        // `upgrade::tests::state_change_rejects_wrong_extension_script`
10753        // (33cc830) cases.
10754        for relpath in [
10755            "lib/demo.rs",
10756            "lib/demo.txt",
10757            "lib/demo.md",
10758            "lib/demo.json",
10759            "lib/demo.yaml",
10760            "lib/demo.toml",
10761            "lib/demo.lisp.bak",
10762            "lib/demo.lispx",
10763            "lib/demo.lis",
10764        ] {
10765            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10766            let err = c.validate_code_paths().unwrap_err();
10767            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10768                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10769            };
10770            assert_eq!(slot, ":bibliotecas");
10771            assert_eq!(path, PathBuf::from(relpath));
10772        }
10773    }
10774
10775    #[test]
10776    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
10777        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
10778        // contract. An uppercase `.LISP` shape that the layout's existence
10779        // check would (case-insensitively, on case-insensitive volumes)
10780        // match the on-disk file still mismatches the canonical form the
10781        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
10782        // contract. Mirrors the peer
10783        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
10784        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
10785        // (33cc830) sweeps.
10786        for relpath in [
10787            "lib/demo.LISP",
10788            "lib/demo.Lisp",
10789            "lib/demo.LiSp",
10790            "lib/demo.lISP",
10791        ] {
10792            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10793            let err = c.validate_code_paths().unwrap_err();
10794            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10795                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10796            };
10797            assert_eq!(slot, ":bibliotecas");
10798            assert_eq!(path, PathBuf::from(relpath));
10799        }
10800    }
10801
10802    #[test]
10803    fn validate_code_paths_accepts_canonical_lisp_shapes() {
10804        // Positive-control sweep through every canonical authoring shape
10805        // every in-tree fixture and the `Caixa::template` scaffold use.
10806        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
10807        // (c97815a) and the lifted predicate's own
10808        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
10809        // (33cc830).
10810        for relpath in [
10811            "lib/demo.lisp",
10812            "lib/handlers.lisp",
10813            "lib/migrations/v01-to-v02.lisp",
10814            "demo.lisp",
10815            "a.lisp",
10816            "./lib/demo.lisp",
10817            "lib/./handlers.lisp",
10818            "lib/migrations/v.0.1.lisp",
10819        ] {
10820            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10821            c.validate_code_paths()
10822                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10823        }
10824    }
10825
10826    #[test]
10827    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
10828        // The file-type gate is per-slot — only `:bibliotecas` carries the
10829        // tatara-lisp-source contract. An extensionless `:exe` entry
10830        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
10831        // canonical shapes every in-tree fixture uses, and must continue
10832        // to pass validate. Pins that a future tightening that broadens
10833        // the `.lisp` gate to either axis surfaces as a test failure
10834        // rather than as a silent breaking change to existing valid
10835        // manifests.
10836        let c = caixa_with_code_paths(
10837            vec![],
10838            vec!["exe/demo", "exe/tool"],
10839            vec!["servicos/demo.computeunit.yaml"],
10840        );
10841        c.validate_code_paths().unwrap();
10842    }
10843
10844    #[test]
10845    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
10846        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
10847        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
10848        // sandbox-shape diagnostic first (the `.lisp` remediation would
10849        // be misleading when the offending path can never resolve under
10850        // the caixa root anyway). Mirrors the peer
10851        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
10852        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
10853        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
10854        // on `:upgrade-from :state-change :script` (33cc830).
10855        //
10856        // Empty wins (the strictly-smaller-scope structural arm).
10857        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10858        assert!(
10859            matches!(
10860                c.validate_code_paths().unwrap_err(),
10861                ManifestError::CodePathEmpty {
10862                    slot: ":bibliotecas"
10863                }
10864            ),
10865            "empty must win over non-lisp-extension",
10866        );
10867        // Absolute wins (the path can't resolve under the caixa root).
10868        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10869        let err = c.validate_code_paths().unwrap_err();
10870        let ManifestError::CodePathAbsolute { slot, .. } = err else {
10871            panic!("absolute must win over non-lisp-extension, got {err:?}");
10872        };
10873        assert_eq!(slot, ":bibliotecas");
10874        // ParentEscape wins (the path escapes the caixa root).
10875        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
10876        let err = c.validate_code_paths().unwrap_err();
10877        let ManifestError::CodePathParentEscape { slot, .. } = err else {
10878            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
10879        };
10880        assert_eq!(slot, ":bibliotecas");
10881    }
10882
10883    #[test]
10884    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
10885        // Within-slot precedence pin: the per-entry file-type shape gate
10886        // fires before the cross-entry duplicate gate, so the narrower
10887        // structural defect dominates the uniqueness diagnostic. A
10888        // `("lib/x.txt" "lib/x.txt")` shape surfaces
10889        // `CodePathNonLispExtension` on the first entry rather than
10890        // `CodePathDuplicate` on the pair — same posture every per-entry
10891        // shape-gate-precedes-duplicate cascade follows on this surface
10892        // (the empty / absolute / parent-escape arms already precede the
10893        // duplicate arm; the lifted file-type arm joins that set).
10894        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
10895        let err = c.validate_code_paths().unwrap_err();
10896        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10897            panic!("expected CodePathNonLispExtension, got {err:?}");
10898        };
10899        assert_eq!(slot, ":bibliotecas");
10900        assert_eq!(path, PathBuf::from("lib/x.txt"));
10901    }
10902
10903    #[test]
10904    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
10905        // Diagnostic-shape pin (peer with
10906        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10907        // on the sandbox-shape arms and
10908        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
10909        // on the duplicate arm): the file-type-arm Display surfaces both
10910        // the offending `:slot` tag, the offending path verbatim, and the
10911        // expected `.lisp` extension named in the remediation text, so a
10912        // `feira lint` run can render the diagnostic without re-parsing.
10913        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
10914        let rendered = c.validate_code_paths().unwrap_err().to_string();
10915        assert!(
10916            rendered.contains(":bibliotecas"),
10917            "diagnostic must name the offending slot: {rendered}",
10918        );
10919        assert!(
10920            rendered.contains("lib/demo.rs"),
10921            "diagnostic must quote the offending path: {rendered}",
10922        );
10923        assert!(
10924            rendered.contains(".lisp"),
10925            "diagnostic must name the expected extension: {rendered}",
10926        );
10927    }
10928
10929    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
10930    //
10931    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
10932    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
10933    // contract. The peer caixa-helm / caixa-flux renderers consume each
10934    // `:servicos` entry through `serde_yaml::from_str` as a typed
10935    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
10936    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
10937    // axis `Path::extension` can't express on its own.
10938
10939    #[test]
10940    fn validate_code_paths_rejects_no_extension_servicos_entry() {
10941        // Canonical "I dragged the wrong file from the workspace tree"
10942        // footgun on the Servico axis. Without the gate the peer
10943        // caixa-helm / caixa-flux renderers hand the extensionless path
10944        // to `serde_yaml::from_str` and fail with a parser-shaped
10945        // diagnostic far from the source caixa.lisp, with no field
10946        // naming the offending `:servicos` entry.
10947        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
10948            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10949            let err = c.validate_code_paths().unwrap_err();
10950            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10951                panic!(
10952                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10953                     got {err:?}"
10954                );
10955            };
10956            assert_eq!(slot, ":servicos");
10957            assert_eq!(path, PathBuf::from(relpath));
10958        }
10959    }
10960
10961    #[test]
10962    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
10963        // Wrong-extension sweep across common authoring footguns on the
10964        // Servico axis. Bare `.yaml` is the canonical "I forgot the
10965        // `.computeunit` segment" typo; the off-by-one-segment shapes
10966        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
10967        // bare `Path::extension` view but mismatch the typed compound
10968        // suffix the renderers' `serde_yaml::from_str` consumer demands.
10969        // Same sweep-posture as the peer
10970        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
10971        // (64772a9) on the sibling tatara-lisp-source axis.
10972        for relpath in [
10973            "servicos/demo.yaml",
10974            "servicos/demo.yml",
10975            "servicos/demo.json",
10976            "servicos/demo.toml",
10977            "servicos/demo.txt",
10978            "servicos/demo.computeunit.yaml.bak",
10979            "servicos/demo.computeunit.yam",
10980            "servicos/demo.computeunit",
10981            "servicos/demo-computeunit.yaml",
10982            "servicos/demo_computeunit.yaml",
10983        ] {
10984            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10985            let err = c.validate_code_paths().unwrap_err();
10986            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10987                panic!(
10988                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10989                     got {err:?}"
10990                );
10991            };
10992            assert_eq!(slot, ":servicos");
10993            assert_eq!(path, PathBuf::from(relpath));
10994        }
10995    }
10996
10997    #[test]
10998    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
10999        // Case-sensitivity sweep — pins the strict lowercase
11000        // `.computeunit.yaml` contract. A case-folded shape that the
11001        // layout's existence check would (case-insensitively, on
11002        // case-insensitive volumes) match the on-disk file still
11003        // mismatches the canonical form the codec emits, breaking the
11004        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
11005        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
11006        // (64772a9) sweep on the sibling tatara-lisp-source axis.
11007        for relpath in [
11008            "servicos/demo.ComputeUnit.yaml",
11009            "servicos/demo.COMPUTEUNIT.yaml",
11010            "servicos/demo.computeunit.YAML",
11011            "servicos/demo.computeunit.Yaml",
11012            "servicos/demo.COMPUTEUNIT.YAML",
11013        ] {
11014            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
11015            let err = c.validate_code_paths().unwrap_err();
11016            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
11017                panic!(
11018                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
11019                     got {err:?}"
11020                );
11021            };
11022            assert_eq!(slot, ":servicos");
11023            assert_eq!(path, PathBuf::from(relpath));
11024        }
11025    }
11026
11027    #[test]
11028    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
11029        // Degenerate hidden-file shape: a file name exactly equal to the
11030        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
11031        // the structural "Servico declared with no identity" footgun.
11032        // The substrate identifies each ComputeUnit by the file-stem
11033        // segment that precedes `.computeunit.yaml` (the rendered
11034        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
11035        // the M3 `:contratos` membership lookup), so an empty stem
11036        // leaves the Servico unidentifiable. Pinned at the typed-axis
11037        // level so a future regression that drops the `name.len() >
11038        // SUFFIX.len()` bound at the predicate surfaces here, not
11039        // piecemeal as a `lareira-` chart-name collision at render time.
11040        for relpath in ["servicos/.computeunit.yaml"] {
11041            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
11042            let err = c.validate_code_paths().unwrap_err();
11043            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
11044                panic!(
11045                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
11046                     got {err:?}"
11047                );
11048            };
11049            assert_eq!(slot, ":servicos");
11050            assert_eq!(path, PathBuf::from(relpath));
11051        }
11052    }
11053
11054    #[test]
11055    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
11056        // Positive-control sweep through every canonical authoring shape
11057        // every in-tree fixture and the `Caixa::template` scaffold use.
11058        // Mirrors the peer
11059        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
11060        // and the lifted predicate's own
11061        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
11062        // render.rs.
11063        for relpath in [
11064            "servicos/demo.computeunit.yaml",
11065            "servicos/hello-rio.computeunit.yaml",
11066            "servicos/my-service.computeunit.yaml",
11067            "servicos/a.computeunit.yaml",
11068            "./servicos/demo.computeunit.yaml",
11069            "servicos/./demo.computeunit.yaml",
11070            "servicos/sub/nested.computeunit.yaml",
11071            "servicos/v0.1.computeunit.yaml",
11072        ] {
11073            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
11074            c.validate_code_paths()
11075                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
11076        }
11077    }
11078
11079    #[test]
11080    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
11081        // The file-type gate is per-slot — only `:servicos` carries the
11082        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
11083        // entry and an extensionless `:exe` entry are the canonical
11084        // shapes every in-tree fixture uses, and must continue to pass
11085        // validate. Peer of
11086        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
11087        // (64772a9) — together pin that the typed
11088        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
11089        // cross-axis leakage in either direction.
11090        let c = caixa_with_code_paths(
11091            vec!["lib/demo.lisp"],
11092            vec!["exe/demo", "exe/tool"],
11093            vec!["servicos/demo.computeunit.yaml"],
11094        );
11095        c.validate_code_paths().unwrap();
11096    }
11097
11098    #[test]
11099    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
11100        // Cross-arm precedence pin: a `:servicos` entry that is *both*
11101        // sandbox-escaping and wrong-extension surfaces the more
11102        // fundamental sandbox-shape diagnostic first (the
11103        // `.computeunit.yaml` remediation would be misleading when the
11104        // offending path can never resolve under the caixa root
11105        // anyway). Mirrors the peer
11106        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
11107        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
11108        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
11109        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
11110        // table establishes.
11111        //
11112        // Empty wins (the strictly-smaller-scope structural arm).
11113        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
11114        assert!(
11115            matches!(
11116                c.validate_code_paths().unwrap_err(),
11117                ManifestError::CodePathEmpty { slot: ":servicos" }
11118            ),
11119            "empty must win over non-computeunit-yaml-extension",
11120        );
11121        // Absolute wins (the path can't resolve under the caixa root).
11122        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
11123        let err = c.validate_code_paths().unwrap_err();
11124        let ManifestError::CodePathAbsolute { slot, .. } = err else {
11125            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
11126        };
11127        assert_eq!(slot, ":servicos");
11128        // ParentEscape wins (the path escapes the caixa root).
11129        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
11130        let err = c.validate_code_paths().unwrap_err();
11131        let ManifestError::CodePathParentEscape { slot, .. } = err else {
11132            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
11133        };
11134        assert_eq!(slot, ":servicos");
11135    }
11136
11137    #[test]
11138    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
11139        // Within-slot precedence pin: the per-entry file-type shape gate
11140        // fires before the cross-entry duplicate gate, so the narrower
11141        // structural defect dominates the uniqueness diagnostic. A
11142        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
11143        // `CodePathNonComputeUnitYamlExtension` on the first entry
11144        // rather than `CodePathDuplicate` on the pair — same posture
11145        // every per-entry shape-gate-precedes-duplicate cascade follows
11146        // on this surface, peer of the 64772a9 `:bibliotecas`
11147        // `("lib/x.txt" "lib/x.txt")` ordering.
11148        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
11149        let err = c.validate_code_paths().unwrap_err();
11150        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
11151            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
11152        };
11153        assert_eq!(slot, ":servicos");
11154        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
11155    }
11156
11157    #[test]
11158    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
11159     {
11160        // Diagnostic-shape pin (peer with
11161        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
11162        // on the sibling tatara-lisp-source axis): the file-type-arm
11163        // Display surfaces both the offending `:slot` tag, the
11164        // offending path verbatim, and the expected
11165        // `.computeunit.yaml` compound suffix named in the remediation
11166        // text, so a `feira lint` run can render the diagnostic without
11167        // re-parsing.
11168        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
11169        let rendered = c.validate_code_paths().unwrap_err().to_string();
11170        assert!(
11171            rendered.contains(":servicos"),
11172            "diagnostic must name the offending slot: {rendered}",
11173        );
11174        assert!(
11175            rendered.contains("servicos/demo.yaml"),
11176            "diagnostic must quote the offending path: {rendered}",
11177        );
11178        assert!(
11179            rendered.contains(".computeunit.yaml"),
11180            "diagnostic must name the expected compound suffix: {rendered}",
11181        );
11182    }
11183
11184    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
11185
11186    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
11187        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11188        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
11189        c
11190    }
11191
11192    #[test]
11193    fn validate_etiquetas_accepts_empty_list() {
11194        // The empty-list identity: every caixa with no declared tags
11195        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
11196        // so the gate is non-disruptive against every existing manifest.
11197        let c = caixa_with_etiquetas(vec![]);
11198        c.validate_etiquetas().unwrap();
11199    }
11200
11201    #[test]
11202    fn validate_etiquetas_accepts_canonical_forms() {
11203        // Positive control sweep: a canonical-shaped non-empty distinct
11204        // tag list passes, mirroring the example checkout-aplicacao
11205        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
11206        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
11207        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
11208        c.validate_etiquetas().unwrap();
11209    }
11210
11211    #[test]
11212    fn validate_etiquetas_rejects_empty_entry() {
11213        // Canonical paste-from-blank-doc footgun. Without the gate the
11214        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
11215        // no-op tag indexing nothing in the future caixa-registry.
11216        let c = caixa_with_etiquetas(vec![""]);
11217        let err = c.validate_etiquetas().unwrap_err();
11218        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11219    }
11220
11221    #[test]
11222    fn validate_etiquetas_rejects_duplicate_entry() {
11223        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
11224        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
11225        // collect at chart render — a "second wins / one silently
11226        // disappears" shape divergent from every peer typed-graph set
11227        // gate. The duplicate-arm names the offending tag verbatim.
11228        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
11229        let err = c.validate_etiquetas().unwrap_err();
11230        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
11231            panic!("expected EtiquetaDuplicate, got {err:?}");
11232        };
11233        assert_eq!(etiqueta, "demo");
11234    }
11235
11236    #[test]
11237    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
11238        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
11239        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
11240        // structural "this entry has no value" defect dominates the
11241        // cross-entry uniqueness diagnostic. Mirrors the peer
11242        // empty-before-duplicate cascades on `:caracteristicas`
11243        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11244        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
11245        // `MembroDuplicate`).
11246        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
11247        let err = c.validate_etiquetas().unwrap_err();
11248        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11249    }
11250
11251    #[test]
11252    fn validate_etiquetas_duplicate_reports_first_collision() {
11253        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11254        // duplicate (the lexicographically-earliest offending position
11255        // — the second `"a"` at index 2 collides with the first `"a"`
11256        // at index 0), not the later `"b"` collision at index 3,
11257        // peer with every other first-collision diagnostic posture on
11258        // this surface (`validate_load_singularity_reports_first_collision`,
11259        // `validate_cleanup_singularity_reports_first_collision`).
11260        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
11261        let err = c.validate_etiquetas().unwrap_err();
11262        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
11263            panic!("expected EtiquetaDuplicate, got {err:?}");
11264        };
11265        assert_eq!(etiqueta, "a");
11266    }
11267
11268    #[test]
11269    fn validate_etiquetas_case_sensitive() {
11270        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
11271        // mirroring the peer `:membros :caixa` / `:children :caixa`
11272        // exact-string-match discipline. The shape gate this routine
11273        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
11274        // grammar) accepts mixed case — crates.io's keyword rule is
11275        // "case-insensitive" at the index layer but admits mixed case
11276        // at the entry layer (the canonical Helm chart `keywords:`
11277        // shape is lowercase by convention, but the grammar admits
11278        // uppercase). Case-sensitivity at the duplicate-set layer
11279        // remains structural — two distinct strings are two distinct
11280        // entries.
11281        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
11282        c.validate_etiquetas().unwrap();
11283    }
11284
11285    #[test]
11286    fn validate_etiquetas_diagnostic_carries_offending_tag() {
11287        // Diagnostic-shape pin (peer with
11288        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
11289        // the error's Display surfaces the offending tag verbatim, so a
11290        // `feira lint` run can render the diagnostic without re-parsing
11291        // and the author can grep their caixa.lisp for the offending
11292        // value.
11293        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
11294        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11295        assert!(
11296            rendered.contains(":etiquetas"),
11297            "diagnostic must name the offending slot: {rendered}",
11298        );
11299        assert!(
11300            rendered.contains("demo"),
11301            "diagnostic must quote the offending tag: {rendered}",
11302        );
11303    }
11304
11305    #[test]
11306    fn validate_etiquetas_rejects_leading_whitespace_entry() {
11307        // Canonical paste-from-aligned-doc footgun. Without the shape
11308        // gate `" mesh"` silently passed validate and landed as a
11309        // YAML plain-style scalar with leading whitespace in the
11310        // rendered Chart.yaml `keywords:` array — every YAML 1.2
11311        // dumper trims leading whitespace from plain-style scalars,
11312        // so the authored space round-tripped inconsistently back
11313        // through `caixa.lisp`. Mirrors the peer
11314        // `validate_autores_rejects_leading_whitespace_entry`.
11315        let c = caixa_with_etiquetas(vec![" mesh"]);
11316        let err = c.validate_etiquetas().unwrap_err();
11317        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11318            panic!("expected EtiquetaInvalid, got {err:?}");
11319        };
11320        assert_eq!(etiqueta, " mesh");
11321        assert!(reason.contains("whitespace"), "got: {reason}");
11322    }
11323
11324    #[test]
11325    fn validate_etiquetas_rejects_embedded_newline_entry() {
11326        // Canonical paste-from-multiline-doc footgun — the author
11327        // pasted a multi-tag block into one `:etiquetas` entry
11328        // instead of splitting into one entry per tag. Without the
11329        // shape gate `"mesh\nhttp"` silently passed validate and
11330        // landed as a YAML-illegal multi-line scalar in the rendered
11331        // Chart.yaml `keywords:` array.
11332        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11333        let err = c.validate_etiquetas().unwrap_err();
11334        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11335            panic!("expected EtiquetaInvalid, got {err:?}");
11336        };
11337        assert_eq!(etiqueta, "mesh\nhttp");
11338        assert!(reason.contains("newline"), "got: {reason}");
11339    }
11340
11341    #[test]
11342    fn validate_etiquetas_rejects_embedded_comma_entry() {
11343        // Canonical CSV-list-separator-confusion footgun: the author
11344        // confused the CSV-style separator convention with the
11345        // `:etiquetas` list grammar. Without the shape gate
11346        // `"mesh,http,grpc"` silently passed validate and landed as a
11347        // single malformed search tag in the rendered Chart.yaml
11348        // `keywords:` array — Artifact Hub's keyword index would
11349        // either silently drop the tag or index it as
11350        // `mesh,http,grpc` instead of three separate tags.
11351        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
11352        let err = c.validate_etiquetas().unwrap_err();
11353        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11354            panic!("expected EtiquetaInvalid, got {err:?}");
11355        };
11356        assert_eq!(etiqueta, "mesh,http,grpc");
11357        assert!(reason.contains('`'), "got: {reason}");
11358        assert!(reason.contains(','), "got: {reason}");
11359    }
11360
11361    #[test]
11362    fn validate_etiquetas_rejects_embedded_slash_entry() {
11363        // Canonical path-separator-confusion footgun: the author
11364        // confused namespace-path notation with the keyword grammar.
11365        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
11366        let err = c.validate_etiquetas().unwrap_err();
11367        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11368            panic!("expected EtiquetaInvalid, got {err:?}");
11369        };
11370        assert_eq!(etiqueta, "caixa/servico");
11371        assert!(reason.contains('/'), "got: {reason}");
11372    }
11373
11374    #[test]
11375    fn validate_etiquetas_rejects_leading_digit_entry() {
11376        // Canonical paste-from-numbered-list footgun: the author
11377        // copied `1. mesh` from a numbered doc and the `1` leaked
11378        // into the tag.
11379        let c = caixa_with_etiquetas(vec!["1mesh"]);
11380        let err = c.validate_etiquetas().unwrap_err();
11381        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11382            panic!("expected EtiquetaInvalid, got {err:?}");
11383        };
11384        assert_eq!(etiqueta, "1mesh");
11385        assert!(reason.contains("digit"), "got: {reason}");
11386    }
11387
11388    #[test]
11389    fn validate_etiquetas_rejects_leading_hyphen_entry() {
11390        // Canonical kebab-leak footgun.
11391        let c = caixa_with_etiquetas(vec!["-foo"]);
11392        let err = c.validate_etiquetas().unwrap_err();
11393        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11394            panic!("expected EtiquetaInvalid, got {err:?}");
11395        };
11396        assert_eq!(etiqueta, "-foo");
11397        assert!(reason.contains('-'), "got: {reason}");
11398    }
11399
11400    #[test]
11401    fn validate_etiquetas_rejects_non_ascii_entry() {
11402        // Canonical paste-from-Unicode-doc footgun. Every legitimate
11403        // search tag is strict ASCII; raw non-ASCII silently
11404        // round-trips inconsistently across NFC/NFD normalization on
11405        // APFS / case-folding filesystems and breaks the Artifact Hub
11406        // keyword search index lookup.
11407        let c = caixa_with_etiquetas(vec!["café"]);
11408        let err = c.validate_etiquetas().unwrap_err();
11409        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11410            panic!("expected EtiquetaInvalid, got {err:?}");
11411        };
11412        assert_eq!(etiqueta, "café");
11413        assert!(reason.contains("non-ASCII"), "got: {reason}");
11414    }
11415
11416    #[test]
11417    fn validate_etiquetas_rejects_period_entry() {
11418        // Canonical namespace-confusion / version-suffix footgun
11419        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
11420        // excludes `.` from the continuation set even though the
11421        // sibling `:caracteristicas` axis (Cargo's feature-name
11422        // grammar) admits it. Tighter than the sibling axis, peer
11423        // with Cargo's own crates.io keyword shape.
11424        let c = caixa_with_etiquetas(vec!["http.1"]);
11425        let err = c.validate_etiquetas().unwrap_err();
11426        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11427            panic!("expected EtiquetaInvalid, got {err:?}");
11428        };
11429        assert_eq!(etiqueta, "http.1");
11430        assert!(reason.contains('.'), "got: {reason}");
11431    }
11432
11433    #[test]
11434    fn validate_etiquetas_empty_takes_precedence_over_shape() {
11435        // Per-entry empty-first cascade pin: an entry that is both
11436        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
11437        // narrower "this entry has no value" structural defect
11438        // dominates the broader shape-predicate diagnostic). The
11439        // empty arm fires before the shape predicate is consulted,
11440        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
11441        // cascade established on the sibling universal-axis Vec<String>
11442        // surface.
11443        let c = caixa_with_etiquetas(vec![""]);
11444        let err = c.validate_etiquetas().unwrap_err();
11445        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11446    }
11447
11448    #[test]
11449    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
11450        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11451        // entry that is malformed surfaces `EtiquetaInvalid` even when
11452        // a later entry would have collided on duplicate. The
11453        // per-entry shape arm fires inside the same loop iteration as
11454        // the empty arm, before the seen-set insert at end-of-iteration
11455        // — structural per-entry defects dominate the cross-entry
11456        // uniqueness diagnostic. Mirrors the peer
11457        // `validate_autores_shape_takes_precedence_over_duplicate`.
11458        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
11459        let err = c.validate_etiquetas().unwrap_err();
11460        assert!(
11461            matches!(err, ManifestError::EtiquetaInvalid { .. }),
11462            "got {err:?}",
11463        );
11464    }
11465
11466    #[test]
11467    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
11468        // Diagnostic-shape pin on the new shape arm (peer with
11469        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
11470        // the rendered Display surfaces both the offending slot name
11471        // and the offending value verbatim, so a `feira lint` run
11472        // points the author at the exact `:etiquetas` entry to fix.
11473        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11474        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11475        assert!(
11476            rendered.contains(":etiquetas"),
11477            "diagnostic must name the offending slot: {rendered}",
11478        );
11479        assert!(
11480            rendered.contains("mesh\\nhttp"),
11481            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11482        );
11483    }
11484
11485    #[test]
11486    fn validate_etiquetas_rejects_at_21_byte_boundary() {
11487        // The 20-byte cap pin — boundary-exceeding case rejected,
11488        // boundary-accepting case passes. Mirrors the peer
11489        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
11490        // side pin, surfaced at the per-axis caller so the cap
11491        // propagates through validate end-to-end. Constructed as a
11492        // single all-`a` token so only the cap arm fires.
11493        let max_ok = "a".repeat(20);
11494        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
11495        c.validate_etiquetas().unwrap();
11496        let too_long = "a".repeat(21);
11497        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
11498        let err = c.validate_etiquetas().unwrap_err();
11499        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
11500            panic!("expected EtiquetaInvalid, got {err:?}");
11501        };
11502        assert!(reason.contains("20"), "got: {reason}");
11503        assert!(reason.contains("21"), "got: {reason}");
11504    }
11505
11506    #[test]
11507    fn validate_etiquetas_accepts_canonical_shaped_forms() {
11508        // Positive control sweep: every canonical-shaped tag from the
11509        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
11510        // example fixtures plus the substrate-fixed tags caixa-helm
11511        // unions in at chart render. Drift between this list and the
11512        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
11513        // sweep surfaces here — one source of truth for the rule.
11514        let c = caixa_with_etiquetas(vec![
11515            "example",
11516            "aplicacao",
11517            "mesh",
11518            "ecommerce",
11519            "demo",
11520            "infrastructure",
11521            "aws",
11522            "akeyless",
11523            "pangea-native",
11524            "hello-world",
11525            "wasm",
11526            "rust",
11527            "tatara-lisp",
11528            "caixa-servico",
11529            "lareira",
11530        ]);
11531        c.validate_etiquetas().unwrap();
11532    }
11533
11534    // ── validate_autores — universal-axis maintainer shape ────────────
11535
11536    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
11537        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11538        c.autores = autores.into_iter().map(String::from).collect();
11539        c
11540    }
11541
11542    #[test]
11543    fn validate_autores_accepts_empty_list() {
11544        // The empty-list identity: `Caixa::template` emits `:autores ()`,
11545        // so the gate is non-disruptive against every existing manifest.
11546        let c = caixa_with_autores(vec![]);
11547        c.validate_autores().unwrap();
11548    }
11549
11550    #[test]
11551    fn validate_autores_accepts_canonical_forms() {
11552        // Positive control sweep: every canonical-shaped non-empty
11553        // distinct maintainer list passes — the hello-rio / checkout-
11554        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
11555        // multi-author shape downstream packaging surfaces emit.
11556        let c = caixa_with_autores(vec!["pleme-io"]);
11557        c.validate_autores().unwrap();
11558        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
11559        c.validate_autores().unwrap();
11560    }
11561
11562    #[test]
11563    fn validate_autores_rejects_empty_entry() {
11564        // Canonical paste-from-blank-doc footgun. Without the gate the
11565        // empty entry rendered as `maintainers: [{name: "", email: null}]`
11566        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
11567        // to.
11568        let c = caixa_with_autores(vec![""]);
11569        let err = c.validate_autores().unwrap_err();
11570        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11571    }
11572
11573    #[test]
11574    fn validate_autores_rejects_duplicate_entry() {
11575        // Canonical copy-paste-the-wrong-author footgun. Unlike the
11576        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
11577        // dedups the rendered `keywords:` array), the `maintainers:`
11578        // rendering has *no* dedup — duplicates stack verbatim. The
11579        // duplicate-arm names the offending author verbatim.
11580        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11581        let err = c.validate_autores().unwrap_err();
11582        let ManifestError::AutorDuplicate { autor } = err else {
11583            panic!("expected AutorDuplicate, got {err:?}");
11584        };
11585        assert_eq!(autor, "pleme-io");
11586    }
11587
11588    #[test]
11589    fn validate_autores_empty_takes_precedence_over_duplicate() {
11590        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
11591        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
11592        // "this entry has no value" defect dominates the cross-entry
11593        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
11594        // cascades on `:etiquetas` (`EtiquetaEmpty` before
11595        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
11596        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11597        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
11598        // `MembroDuplicate`).
11599        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
11600        let err = c.validate_autores().unwrap_err();
11601        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11602    }
11603
11604    #[test]
11605    fn validate_autores_duplicate_reports_first_collision() {
11606        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11607        // duplicate (the lexicographically-earliest offending position
11608        // — the second `"a"` at index 2 collides with the first `"a"`
11609        // at index 0), not the later `"b"` collision at index 3,
11610        // peer with every other first-collision diagnostic posture on
11611        // this surface.
11612        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
11613        let err = c.validate_autores().unwrap_err();
11614        let ManifestError::AutorDuplicate { autor } = err else {
11615            panic!("expected AutorDuplicate, got {err:?}");
11616        };
11617        assert_eq!(autor, "a");
11618    }
11619
11620    #[test]
11621    fn validate_autores_case_sensitive() {
11622        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
11623        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
11624        // / `:children :caixa` exact-string-match discipline.
11625        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
11626        c.validate_autores().unwrap();
11627    }
11628
11629    #[test]
11630    fn validate_autores_diagnostic_carries_offending_author() {
11631        // Diagnostic-shape pin (peer with
11632        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
11633        // error's Display surfaces the offending author verbatim, so a
11634        // `feira lint` run can render the diagnostic without re-parsing
11635        // and the author can grep their caixa.lisp for the offending
11636        // value.
11637        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11638        let rendered = c.validate_autores().unwrap_err().to_string();
11639        assert!(
11640            rendered.contains(":autores"),
11641            "diagnostic must name the offending slot: {rendered}",
11642        );
11643        assert!(
11644            rendered.contains("pleme-io"),
11645            "diagnostic must quote the offending author: {rendered}",
11646        );
11647    }
11648
11649    #[test]
11650    fn validate_autores_rejects_leading_whitespace_entry() {
11651        // Canonical paste-from-aligned-doc footgun. Without the shape
11652        // gate `" pleme-io"` silently passed validate and landed as a
11653        // YAML plain-style scalar with leading whitespace in the
11654        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
11655        // dumper trims leading whitespace from plain-style scalars, so
11656        // the authored space round-tripped inconsistently back through
11657        // `caixa.lisp`. Mirrors the peer
11658        // `validate_descricao_rejects_leading_whitespace`.
11659        let c = caixa_with_autores(vec![" pleme-io"]);
11660        let err = c.validate_autores().unwrap_err();
11661        let ManifestError::AutorInvalid { autor, reason } = err else {
11662            panic!("expected AutorInvalid, got {err:?}");
11663        };
11664        assert_eq!(autor, " pleme-io");
11665        assert!(reason.contains("whitespace"), "got: {reason}");
11666    }
11667
11668    #[test]
11669    fn validate_autores_rejects_trailing_whitespace_entry() {
11670        // Canonical paste-from-doc footgun.
11671        let c = caixa_with_autores(vec!["pleme-io "]);
11672        let err = c.validate_autores().unwrap_err();
11673        let ManifestError::AutorInvalid { autor, reason } = err else {
11674            panic!("expected AutorInvalid, got {err:?}");
11675        };
11676        assert_eq!(autor, "pleme-io ");
11677        assert!(reason.contains("whitespace"), "got: {reason}");
11678    }
11679
11680    #[test]
11681    fn validate_autores_rejects_embedded_newline_entry() {
11682        // Canonical paste-from-multiline-doc footgun — the author
11683        // pasted a multi-line block of author records into one
11684        // `:autores` entry instead of splitting into one entry per
11685        // author. Without the shape gate `"alice\nbob"` silently
11686        // passed validate and landed as a YAML-illegal multi-line
11687        // scalar in the rendered Chart.yaml `maintainers:` array.
11688        let c = caixa_with_autores(vec!["alice\nbob"]);
11689        let err = c.validate_autores().unwrap_err();
11690        let ManifestError::AutorInvalid { autor, reason } = err else {
11691            panic!("expected AutorInvalid, got {err:?}");
11692        };
11693        assert_eq!(autor, "alice\nbob");
11694        assert!(reason.contains("newline"), "got: {reason}");
11695    }
11696
11697    #[test]
11698    fn validate_autores_rejects_embedded_carriage_return_entry() {
11699        // Canonical paste-from-Windows-CRLF-doc footgun.
11700        let c = caixa_with_autores(vec!["alice\rbob"]);
11701        let err = c.validate_autores().unwrap_err();
11702        let ManifestError::AutorInvalid { autor, reason } = err else {
11703            panic!("expected AutorInvalid, got {err:?}");
11704        };
11705        assert_eq!(autor, "alice\rbob");
11706        assert!(reason.contains("carriage return"), "got: {reason}");
11707    }
11708
11709    #[test]
11710    fn validate_autores_rejects_embedded_tab_entry() {
11711        // Canonical tab-from-aligned-doc footgun.
11712        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
11713        let err = c.validate_autores().unwrap_err();
11714        let ManifestError::AutorInvalid { autor, reason } = err else {
11715            panic!("expected AutorInvalid, got {err:?}");
11716        };
11717        assert_eq!(autor, "Pleme\tContributors");
11718        assert!(reason.contains("tab"), "got: {reason}");
11719    }
11720
11721    #[test]
11722    fn validate_autores_rejects_embedded_control_bytes_entry() {
11723        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
11724        // surface the same control-byte arm.
11725        for entry in [
11726            "alice\x00bob",
11727            "alice\x07bob",
11728            "alice\x1bbob",
11729            "alice\x7fbob",
11730        ] {
11731            let c = caixa_with_autores(vec![entry]);
11732            let err = c.validate_autores().unwrap_err();
11733            let ManifestError::AutorInvalid { autor, reason } = err else {
11734                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
11735            };
11736            assert_eq!(autor, entry);
11737            assert!(
11738                reason.contains("control character"),
11739                "{entry:?} reason: {reason}",
11740            );
11741        }
11742    }
11743
11744    #[test]
11745    fn validate_autores_accepts_unicode_entry() {
11746        // Unicode positive control: realistic maintainer names carry
11747        // Unicode (`François`, `日本語`, `naïve`). The predicate must
11748        // round-trip Unicode losslessly, peer with the
11749        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
11750        // sweep.
11751        let c = caixa_with_autores(vec![
11752            "François Dupont",
11753            "日本語の名前",
11754            "naïve <naive@example.com>",
11755        ]);
11756        c.validate_autores().unwrap();
11757    }
11758
11759    #[test]
11760    fn validate_autores_empty_takes_precedence_over_shape() {
11761        // Per-entry empty-first cascade pin: an entry that is both
11762        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
11763        // "this entry has no value" structural defect dominates the
11764        // broader shape-predicate diagnostic). The empty arm fires
11765        // before the shape predicate is consulted, mirroring the peer
11766        // `validate_repositorio_empty_takes_precedence_over_shape`
11767        // cascade on the universal `Option<String>` siblings — and now
11768        // established on the Vec<String> per-entry surface.
11769        let c = caixa_with_autores(vec![""]);
11770        let err = c.validate_autores().unwrap_err();
11771        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11772    }
11773
11774    #[test]
11775    fn validate_autores_shape_takes_precedence_over_duplicate() {
11776        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11777        // entry that is malformed surfaces `AutorInvalid` even when a
11778        // later entry would have collided on duplicate. The per-entry
11779        // shape arm fires inside the same loop iteration as the empty
11780        // arm, before the seen-set insert at end-of-iteration —
11781        // structural per-entry defects dominate the cross-entry
11782        // uniqueness diagnostic.
11783        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
11784        let err = c.validate_autores().unwrap_err();
11785        assert!(
11786            matches!(err, ManifestError::AutorInvalid { .. }),
11787            "got {err:?}",
11788        );
11789    }
11790
11791    #[test]
11792    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
11793        // Diagnostic-shape pin on the new shape arm (peer with
11794        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
11795        // the rendered Display surfaces both the offending slot name
11796        // and the offending value verbatim, so a `feira lint` run
11797        // points the author at the exact `:autores` entry to fix.
11798        let c = caixa_with_autores(vec!["alice\nbob"]);
11799        let rendered = c.validate_autores().unwrap_err().to_string();
11800        assert!(
11801            rendered.contains(":autores"),
11802            "diagnostic must name the offending slot: {rendered}",
11803        );
11804        assert!(
11805            rendered.contains("alice\\nbob"),
11806            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11807        );
11808    }
11809
11810    #[test]
11811    fn validate_autores_rejects_at_129_byte_boundary() {
11812        // The 128-byte cap pin — boundary-exceeding case rejected,
11813        // boundary-accepting case passes. Mirrors the peer
11814        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
11815        // substrate-side pin, surfaced at the per-axis caller so the
11816        // cap propagates through validate end-to-end. Constructed as
11817        // a single all-`a` token so only the cap arm fires.
11818        let max_ok = "a".repeat(128);
11819        let c = caixa_with_autores(vec![max_ok.as_str()]);
11820        c.validate_autores().unwrap();
11821        let too_long = "a".repeat(129);
11822        let c = caixa_with_autores(vec![too_long.as_str()]);
11823        let err = c.validate_autores().unwrap_err();
11824        let ManifestError::AutorInvalid { reason, .. } = err else {
11825            panic!("expected AutorInvalid, got {err:?}");
11826        };
11827        assert!(reason.contains("128"), "got: {reason}");
11828        assert!(reason.contains("129"), "got: {reason}");
11829    }
11830
11831    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
11832
11833    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
11834        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11835        c.repositorio = repositorio.map(String::from);
11836        c
11837    }
11838
11839    #[test]
11840    fn validate_repositorio_accepts_none() {
11841        // The omit-the-slot identity: `:repositorio` is optional. The
11842        // gate is a no-op when the author didn't declare a value —
11843        // every caixa without a `:repositorio` line trivially passes,
11844        // and the substrate-side renderers fall back to their
11845        // documented placeholder (`caixa-helm`'s `home: None`,
11846        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
11847        // URL). Mirrors the peer `validate_restart_window_accepts_none`
11848        // posture on the other `Option<String>` Caixa slot.
11849        let c = caixa_with_repositorio(None);
11850        c.validate_repositorio().unwrap();
11851    }
11852
11853    #[test]
11854    fn validate_repositorio_accepts_canonical_forms() {
11855        // Positive control sweep across every documented `:repositorio`
11856        // authoring shape — the same union the shared
11857        // `crate::render::is_git_repo_url` predicate accepts and the
11858        // peer `:deps :fonte :repo` axis already routes through.
11859        // Covers the `github:` shorthand (the canonical pleme-io
11860        // convention used in the `:repositorio` field of every
11861        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
11862        // `examples/`), the `https://…` URL the README quickstart uses,
11863        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
11864        // `file://` URL schemes the shared predicate documents.
11865        for repo in [
11866            "github:pleme-io/hello-rio",
11867            "github:pleme-io/checkout",
11868            "https://github.com/pleme-io/hello-rio",
11869            "ssh://git@github.com/pleme-io/hello-rio.git",
11870            "git://github.com/pleme-io/hello-rio.git",
11871            "git@github.com:pleme-io/hello-rio.git",
11872            "file:///srv/pleme/hello-rio",
11873        ] {
11874            let c = caixa_with_repositorio(Some(repo));
11875            c.validate_repositorio()
11876                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
11877        }
11878    }
11879
11880    #[test]
11881    fn validate_repositorio_rejects_empty_some() {
11882        // Canonical paste-from-blank-doc footgun. The narrower
11883        // [`ManifestError::RepositorioEmpty`] arm fires before the
11884        // shape predicate is consulted, mirroring the empty-first
11885        // cascade every peer per-axis identity gate uses
11886        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
11887        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
11888        // the empty `Some("")` silently passed the renderer's
11889        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
11890        // on `None`) and landed as `home: ""` in `Chart.yaml` /
11891        // `url: ""` in the FluxCD `GitRepository`.
11892        let c = caixa_with_repositorio(Some(""));
11893        let err = c.validate_repositorio().unwrap_err();
11894        assert!(
11895            matches!(err, ManifestError::RepositorioEmpty),
11896            "got {err:?}",
11897        );
11898    }
11899
11900    #[test]
11901    fn validate_repositorio_rejects_whitespace() {
11902        // Paste-from-doc whitespace footgun. The shared
11903        // `is_git_repo_url` predicate refuses any whitespace byte; a
11904        // trailing space in a `:repositorio` value silently broke
11905        // `git clone '<value> '` at clone time. The diagnostic names
11906        // the offending value verbatim.
11907        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
11908        let err = c.validate_repositorio().unwrap_err();
11909        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
11910            panic!("expected RepositorioInvalid, got {err:?}");
11911        };
11912        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
11913    }
11914
11915    #[test]
11916    fn validate_repositorio_rejects_control_char() {
11917        // Paste-from-multiline-doc CRLF footgun — control characters
11918        // at the URL boundary are a class of subprocess-arg injection
11919        // and break git's URL parser at every porcelain entry point.
11920        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
11921        let err = c.validate_repositorio().unwrap_err();
11922        assert!(
11923            matches!(err, ManifestError::RepositorioInvalid { .. }),
11924            "got {err:?}",
11925        );
11926    }
11927
11928    #[test]
11929    fn validate_repositorio_rejects_leading_dash() {
11930        // Canonical CLI-argument-injection footgun: `git clone <repo>`
11931        // interprets a leading `-` as a CLI flag, so a
11932        // `-upload-pack=…` value escapes the subprocess argument
11933        // boundary. The shared predicate refuses every leading-`-`
11934        // shape at validate time.
11935        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
11936        let err = c.validate_repositorio().unwrap_err();
11937        assert!(
11938            matches!(err, ManifestError::RepositorioInvalid { .. }),
11939            "got {err:?}",
11940        );
11941    }
11942
11943    #[test]
11944    fn validate_repositorio_rejects_missing_colon_separator() {
11945        // The bare `org/repo` ambiguity footgun — `git clone` reads
11946        // a no-`:` form as a relative filesystem path rather than the
11947        // GitHub-shorthand expansion the author probably intended.
11948        // The shared predicate refuses every shape without a `:`
11949        // separator.
11950        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
11951        let err = c.validate_repositorio().unwrap_err();
11952        assert!(
11953            matches!(err, ManifestError::RepositorioInvalid { .. }),
11954            "got {err:?}",
11955        );
11956    }
11957
11958    #[test]
11959    fn validate_repositorio_rejects_fragment_anchor() {
11960        // Paste-from-browser-address-bar footgun on the
11961        // `:repositorio` axis — an author copies a GitHub permalink
11962        // to a README section / line-permalink and forgets to trim
11963        // the `#fragment` tail. The shared `is_git_repo_url`
11964        // predicate refuses the byte at the URL-grammar layer
11965        // (libcurl strips the fragment before opening the
11966        // transport, so the byte rides verbatim into the rendered
11967        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
11968        // fields but is silently dropped on the wire — two
11969        // manifest variants whose values differ only in their
11970        // fragment anchor lock to two distinct rendered artifacts
11971        // for the byte-identical clone, defeating the THEORY.md
11972        // §V.2 render-determinism contract on the `:repositorio`
11973        // axis the peer `:fonte :repo` axis already closes).
11974        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
11975        let err = c.validate_repositorio().unwrap_err();
11976        let ManifestError::RepositorioInvalid {
11977            repositorio,
11978            reason,
11979        } = err
11980        else {
11981            panic!("expected RepositorioInvalid, got {err:?}");
11982        };
11983        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
11984        assert!(
11985            reason.contains("must not contain `#`"),
11986            "reason must surface the fragment-`#` arm, got {reason:?}"
11987        );
11988    }
11989
11990    #[test]
11991    fn validate_repositorio_rejects_query_string() {
11992        // Paste-from-browser-address-bar footgun on the
11993        // `:repositorio` axis (peer with the a68f818 fragment-`#`
11994        // arm on the same axis). An author copies a GitHub tab
11995        // deep-link out of the address bar and forgets to trim
11996        // the `?tab=…` query tail. The shared `is_git_repo_url`
11997        // predicate refuses the byte at the URL-grammar layer
11998        // (GitHub / GitLab / Bitbucket silently ignore the
11999        // `?query` tail and serve the same repo regardless, so
12000        // the byte rides verbatim into the rendered `Chart.yaml`
12001        // `home:` and FluxCD `GitRepository` `url:` fields but
12002        // is silently masked at the wire — two manifest variants
12003        // whose values differ only in their query tail lock to
12004        // two distinct rendered artifacts for the byte-identical
12005        // clone, defeating the THEORY.md §V.2 render-determinism
12006        // contract on the `:repositorio` axis the peer `:fonte
12007        // :repo` axis already closes).
12008        let c = caixa_with_repositorio(Some(
12009            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
12010        ));
12011        let err = c.validate_repositorio().unwrap_err();
12012        let ManifestError::RepositorioInvalid {
12013            repositorio,
12014            reason,
12015        } = err
12016        else {
12017            panic!("expected RepositorioInvalid, got {err:?}");
12018        };
12019        assert_eq!(
12020            repositorio,
12021            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
12022        );
12023        assert!(
12024            reason.contains("must not contain `?`"),
12025            "reason must surface the query-`?` arm, got {reason:?}"
12026        );
12027    }
12028
12029    #[test]
12030    fn validate_repositorio_rejects_embedded_backslash() {
12031        // Windows-file-path-confusion footgun on the `:repositorio`
12032        // axis (peer with the prior fragment-`#` / query-`?` arms on
12033        // the same axis, and peer with the new dep-level `:fonte :repo`
12034        // backslash arm on the URL-grammar trajectory). An author
12035        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
12036        // hello-rio` into the `:repositorio` slot, expecting the
12037        // `lareira-<nome>` chart's `home:` field and the FluxCD
12038        // `GitRepository` `url:` field to render the canonical local
12039        // file-URI. The shared `is_git_repo_url` predicate refuses
12040        // the byte at the URL-grammar layer (libcurl silently
12041        // translates `\` → `/` on some platforms and refuses it on
12042        // others, so the byte rides verbatim into the rendered
12043        // artifacts but is silently rewritten or rejected at the wire
12044        // — two manifest variants whose values differ only in
12045        // backslash-vs-forward-slash lock to two distinct rendered
12046        // artifacts for the byte-identical clone, defeating the
12047        // THEORY.md §V.2 render-determinism contract on the
12048        // `:repositorio` axis the peer `:fonte :repo` axis already
12049        // closes).
12050        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
12051        let err = c.validate_repositorio().unwrap_err();
12052        let ManifestError::RepositorioInvalid {
12053            repositorio,
12054            reason,
12055        } = err
12056        else {
12057            panic!("expected RepositorioInvalid, got {err:?}");
12058        };
12059        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
12060        assert!(
12061            reason.contains("must not contain `\\`"),
12062            "reason must surface the backslash-`\\` arm, got {reason:?}"
12063        );
12064    }
12065
12066    #[test]
12067    fn validate_repositorio_rejects_uri_template_placeholder() {
12068        // URI Template (RFC 6570) placeholder footgun on the
12069        // `:repositorio` axis (peer with the prior fragment-`#` /
12070        // query-`?` / backslash-`\` arms on the same axis, and peer
12071        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
12072        // URL-grammar trajectory). An author pastes a quick-start
12073        // README snippet / OpenAPI `servers:` URL / Helm chart
12074        // `home:` template carrying unresolved `{org}` / `{repo}`
12075        // placeholders into the `:repositorio` slot, expecting the
12076        // substrate to resolve the placeholder downstream. The
12077        // shared `is_git_repo_url` predicate refuses the byte at the
12078        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
12079        // `%7B` / `%7D` on the wire, so the byte round-trips
12080        // inconsistently between the rendered `Chart.yaml home:` /
12081        // FluxCD `GitRepository url:` and the resolver's `git clone`
12082        // invocation, defeating the THEORY.md §V.2 render-
12083        // determinism contract on the `:repositorio` axis the peer
12084        // `:fonte :repo` axis already closes; every git porcelain
12085        // entry-point additionally fetches a nonexistent literal-
12086        // `{placeholder}`-named path far from the source caixa.lisp).
12087        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
12088        let err = c.validate_repositorio().unwrap_err();
12089        let ManifestError::RepositorioInvalid {
12090            repositorio,
12091            reason,
12092        } = err
12093        else {
12094            panic!("expected RepositorioInvalid, got {err:?}");
12095        };
12096        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
12097        assert!(
12098            reason.contains("must not contain `{`"),
12099            "reason must surface the open-brace `{{` arm, got {reason:?}"
12100        );
12101        assert!(
12102            reason.contains("URI Template") || reason.contains("RFC 6570"),
12103            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
12104        );
12105    }
12106
12107    #[test]
12108    fn validate_repositorio_empty_takes_precedence_over_shape() {
12109        // Empty-first cascade pin: the empty `Some("")` surfaces the
12110        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
12111        // `RepositorioInvalid`, mirroring the peer
12112        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
12113        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
12114        // `is_git_repo_url` predicate also rejects the empty input
12115        // (defensively, with its own `"must not be empty"` reason),
12116        // but the manifest-layer empty arm runs first to surface the
12117        // narrower diagnostic verbatim.
12118        let c = caixa_with_repositorio(Some(""));
12119        let err = c.validate_repositorio().unwrap_err();
12120        assert!(
12121            matches!(err, ManifestError::RepositorioEmpty),
12122            "got {err:?}",
12123        );
12124    }
12125
12126    #[test]
12127    fn validate_repositorio_diagnostic_carries_offending_value() {
12128        // Diagnostic-shape pin (peer with
12129        // `validate_autores_diagnostic_carries_offending_author`): the
12130        // error's Display surfaces the offending value + slot name
12131        // verbatim, so a `feira lint` run can render the diagnostic
12132        // without re-parsing and the author can grep their caixa.lisp
12133        // for the offending `:repositorio` value.
12134        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
12135        let rendered = c.validate_repositorio().unwrap_err().to_string();
12136        assert!(
12137            rendered.contains(":repositorio"),
12138            "diagnostic must name the offending slot: {rendered}",
12139        );
12140        assert!(
12141            rendered.contains("pleme-io/hello-rio"),
12142            "diagnostic must quote the offending value: {rendered}",
12143        );
12144    }
12145
12146    // ── validate_descricao — universal-axis Chart.yaml description shape ──
12147
12148    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
12149        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12150        c.descricao = descricao.map(String::from);
12151        c
12152    }
12153
12154    #[test]
12155    fn validate_descricao_accepts_none() {
12156        // The omit-the-slot identity: `:descricao` is optional. The
12157        // gate is a no-op when the author didn't declare a value —
12158        // every caixa without a `:descricao` line trivially passes,
12159        // and the substrate-side renderers fall back to their
12160        // documented `caixa.nome`-derived placeholder. Mirrors the
12161        // peer `validate_repositorio_accepts_none` posture on the
12162        // sibling `Option<String>` Caixa slot.
12163        let c = caixa_with_descricao(None);
12164        c.validate_descricao().unwrap();
12165    }
12166
12167    #[test]
12168    fn validate_descricao_accepts_canonical_summary() {
12169        // Positive control: the canonical pleme-io descricao shape —
12170        // a short free-form prose summary — passes the gate. Covers
12171        // the fixture shapes the `caixa-helm` / `caixa-flux` /
12172        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
12173        // wasip2 caixa Servico."`, `"Checkout flow."`).
12174        for desc in [
12175            "Canonical Rust→wasm32-wasip2 caixa Servico.",
12176            "Checkout flow.",
12177            "AWS provider caixa for tatara-lisp",
12178            "FIXME — describe this caixa",
12179            "x",
12180        ] {
12181            let c = caixa_with_descricao(Some(desc));
12182            c.validate_descricao()
12183                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
12184        }
12185    }
12186
12187    #[test]
12188    fn validate_descricao_rejects_empty_some() {
12189        // Canonical paste-from-blank-doc footgun. Without this gate
12190        // the empty `Some("")` silently passed the renderer's
12191        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
12192        // on `None`) and landed as `description: ""` in `Chart.yaml`
12193        // and a blank `README.md` header. Mirrors the peer
12194        // [`ManifestError::RepositorioEmpty`] empty-arm on the
12195        // sibling `Option<String>` Caixa slot.
12196        let c = caixa_with_descricao(Some(""));
12197        let err = c.validate_descricao().unwrap_err();
12198        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
12199    }
12200
12201    #[test]
12202    fn validate_descricao_rejects_leading_whitespace() {
12203        // Paste-from-aligned-doc footgun: a leading ASCII space the
12204        // bare empty-arm gate accepted, the shape predicate now
12205        // refuses. The diagnostic carries the offending value
12206        // verbatim (with the leading space preserved) so the author
12207        // can grep their caixa.lisp for the exact `:descricao` line
12208        // and fix the round-trip-inconsistent leading whitespace.
12209        // Mirrors the peer
12210        // `validate_licenca_rejects_leading_whitespace` arm on the
12211        // sibling `:licenca` axis.
12212        let c = caixa_with_descricao(Some(" Checkout flow."));
12213        let err = c.validate_descricao().unwrap_err();
12214        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
12215            panic!("expected DescricaoInvalid, got {err:?}");
12216        };
12217        assert_eq!(descricao, " Checkout flow.");
12218        assert!(reason.contains("whitespace"), "got: {reason:?}");
12219    }
12220
12221    #[test]
12222    fn validate_descricao_rejects_trailing_whitespace() {
12223        // Paste-from-doc footgun: a trailing ASCII space the bare
12224        // empty-arm gate accepted, the shape predicate now refuses.
12225        let c = caixa_with_descricao(Some("Checkout flow. "));
12226        let err = c.validate_descricao().unwrap_err();
12227        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
12228            panic!("expected DescricaoInvalid, got {err:?}");
12229        };
12230        assert_eq!(descricao, "Checkout flow. ");
12231        assert!(reason.contains("whitespace"), "got: {reason:?}");
12232    }
12233
12234    #[test]
12235    fn validate_descricao_rejects_embedded_newline() {
12236        // Paste-from-multiline-doc footgun: an embedded LF the bare
12237        // empty-arm gate accepted, the shape predicate now refuses.
12238        // Without this gate the embedded newline silently landed in
12239        // the rendered Chart.yaml as a multi-line YAML block scalar,
12240        // and every chart-aware UI (`helm list`, `helm search`,
12241        // Artifact Hub) renders the description in a single-line
12242        // column so the embedded newline is silently dropped at
12243        // every downstream consumer.
12244        let c = caixa_with_descricao(Some("Checkout\nflow."));
12245        let err = c.validate_descricao().unwrap_err();
12246        assert!(
12247            matches!(err, ManifestError::DescricaoInvalid { .. }),
12248            "got {err:?}",
12249        );
12250        assert!(err.to_string().contains("newline"), "got {err}");
12251    }
12252
12253    #[test]
12254    fn validate_descricao_rejects_embedded_carriage_return() {
12255        // Paste-from-Windows-CRLF-doc footgun.
12256        let c = caixa_with_descricao(Some("Checkout\rflow."));
12257        let err = c.validate_descricao().unwrap_err();
12258        assert!(
12259            matches!(err, ManifestError::DescricaoInvalid { .. }),
12260            "got {err:?}",
12261        );
12262        assert!(err.to_string().contains("carriage return"), "got {err}");
12263    }
12264
12265    #[test]
12266    fn validate_descricao_rejects_embedded_tab() {
12267        // Tab-from-aligned-doc footgun.
12268        let c = caixa_with_descricao(Some("Checkout\tflow."));
12269        let err = c.validate_descricao().unwrap_err();
12270        assert!(
12271            matches!(err, ManifestError::DescricaoInvalid { .. }),
12272            "got {err:?}",
12273        );
12274        assert!(err.to_string().contains("tab"), "got {err}");
12275    }
12276
12277    #[test]
12278    fn validate_descricao_rejects_embedded_control_bytes() {
12279        // Paste-from-binary-blob footgun: every other control byte
12280        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
12281        // the peer SPDX-expression control-byte arm.
12282        for s in [
12283            "Checkout\x00flow.",
12284            "Checkout\x07flow.",
12285            "Checkout\x1bflow.",
12286            "Checkout\x7fflow.",
12287        ] {
12288            let c = caixa_with_descricao(Some(s));
12289            let err = c.validate_descricao().unwrap_err();
12290            assert!(
12291                matches!(err, ManifestError::DescricaoInvalid { .. }),
12292                "{s:?} got {err:?}",
12293            );
12294            assert!(
12295                err.to_string().contains("control character"),
12296                "{s:?} got {err}",
12297            );
12298        }
12299    }
12300
12301    #[test]
12302    fn validate_descricao_accepts_unicode_prose() {
12303        // Positive control: Unicode prose is accepted — the
12304        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
12305        // and `Caixa::template`'s `"FIXME — describe this caixa"`
12306        // scaffold every `feira init` emits must continue to pass.
12307        for s in [
12308            "Canonical Rust→wasm32-wasip2 caixa Servico.",
12309            "FIXME — describe this caixa",
12310            "Caixa pour le projet tâche",
12311            "日本語の説明",
12312        ] {
12313            let c = caixa_with_descricao(Some(s));
12314            c.validate_descricao()
12315                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
12316        }
12317    }
12318
12319    #[test]
12320    fn validate_descricao_empty_takes_precedence_over_shape() {
12321        // Cascade pin: a `Some("")` surfaces the narrower
12322        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
12323        // shape-predicate arm. Mirrors the peer
12324        // `validate_licenca_empty_takes_precedence_over_shape` pin
12325        // on the sibling `:licenca` axis.
12326        let c = caixa_with_descricao(Some(""));
12327        let err = c.validate_descricao().unwrap_err();
12328        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
12329    }
12330
12331    #[test]
12332    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
12333        // Diagnostic-shape pin: the error's Display surfaces both
12334        // the `:descricao` slot name and the offending value
12335        // verbatim, so a `feira lint` run can render the diagnostic
12336        // without re-parsing and the author can grep their caixa.lisp
12337        // for the offending `:descricao` line. Mirrors the peer
12338        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
12339        // pin (ee2e888) on the sibling `:licenca` axis.
12340        // The `{descricao:?}` Debug format escapes embedded control
12341        // bytes; the quoted offending value surfaces as
12342        // `"Checkout\nflow."` (literal backslash-n) in the rendered
12343        // diagnostic. The author can grep their caixa.lisp for the
12344        // literal `Checkout` summary prefix.
12345        let c = caixa_with_descricao(Some("Checkout\nflow."));
12346        let rendered = c.validate_descricao().unwrap_err().to_string();
12347        assert!(
12348            rendered.contains(":descricao"),
12349            "diagnostic must name the offending slot: {rendered}",
12350        );
12351        assert!(
12352            rendered.contains("Checkout\\nflow."),
12353            "diagnostic must quote the offending value (debug-escaped): {rendered}",
12354        );
12355    }
12356
12357    #[test]
12358    fn validate_descricao_template_passes() {
12359        // Round-trip pin: the bare `Caixa::template` shape carries
12360        // `:descricao "FIXME — describe this caixa"` (a non-empty
12361        // sentinel), so the template-derived Caixa passes the gate by
12362        // construction. A future template-shape change that omits or
12363        // empties `:descricao` would surface here as a regression.
12364        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12365        c.validate_descricao().unwrap();
12366    }
12367
12368    #[test]
12369    fn validate_descricao_diagnostic_names_offending_slot() {
12370        // Diagnostic-shape pin (peer with
12371        // `validate_repositorio_diagnostic_carries_offending_value`):
12372        // the error's Display surfaces the `:descricao` slot name
12373        // verbatim, so a `feira lint` run can render the diagnostic
12374        // without re-parsing and the author can grep their caixa.lisp
12375        // for the offending `:descricao` line.
12376        let c = caixa_with_descricao(Some(""));
12377        let rendered = c.validate_descricao().unwrap_err().to_string();
12378        assert!(
12379            rendered.contains(":descricao"),
12380            "diagnostic must name the offending slot: {rendered}",
12381        );
12382    }
12383
12384    // ── validate_licenca — universal-axis chart README license shape ──
12385
12386    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
12387        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12388        c.licenca = licenca.map(String::from);
12389        c
12390    }
12391
12392    #[test]
12393    fn validate_licenca_accepts_none() {
12394        // The omit-the-slot identity: `:licenca` is optional. The
12395        // gate is a no-op when the author didn't declare a value —
12396        // every caixa without a `:licenca` line trivially passes,
12397        // and the substrate-side `caixa-helm` renderer falls back to
12398        // the documented `"MIT"` placeholder. Mirrors the peer
12399        // `validate_descricao_accepts_none` posture on the sibling
12400        // `Option<String>` Caixa slot.
12401        let c = caixa_with_licenca(None);
12402        c.validate_licenca().unwrap();
12403    }
12404
12405    #[test]
12406    fn validate_licenca_accepts_canonical_expressions() {
12407        // Positive control: every canonical SPDX expression shape
12408        // pleme-io carries in its existing fixtures + the canonical
12409        // SPDX dual-license / with-exception / `+`-suffix / grouped /
12410        // user-defined-reference shapes all pass the gate. Covers
12411        // the single-license, `OR`-compound, `AND`-compound,
12412        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
12413        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
12414        // production the SPDX 2.1 expression grammar admits that
12415        // sits within the alphabet floor the
12416        // `is_spdx_expression_shape` predicate enforces.
12417        for lic in [
12418            "MIT",
12419            "Apache-2.0",
12420            "Apache-2.0 OR MIT",
12421            "Apache-2.0 AND MIT",
12422            "BSD-3-Clause",
12423            "MPL-2.0",
12424            "GPL-3.0-or-later",
12425            "GPL-2.0+",
12426            "Apache-2.0 WITH LLVM-exception",
12427            "(MIT OR Apache-2.0) AND BSD-3-Clause",
12428            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
12429            "LicenseRef-MyLicense",
12430            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
12431            "x",
12432        ] {
12433            let c = caixa_with_licenca(Some(lic));
12434            c.validate_licenca()
12435                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
12436        }
12437    }
12438
12439    #[test]
12440    fn validate_licenca_rejects_trailing_whitespace() {
12441        // Paste-from-doc whitespace footgun. A trailing space in the
12442        // `:licenca` value would silently break a downstream SPDX
12443        // parser that splits on exact `AND` / `OR` / `WITH` keyword
12444        // boundaries. The shape predicate refuses every trailing
12445        // whitespace byte by construction. Peer with
12446        // `validate_repositorio_rejects_whitespace` and
12447        // `validate_edicao_rejects_trailing_whitespace`.
12448        let c = caixa_with_licenca(Some("MIT "));
12449        let err = c.validate_licenca().unwrap_err();
12450        let ManifestError::LicencaInvalid { licenca, .. } = err else {
12451            panic!("expected LicencaInvalid, got {err:?}");
12452        };
12453        assert_eq!(licenca, "MIT ");
12454    }
12455
12456    #[test]
12457    fn validate_licenca_rejects_leading_whitespace() {
12458        // Symmetric paste-from-doc whitespace footgun on the leading
12459        // boundary — the gate refuses every shape that starts with a
12460        // space byte by construction. Peer with
12461        // `validate_edicao_rejects_leading_whitespace`.
12462        let c = caixa_with_licenca(Some(" MIT"));
12463        let err = c.validate_licenca().unwrap_err();
12464        assert!(
12465            matches!(err, ManifestError::LicencaInvalid { .. }),
12466            "got {err:?}",
12467        );
12468    }
12469
12470    #[test]
12471    fn validate_licenca_rejects_control_char() {
12472        // Paste-from-multiline-doc CRLF footgun — control characters
12473        // at the value boundary land as a malformed line in the
12474        // rendered chart `README.md` `## License` section. Peer with
12475        // `validate_repositorio_rejects_control_char` and
12476        // `validate_edicao_rejects_control_char`.
12477        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
12478            let c = caixa_with_licenca(Some(lic));
12479            let err = c.validate_licenca().unwrap_err();
12480            assert!(
12481                matches!(err, ManifestError::LicencaInvalid { .. }),
12482                "expected LicencaInvalid on {lic:?}, got {err:?}",
12483            );
12484        }
12485    }
12486
12487    #[test]
12488    fn validate_licenca_rejects_tab() {
12489        // Tab-from-aligned-doc footgun — SPDX expressions use a
12490        // single ASCII space between tokens; a tab breaks every
12491        // downstream SPDX parser that splits on exact `" "`
12492        // boundaries.
12493        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
12494        let err = c.validate_licenca().unwrap_err();
12495        assert!(
12496            matches!(err, ManifestError::LicencaInvalid { .. }),
12497            "got {err:?}",
12498        );
12499    }
12500
12501    #[test]
12502    fn validate_licenca_rejects_non_ascii() {
12503        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
12504        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
12505        // ".")` production. The shape predicate refuses every
12506        // non-ASCII byte by construction; peer with
12507        // `validate_edicao_rejects_non_ascii_lookalike`.
12508        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
12509            let c = caixa_with_licenca(Some(lic));
12510            let err = c.validate_licenca().unwrap_err();
12511            assert!(
12512                matches!(err, ManifestError::LicencaInvalid { .. }),
12513                "expected LicencaInvalid on {lic:?}, got {err:?}",
12514            );
12515        }
12516    }
12517
12518    #[test]
12519    fn validate_licenca_rejects_underscore() {
12520        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
12521        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
12522        // snake-case identifier conventions that don't apply to the
12523        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
12524        // "-" / "."`). The shape predicate refuses every underscore
12525        // byte by construction.
12526        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
12527            let c = caixa_with_licenca(Some(lic));
12528            let err = c.validate_licenca().unwrap_err();
12529            assert!(
12530                matches!(err, ManifestError::LicencaInvalid { .. }),
12531                "expected LicencaInvalid on {lic:?}, got {err:?}",
12532            );
12533        }
12534    }
12535
12536    #[test]
12537    fn validate_licenca_rejects_comma_separator() {
12538        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
12539        // SPDX expressions compose multiple licenses via `AND` / `OR`
12540        // keywords, not the comma separator. The shape predicate
12541        // refuses every comma byte by construction.
12542        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
12543            let c = caixa_with_licenca(Some(lic));
12544            let err = c.validate_licenca().unwrap_err();
12545            assert!(
12546                matches!(err, ManifestError::LicencaInvalid { .. }),
12547                "expected LicencaInvalid on {lic:?}, got {err:?}",
12548            );
12549        }
12550    }
12551
12552    #[test]
12553    fn validate_licenca_rejects_slash_dual_license() {
12554        // Slash-dual-license colloquial idiom footgun — the
12555        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
12556        // `package.license` field but non-SPDX; the SPDX equivalent
12557        // is `MIT OR Apache-2.0`. The shape predicate refuses every
12558        // forward-slash byte by construction.
12559        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
12560            let c = caixa_with_licenca(Some(lic));
12561            let err = c.validate_licenca().unwrap_err();
12562            assert!(
12563                matches!(err, ManifestError::LicencaInvalid { .. }),
12564                "expected LicencaInvalid on {lic:?}, got {err:?}",
12565            );
12566        }
12567    }
12568
12569    #[test]
12570    fn validate_licenca_rejects_semicolon_separator() {
12571        // Semicolon-list-separator confusion footgun — adjacent to
12572        // the comma-separator idiom, every list-separator-belongs-
12573        // to-list-grammar confusion lands here.
12574        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
12575        let err = c.validate_licenca().unwrap_err();
12576        assert!(
12577            matches!(err, ManifestError::LicencaInvalid { .. }),
12578            "got {err:?}",
12579        );
12580    }
12581
12582    #[test]
12583    fn validate_licenca_empty_takes_precedence_over_shape() {
12584        // Empty-first cascade pin: the empty `Some("")` surfaces the
12585        // narrower `LicencaEmpty` not the shape-predicate-wrapped
12586        // `LicencaInvalid`, mirroring the peer
12587        // `validate_edicao_empty_takes_precedence_over_shape` and
12588        // `validate_repositorio_empty_takes_precedence_over_shape`
12589        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
12590        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
12591        // The shape predicate also refuses the empty input
12592        // (defensively — `"must not be empty"`), but the manifest-
12593        // layer empty arm runs first to surface the narrower
12594        // diagnostic verbatim.
12595        let c = caixa_with_licenca(Some(""));
12596        let err = c.validate_licenca().unwrap_err();
12597        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12598    }
12599
12600    #[test]
12601    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
12602        // Diagnostic-shape pin on the shape-predicate arm (peer with
12603        // `validate_edicao_invalid_diagnostic_carries_offending_value`
12604        // and `validate_repositorio_diagnostic_carries_offending_value`):
12605        // the error's Display surfaces the offending value + slot
12606        // name verbatim, so a `feira lint` run can render the
12607        // diagnostic without re-parsing and the author can grep
12608        // their caixa.lisp for the offending `:licenca` value.
12609        let c = caixa_with_licenca(Some("Apache_2.0"));
12610        let rendered = c.validate_licenca().unwrap_err().to_string();
12611        assert!(
12612            rendered.contains(":licenca"),
12613            "diagnostic must name the offending slot: {rendered}",
12614        );
12615        assert!(
12616            rendered.contains("Apache_2.0"),
12617            "diagnostic must quote the offending value: {rendered}",
12618        );
12619    }
12620
12621    #[test]
12622    fn validate_licenca_rejects_empty_some() {
12623        // Canonical paste-from-blank-doc footgun. Without this gate
12624        // the empty `Some("")` silently passed the renderer's
12625        // `Option::unwrap_or_else(|| "MIT".into())` (which only
12626        // fires on `None`) and landed as a bare trailing period in
12627        // the rendered chart `README.md` `## License` section.
12628        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
12629        // arm on the sibling `Option<String>` Caixa slot.
12630        let c = caixa_with_licenca(Some(""));
12631        let err = c.validate_licenca().unwrap_err();
12632        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12633    }
12634
12635    #[test]
12636    fn validate_licenca_template_passes() {
12637        // Round-trip pin: the bare `Caixa::template` shape (whether
12638        // it carries `:licenca` or omits it) passes the gate by
12639        // construction. A future template-shape change that
12640        // introduced `(:licenca "")` would surface here as a
12641        // regression. Mirrors the peer
12642        // `validate_descricao_template_passes` pin.
12643        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12644        c.validate_licenca().unwrap();
12645    }
12646
12647    #[test]
12648    fn validate_licenca_diagnostic_names_offending_slot() {
12649        // Diagnostic-shape pin (peer with
12650        // `validate_descricao_diagnostic_names_offending_slot`):
12651        // the error's Display surfaces the `:licenca` slot name
12652        // verbatim, so a `feira lint` run can render the diagnostic
12653        // without re-parsing and the author can grep their caixa.lisp
12654        // for the offending `:licenca` line.
12655        let c = caixa_with_licenca(Some(""));
12656        let rendered = c.validate_licenca().unwrap_err().to_string();
12657        assert!(
12658            rendered.contains(":licenca"),
12659            "diagnostic must name the offending slot: {rendered}",
12660        );
12661    }
12662
12663    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
12664
12665    #[test]
12666    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
12667        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
12668        // pin: [`Caixa::licenca`] must return the `:licenca` typed
12669        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12670        // raw `self.licenca.as_deref()` access across every
12671        // representative value in the accept-set — `None` (the "omit
12672        // the slot to defer to the caixa-helm renderer's `MIT`
12673        // fallback" arm every existing fixture without a `:licenca`
12674        // line carries), `Some("")` (a past-the-guard sentinel that
12675        // pins the accessor doesn't perform a silent
12676        // `Some("") → None` collapse on the empty arm — validate
12677        // rejects `Some("")` through `LicencaEmpty` but the accessor
12678        // must ship the raw slot verbatim so a validate-time gate
12679        // regression surfaces at the caixa-helm emit boundary rather
12680        // than being silently absorbed into the fallback), `Some("MIT")`
12681        // (the canonical single-license shape every `feira init`
12682        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
12683        // canonical `OR`-compound shape the peer
12684        // `validate_licenca_accepts_canonical_expressions` positive
12685        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
12686        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
12687        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
12688        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
12689        // guard sentinels — validate rejects each through
12690        // `LicencaInvalid` but the accessor must ship the raw slot
12691        // verbatim).
12692        //
12693        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
12694        // accessor pin on the substrate primitive — opens the "outer
12695        // [`Caixa`] `Option<&str>` scalar" projection pattern the
12696        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
12697        // future lifts fold on. Sibling in shape to the peer per-`:placement`
12698        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12699        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12700        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12701        // axes, extended onto the outer top-level [`Caixa`] universal-
12702        // axis surface. Pins against a future silent detour that
12703        // returned an owned `Option<String>` (which would type-check
12704        // but silently allocate on every accessor call, breaking the
12705        // zero-cost projection every peer sibling accessor carries), a
12706        // `Some("") → None` collapse (which would silently absorb the
12707        // `LicencaEmpty` refusal case at the accessor boundary and the
12708        // caixa-helm emit path would silently fall back to `"MIT"` on
12709        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
12710        // `None → Some("MIT")` collapse (which would silently reify
12711        // the caixa-helm renderer's `"MIT"` fallback at the accessor
12712        // boundary and every downstream consumer keying off the
12713        // `Option::is_none()` discriminator would lose the "author
12714        // omitted the slot" signal).
12715        for licenca in [
12716            None,
12717            Some(""),
12718            Some("MIT"),
12719            Some("Apache-2.0 OR MIT"),
12720            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
12721            Some("MIT "),
12722            Some(" MIT"),
12723            Some("MIT\n"),
12724            Some("Apache_2.0"),
12725            Some("MIT,Apache-2.0"),
12726        ] {
12727            let c = caixa_with_licenca(licenca);
12728            assert_eq!(
12729                c.licenca(),
12730                licenca,
12731                "Caixa::licenca must return :licenca verbatim (got {:?}, \
12732                 expected {licenca:?})",
12733                c.licenca(),
12734            );
12735            assert_eq!(
12736                c.licenca(),
12737                c.licenca.as_deref(),
12738                "Caixa::licenca must byte-equal the raw \
12739                 `self.licenca.as_deref()` field access across every \
12740                 value in the Option<&str> accept-set",
12741            );
12742        }
12743    }
12744
12745    #[test]
12746    fn validate_licenca_empty_arm_routes_through_accessor() {
12747        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
12748        // must key off [`Caixa::licenca`], not the raw
12749        // `self.licenca.as_deref()` field access. Structurally: a
12750        // `Caixa { licenca: Some(""), .. }` must surface the
12751        // `LicencaEmpty` refusal exactly, and a
12752        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
12753        // single-license form) must pass validate. The pair jointly
12754        // pins the accessor + validate-gate composition: any future
12755        // silent detour that had the accessor return `None` on the
12756        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
12757        // silently absorb the `LicencaEmpty` refusal at the accessor
12758        // boundary and the validate gate would accept a struct-literal
12759        // `Caixa { licenca: Some(""), .. }` — the composition pin
12760        // catches that at caixa-core build time.
12761        //
12762        // Peer of the per-`:politicas :circuit-breaker`
12763        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12764        // accessor-composition pin
12765        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12766        // on the sibling per-M3-mesh-slot required-`u32` axis — same
12767        // "the validate / shape-gate predicate must route through the
12768        // substrate-primitive typed dispatch" discipline extended onto
12769        // the outer top-level [`Caixa`] universal-axis
12770        // `Option<&str>`-composition surface.
12771        let c = caixa_with_licenca(Some(""));
12772        assert!(
12773            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
12774            "validate_licenca must reject licenca == Some(\"\") with \
12775             LicencaEmpty — the accessor and the validate gate must \
12776             route through the same substrate-primitive typed dispatch \
12777             on the :licenca empty arm",
12778        );
12779        let c = caixa_with_licenca(Some("MIT"));
12780        assert!(
12781            c.validate_licenca().is_ok(),
12782            "validate_licenca must accept licenca == Some(\"MIT\") \
12783             (the canonical single-license SPDX shape)",
12784        );
12785    }
12786
12787    #[test]
12788    fn licenca_projects_option_str_by_borrow() {
12789        // The by-borrow pin: [`Caixa::licenca`] returns
12790        // `Option<&str>` by borrow — the `&str` borrows the underlying
12791        // `String` storage of the `Option<String>` slot and the
12792        // accessor must not allocate a fresh `String` on every call.
12793        // Peer of the per-`:placement`
12794        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12795        // borrow pin on the peer per-M3-mesh-slot
12796        // `Option<&str>`-return axis, extended onto the outer top-
12797        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12798        // accessor's returned `&str` must borrow from `&self` (the
12799        // returned reference's lifetime is tied to `&self`), and
12800        // calling the accessor twice on the same [`Caixa`] must yield
12801        // the same `Option<&str>` verbatim (idempotent, no side
12802        // effects on `&self`).
12803        //
12804        // Pins against a future silent detour that returned an owned
12805        // `Option<String>` (which would type-check but silently
12806        // allocate on every call, breaking the zero-cost projection
12807        // every peer sibling accessor carries), or a one-arm-only
12808        // accessor that returned a saturating value on some sentinel
12809        // input (breaking the pass-through invariant the sibling
12810        // required-scalar accessors carry).
12811        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
12812            let c = caixa_with_licenca(licenca);
12813            let first = c.licenca();
12814            let second = c.licenca();
12815            assert_eq!(
12816                first, second,
12817                "Caixa::licenca must be idempotent — two successive \
12818                 calls on the same &self must return the same \
12819                 Option<&str>",
12820            );
12821            assert_eq!(
12822                first, licenca,
12823                "Caixa::licenca must return :licenca verbatim by \
12824                 borrow — got {first:?}, expected {licenca:?}",
12825            );
12826        }
12827    }
12828
12829    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
12830
12831    #[test]
12832    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
12833        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
12834        // pin: [`Caixa::repositorio`] must return the `:repositorio`
12835        // typed byte-string verbatim as an `Option<&str>`, byte-equal
12836        // to the raw `self.repositorio.as_deref()` access across every
12837        // representative value in the accept-set — `None` (the "omit
12838        // the slot to defer to the per-renderer placeholder" arm every
12839        // existing fixture without a `:repositorio` line carries),
12840        // `Some("")` (a past-the-guard sentinel that pins the accessor
12841        // doesn't perform a silent `Some("") → None` collapse on the
12842        // empty arm — validate rejects `Some("")` through
12843        // `RepositorioEmpty` but the accessor must ship the raw slot
12844        // verbatim so a validate-time gate regression surfaces at the
12845        // caixa-helm / caixa-flux emit boundary rather than being
12846        // silently absorbed into the per-renderer fallback),
12847        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
12848        // shorthand every existing manifest fixture across
12849        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
12850        // `Some("https://github.com/pleme-io/checkout")` (the canonical
12851        // `https://` URL the README quickstart uses),
12852        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
12853        // `Some("git://github.com/pleme-io/checkout.git")` /
12854        // `Some("git@github.com:pleme-io/checkout.git")` /
12855        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
12856        // github scheme the shared `is_git_repo_url` predicate
12857        // documents), and five past-the-guard sentinels for the
12858        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
12859        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
12860        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
12861        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
12862        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
12863        // sentinels pin the accessor doesn't silently absorb the
12864        // refusal cases into a fallback).
12865        //
12866        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
12867        // accessor pin on the substrate primitive — sibling of the peer
12868        // [`Caixa::licenca`] (6d5bc28) pin
12869        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
12870        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
12871        // projection pin pattern this pin folds on. Sibling in shape to
12872        // the peer per-`:placement`
12873        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12874        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12875        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12876        // axes, extended onto the outer top-level [`Caixa`] universal-
12877        // axis surface. Pins against a future silent detour that
12878        // returned an owned `Option<String>` (which would type-check
12879        // but silently allocate on every accessor call, breaking the
12880        // zero-cost projection every peer sibling accessor carries), a
12881        // `Some("") → None` collapse (which would silently absorb the
12882        // `RepositorioEmpty` refusal case at the accessor boundary and
12883        // the caixa-helm `Chart.yaml` `home:` fold would silently
12884        // render a `home: null` / omitted field on a struct-literal
12885        // `Caixa { repositorio: Some(""), .. }`), or a
12886        // `None → Some(<default>)` collapse (which would silently reify
12887        // the per-renderer fallback at the accessor boundary and every
12888        // downstream consumer keying off the `Option::is_none()`
12889        // discriminator would lose the "author omitted the slot"
12890        // signal).
12891        for repositorio in [
12892            None,
12893            Some(""),
12894            Some("github:pleme-io/hello-rio"),
12895            Some("https://github.com/pleme-io/checkout"),
12896            Some("ssh://git@github.com/pleme-io/checkout.git"),
12897            Some("git://github.com/pleme-io/checkout.git"),
12898            Some("git@github.com:pleme-io/checkout.git"),
12899            Some("file:///opt/mirrors/pleme-io/checkout"),
12900            Some("pleme-io/checkout"),
12901            Some("-upload-pack=evil"),
12902            Some("github:pleme-io/checkout?ref=main"),
12903            Some("github:pleme-io/checkout#main"),
12904            Some("github:pleme-io/{tpl}"),
12905        ] {
12906            let c = caixa_with_repositorio(repositorio);
12907            assert_eq!(
12908                c.repositorio(),
12909                repositorio,
12910                "Caixa::repositorio must return :repositorio verbatim \
12911                 (got {:?}, expected {repositorio:?})",
12912                c.repositorio(),
12913            );
12914            assert_eq!(
12915                c.repositorio(),
12916                c.repositorio.as_deref(),
12917                "Caixa::repositorio must byte-equal the raw \
12918                 `self.repositorio.as_deref()` field access across every \
12919                 value in the Option<&str> accept-set",
12920            );
12921        }
12922    }
12923
12924    #[test]
12925    fn validate_repositorio_empty_arm_routes_through_accessor() {
12926        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
12927        // gate must key off [`Caixa::repositorio`], not the raw
12928        // `self.repositorio.as_deref()` field access. Structurally: a
12929        // `Caixa { repositorio: Some(""), .. }` must surface the
12930        // `RepositorioEmpty` refusal exactly, and a
12931        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
12932        // (the canonical `github:` shorthand form) must pass validate.
12933        // The pair jointly pins the accessor + validate-gate
12934        // composition: any future silent detour that had the accessor
12935        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
12936        // collapse) would silently absorb the `RepositorioEmpty` refusal
12937        // at the accessor boundary and the validate gate would accept a
12938        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
12939        // composition pin catches that at caixa-core build time.
12940        //
12941        // Peer of the [`Caixa::licenca`] (6d5bc28)
12942        // `validate_licenca_empty_arm_routes_through_accessor`
12943        // composition pin on the sibling outer top-level [`Caixa`]
12944        // `Option<&str>` universal-axis surface — same "the validate /
12945        // shape-gate predicate must route through the substrate-
12946        // primitive typed dispatch" discipline extended onto the second
12947        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
12948        // composition surface.
12949        let c = caixa_with_repositorio(Some(""));
12950        assert!(
12951            matches!(
12952                c.validate_repositorio(),
12953                Err(ManifestError::RepositorioEmpty),
12954            ),
12955            "validate_repositorio must reject repositorio == Some(\"\") \
12956             with RepositorioEmpty — the accessor and the validate gate \
12957             must route through the same substrate-primitive typed \
12958             dispatch on the :repositorio empty arm",
12959        );
12960        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
12961        assert!(
12962            c.validate_repositorio().is_ok(),
12963            "validate_repositorio must accept repositorio == \
12964             Some(\"github:pleme-io/hello-rio\") (the canonical \
12965             `github:` shorthand git-repo-URL shape)",
12966        );
12967    }
12968
12969    #[test]
12970    fn repositorio_projects_option_str_by_borrow() {
12971        // The by-borrow pin: [`Caixa::repositorio`] returns
12972        // `Option<&str>` by borrow — the `&str` borrows the underlying
12973        // `String` storage of the `Option<String>` slot and the
12974        // accessor must not allocate a fresh `String` on every call.
12975        // Peer of the per-`:placement`
12976        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
12977        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
12978        // `Option<&str>`-return axes, extended onto the second outer
12979        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
12980        // the accessor's returned `&str` must borrow from `&self` (the
12981        // returned reference's lifetime is tied to `&self`), and
12982        // calling the accessor twice on the same [`Caixa`] must yield
12983        // the same `Option<&str>` verbatim (idempotent, no side effects
12984        // on `&self`).
12985        //
12986        // Pins against a future silent detour that returned an owned
12987        // `Option<String>` (which would type-check but silently
12988        // allocate on every call, breaking the zero-cost projection
12989        // every peer sibling accessor carries), or a one-arm-only
12990        // accessor that returned a saturating value on some sentinel
12991        // input (breaking the pass-through invariant the sibling
12992        // required-scalar accessors carry).
12993        for repositorio in [
12994            None,
12995            Some(""),
12996            Some("github:pleme-io/hello-rio"),
12997            Some("https://github.com/pleme-io/checkout"),
12998        ] {
12999            let c = caixa_with_repositorio(repositorio);
13000            let first = c.repositorio();
13001            let second = c.repositorio();
13002            assert_eq!(
13003                first, second,
13004                "Caixa::repositorio must be idempotent — two successive \
13005                 calls on the same &self must return the same \
13006                 Option<&str>",
13007            );
13008            assert_eq!(
13009                first, repositorio,
13010                "Caixa::repositorio must return :repositorio verbatim by \
13011                 borrow — got {first:?}, expected {repositorio:?}",
13012            );
13013        }
13014    }
13015
13016    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
13017
13018    #[test]
13019    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
13020        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
13021        // return the author-declared `:repositorio` byte-string verbatim
13022        // on the `Some` arm — no scheme rewrite, no trailing-slash
13023        // canonicalization, no `github:` → `https://github.com/`
13024        // desugaring. The resolved-URL composer is the projection of
13025        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
13026        // the `String`-return arity every substrate-side field-fill
13027        // consumer keys off; on the `Some` arm the projection is
13028        // `str::to_owned` verbatim, so every accept-set value the
13029        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
13030        // across_permutations` pin covers (`https://…`, `github:…`,
13031        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
13032        // guard sentinel `pleme-io/…`) must survive the accessor
13033        // byte-equal. Pins against a future silent detour that rewrote
13034        // the `github:` shorthand to the `https://github.com/` full URL
13035        // at the accessor boundary (which would silently split the
13036        // resolved-URL surface from the raw [`Caixa::repositorio`]
13037        // accessor's documented pass-through invariant), or a trailing-
13038        // slash normalization (which would silently break the
13039        // FluxCD `GitRepository` `spec.url` byte-exact match every
13040        // downstream consumer keys the source-controller reconcile off).
13041        for repositorio in [
13042            "github:pleme-io/hello-rio",
13043            "https://github.com/pleme-io/checkout",
13044            "ssh://git@github.com/pleme-io/checkout.git",
13045            "git://github.com/pleme-io/checkout.git",
13046            "git@github.com:pleme-io/checkout.git",
13047            "file:///opt/mirrors/pleme-io/checkout",
13048        ] {
13049            let c = caixa_with_repositorio(Some(repositorio));
13050            assert_eq!(
13051                c.canonical_git_url(),
13052                repositorio,
13053                "Caixa::canonical_git_url on the Some arm must return \
13054                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
13055                c.canonical_git_url(),
13056            );
13057        }
13058    }
13059
13060    #[test]
13061    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
13062        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
13063        // `None` arm must emit the substrate's canonical pleme-org github
13064        // URL derived from `caixa.nome()` — `https://github.com/<org>/
13065        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
13066        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
13067        // is the exact byte-image of the prior inline
13068        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
13069        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
13070        // re-derived open-coded. Pins against a future silent detour
13071        // that migrated the `<org>` segment to a different constant (a
13072        // fork rebranding that split off a new
13073        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
13074        // to migrate onto), a scheme change (`https://` → `git://` or
13075        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
13076        // override (which would break the substrate-wide single-source-
13077        // of-truth guarantee this method encodes).
13078        let c = caixa_with_repositorio(None);
13079        let expected = format!(
13080            "https://github.com/{org}/{nome}",
13081            org = crate::DEFAULT_PLEME_GIT_ORG,
13082            nome = c.nome(),
13083        );
13084        assert_eq!(
13085            c.canonical_git_url(),
13086            expected,
13087            "Caixa::canonical_git_url on the None arm must fold through \
13088             the substrate's canonical pleme-org github URL fallback \
13089             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
13090             {:?}, expected {expected:?}",
13091            c.canonical_git_url(),
13092        );
13093    }
13094
13095    #[test]
13096    fn canonical_git_url_byte_matches_manual_composition() {
13097        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
13098        // byte-identically to the manual open-coded
13099        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
13100        //  format!("https://github.com/{org}/{nome}", ...))` composition
13101        // every prior substrate-side caller re-derived. Guards the
13102        // paired-site convergence just applied at caixa-flux's
13103        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
13104        // now routes through this accessor): a future implementation of
13105        // this method that reordered the format arguments, swapped the
13106        // `<org>` constant for a different one, or interposed a
13107        // canonicalization pass on the `Some` arm surfaces here as a
13108        // caixa-core build-time test failure rather than as a downstream
13109        // FluxCD `GitRepository` reconcile mismatch far from this
13110        // method's source.
13111        for repositorio in [
13112            None,
13113            Some("github:pleme-io/hello-rio"),
13114            Some("https://github.com/pleme-io/checkout"),
13115            Some("ssh://git@github.com/pleme-io/checkout.git"),
13116        ] {
13117            let c = caixa_with_repositorio(repositorio);
13118            let manual = c.repositorio().map_or_else(
13119                || {
13120                    format!(
13121                        "https://github.com/{org}/{nome}",
13122                        org = crate::DEFAULT_PLEME_GIT_ORG,
13123                        nome = c.nome(),
13124                    )
13125                },
13126                str::to_owned,
13127            );
13128            assert_eq!(
13129                c.canonical_git_url(),
13130                manual,
13131                "Caixa::canonical_git_url must byte-equal the manual \
13132                 open-coded `repositorio().map(str::to_owned)\
13133                 .unwrap_or_else(|| format!(...))` composition across \
13134                 every representative :repositorio input — got {:?}, \
13135                 expected {manual:?}",
13136                c.canonical_git_url(),
13137            );
13138        }
13139    }
13140
13141    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
13142
13143    #[test]
13144    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
13145        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
13146        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
13147        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
13148        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
13149        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
13150        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
13151        // the `0.0.0` boundary case. Every accept-set value the peer
13152        // validate gate lets through must survive the resolved-tag
13153        // projection byte-equal.
13154        for versao in [
13155            "0.1.0",
13156            "0.0.0",
13157            "1.0.0",
13158            "1.2.3-rc.1",
13159            "1.2.3+build.42",
13160            "1.2.3-rc.1+build.42",
13161        ] {
13162            let c = caixa_with_versao(versao);
13163            let expected = format!(
13164                "{prefix}{versao}",
13165                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13166            );
13167            assert_eq!(
13168                c.publish_tag(),
13169                expected,
13170                "Caixa::publish_tag must compose \
13171                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
13172                 :versao ({versao:?}) verbatim — got {got:?}, \
13173                 expected {expected:?}",
13174                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13175                got = c.publish_tag(),
13176            );
13177        }
13178    }
13179
13180    #[test]
13181    fn publish_tag_starts_with_default_publish_tag_prefix() {
13182        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
13183        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
13184        // byte-string on every input, guarding a hypothetical future
13185        // implementation that migrated the prefix segment to an inline
13186        // literal (`"v"`) that would silently drift from any rebrand of
13187        // the lifted constant. Peer to the sibling caixa-flux
13188        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
13189        // test which pins the same prefix invariant at the reader-side
13190        // `GitRefSpec::Tag` emit site.
13191        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
13192            let c = caixa_with_versao(versao);
13193            let tag = c.publish_tag();
13194            assert!(
13195                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
13196                "Caixa::publish_tag emission {tag:?} must start with \
13197                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
13198                 ({prefix:?})",
13199                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13200            );
13201        }
13202    }
13203
13204    #[test]
13205    fn publish_tag_byte_matches_manual_composition() {
13206        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
13207        // identically to the manual open-coded
13208        // `format!("{prefix}{versao}", prefix =
13209        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
13210        //  caixa.versao())` composition every prior substrate-side
13211        // caller re-derived. Guards the paired-site convergence just
13212        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
13213        // `git_ref` composer (which now routes through this accessor):
13214        // a future implementation of this method that reordered the
13215        // format arguments, swapped the `<prefix>` constant for a
13216        // different one, or interposed a canonicalization pass on the
13217        // `:versao` axis surfaces here as a caixa-core build-time test
13218        // failure rather than as a downstream FluxCD `GitRepository`
13219        // reconcile mismatch far from this method's source.
13220        for versao in [
13221            "0.1.0",
13222            "0.0.0",
13223            "1.2.3-rc.1",
13224            "1.2.3+build.42",
13225            "1.2.3-rc.1+build.42",
13226        ] {
13227            let c = caixa_with_versao(versao);
13228            let manual = format!(
13229                "{prefix}{versao}",
13230                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13231                versao = c.versao(),
13232            );
13233            assert_eq!(
13234                c.publish_tag(),
13235                manual,
13236                "Caixa::publish_tag must byte-equal the manual \
13237                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
13238                 composition across every representative :versao input \
13239                 — got {got:?}, expected {manual:?}",
13240                got = c.publish_tag(),
13241            );
13242        }
13243    }
13244
13245    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
13246
13247    #[test]
13248    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
13249        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
13250        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
13251        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
13252        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
13253        // set sweep documents — single-word, hyphen-joined, version-
13254        // suffixed, single-char, two-char, digit-start, retry-suffixed.
13255        // Every accept-set value the peer validate gate lets through must
13256        // survive the resolved-chart-name projection byte-equal.
13257        for nome in [
13258            "checkout",
13259            "cart-v2",
13260            "a",
13261            "db",
13262            "3rd-party-shim",
13263            "payment-retry",
13264            "0",
13265        ] {
13266            let c = caixa_with_nome(nome);
13267            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
13268            assert_eq!(
13269                c.lareira_chart_name(),
13270                expected,
13271                "Caixa::lareira_chart_name must compose \
13272                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
13273                 :nome ({nome:?}) verbatim — got {got:?}, \
13274                 expected {expected:?}",
13275                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
13276                got = c.lareira_chart_name(),
13277            );
13278        }
13279    }
13280
13281    #[test]
13282    fn lareira_chart_name_starts_with_lifted_prefix() {
13283        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
13284        // must begin with the canonical
13285        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
13286        // input, guarding a hypothetical future implementation that
13287        // migrated the prefix segment to an inline literal (`"lareira-"`)
13288        // that would silently drift from any rebrand of the lifted
13289        // constant. Peer to the sibling
13290        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
13291        // the co-resident resolved-publish-tag composer's prefix axis.
13292        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13293            let c = caixa_with_nome(nome);
13294            let chart = c.lareira_chart_name();
13295            assert!(
13296                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
13297                "Caixa::lareira_chart_name emission {chart:?} must start \
13298                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
13299                 ({prefix:?})",
13300                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
13301            );
13302        }
13303    }
13304
13305    #[test]
13306    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
13307        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
13308        // byte-identically to the manual open-coded
13309        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
13310        // composition every prior substrate-side caller re-derived.
13311        // Guards the paired-site convergence just applied at caixa-helm's
13312        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
13313        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
13314        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
13315        // composer (all of which now route through this accessor): a
13316        // future implementation of this method that reordered the
13317        // composition arguments, swapped the `<prefix>` constant for a
13318        // different one, or interposed a canonicalization pass on the
13319        // `:nome` axis surfaces here as a caixa-core build-time test
13320        // failure rather than as a downstream Helm chart-render / FluxCD
13321        // reconcile / tatara Process-CR mismatch far from this method's
13322        // source.
13323        for nome in [
13324            "checkout",
13325            "cart-v2",
13326            "a",
13327            "db",
13328            "3rd-party-shim",
13329            "payment-retry",
13330        ] {
13331            let c = caixa_with_nome(nome);
13332            let manual = crate::lareira_chart_name(c.nome());
13333            assert_eq!(
13334                c.lareira_chart_name(),
13335                manual,
13336                "Caixa::lareira_chart_name must byte-equal the manual \
13337                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
13338                 composition across every representative :nome input — \
13339                 got {got:?}, expected {manual:?}",
13340                got = c.lareira_chart_name(),
13341            );
13342        }
13343    }
13344
13345    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
13346
13347    #[test]
13348    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
13349        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
13350        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
13351        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
13352        // across the full paired `(registry, :nome)` accept-set — every
13353        // representative registry the substrate-side emitters carry
13354        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
13355        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
13356        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
13357        // inline_format` render-side pin exercises; `registry.example.
13358        // com`, an off-org shape; `localhost:5000`, the local-dev shape
13359        // every `feira chart` iteration path lands under) × every DNS-
13360        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
13361        // forms` positive-set sweep documents (single-word, hyphen-
13362        // joined, single-char, two-char, digit-start, retry-suffixed).
13363        // Every accept-set pair the peer validate gates let through must
13364        // survive the resolved-OCI-ref projection byte-equal.
13365        for registry in [
13366            "ghcr.io/pleme-io/charts",
13367            "ghcr.io/pleme-io",
13368            "registry.example.com",
13369            "localhost:5000",
13370        ] {
13371            for nome in [
13372                "checkout",
13373                "cart-v2",
13374                "a",
13375                "db",
13376                "3rd-party-shim",
13377                "payment-retry",
13378                "0",
13379            ] {
13380                let c = caixa_with_nome(nome);
13381                let expected = format!(
13382                    "{scheme}{registry}/{chart}",
13383                    scheme = crate::OCI_SCHEME_PREFIX,
13384                    chart = crate::lareira_chart_name(nome),
13385                );
13386                assert_eq!(
13387                    c.oci_chart_ref(registry),
13388                    expected,
13389                    "Caixa::oci_chart_ref must compose \
13390                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
13391                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
13392                     expected {expected:?}",
13393                    scheme = crate::OCI_SCHEME_PREFIX,
13394                    got = c.oci_chart_ref(registry),
13395                );
13396            }
13397        }
13398    }
13399
13400    #[test]
13401    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
13402        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
13403        // emission must begin with the canonical
13404        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
13405        // a hypothetical future implementation that migrated the scheme
13406        // segment to an inline literal (`"oci://"`) that would silently
13407        // drift from any rebrand of the lifted constant. Peer to the
13408        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
13409        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
13410        // co-resident resolved-publish-tag / resolved-chart-name
13411        // composers' prefix axes.
13412        for registry in [
13413            "ghcr.io/pleme-io/charts",
13414            "ghcr.io/pleme-io",
13415            "localhost:5000",
13416        ] {
13417            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13418                let c = caixa_with_nome(nome);
13419                let ref_ = c.oci_chart_ref(registry);
13420                assert!(
13421                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
13422                    "Caixa::oci_chart_ref emission {ref_:?} must start \
13423                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
13424                     — registry ({registry:?}), :nome ({nome:?})",
13425                    scheme = crate::OCI_SCHEME_PREFIX,
13426                );
13427            }
13428        }
13429    }
13430
13431    #[test]
13432    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
13433        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
13434        // identically to the manual open-coded
13435        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
13436        // composition every prior substrate-side caller re-derived.
13437        // Guards the paired-site convergence just applied at caixa-
13438        // tatara's [`derive_chart_ref`] helper (which now routes through
13439        // this accessor): a future implementation of this method that
13440        // reordered the composition arguments, swapped the `<scheme>`
13441        // constant for a different one, migrated the `<chart>` segment
13442        // off the paired [`crate::lareira_chart_name`] composer, or
13443        // interposed a canonicalization pass on either input axis
13444        // surfaces here as a caixa-core build-time test failure rather
13445        // than as a downstream `helm install` / FluxCD OCI-source
13446        // reconcile / tatara `Process`-CR mismatch far from this
13447        // method's source. Sibling to the peer
13448        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
13449        // / [`publish_tag_byte_matches_manual_composition`] /
13450        // [`canonical_git_url_byte_matches_manual_composition`] byte-
13451        // parity pins that carry the same discipline on the co-resident
13452        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
13453        // composers.
13454        for registry in [
13455            "ghcr.io/pleme-io/charts",
13456            "ghcr.io/pleme-io",
13457            "registry.example.com",
13458            "localhost:5000",
13459        ] {
13460            for nome in [
13461                "checkout",
13462                "cart-v2",
13463                "a",
13464                "db",
13465                "3rd-party-shim",
13466                "payment-retry",
13467            ] {
13468                let c = caixa_with_nome(nome);
13469                let manual = crate::oci_chart_ref(registry, c.nome());
13470                assert_eq!(
13471                    c.oci_chart_ref(registry),
13472                    manual,
13473                    "Caixa::oci_chart_ref must byte-equal the manual \
13474                     open-coded `caixa_core::oci_chart_ref(registry, \
13475                     caixa.nome())` composition across every representative \
13476                     (registry, :nome) pair — registry ({registry:?}), \
13477                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
13478                    got = c.oci_chart_ref(registry),
13479                );
13480            }
13481        }
13482    }
13483
13484    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
13485
13486    #[test]
13487    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
13488        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
13489        // pin: [`Caixa::descricao`] must return the `:descricao` typed
13490        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13491        // raw `self.descricao.as_deref()` access across every
13492        // representative value in the accept-set — `None` (the "omit
13493        // the slot to defer to the per-renderer `caixa.nome`-derived
13494        // fallback" arm every existing fixture without a `:descricao`
13495        // line carries), `Some("")` (a past-the-guard sentinel that
13496        // pins the accessor doesn't perform a silent `Some("") → None`
13497        // collapse on the empty arm — validate rejects `Some("")`
13498        // through `DescricaoEmpty` but the accessor must ship the raw
13499        // slot verbatim so a validate-time gate regression surfaces at
13500        // the caixa-helm / caixa-feira emit boundary rather than being
13501        // silently absorbed into the per-renderer `caixa.nome`-derived
13502        // fallback), `Some("Checkout flow.")` (the canonical one-line
13503        // prose descriptor the peer
13504        // `validate_descricao_accepts_canonical_value` positive sweep
13505        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
13506        // Servico.")` (the multi-byte Unicode continuation-byte shape
13507        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
13508        // multi-glyph Unicode shape the peer
13509        // `is_chart_description_shape` predicate accepts), and five
13510        // past-the-guard sentinels for the `DescricaoInvalid` refusal
13511        // cases (`Some(" Checkout flow.")` leading-whitespace,
13512        // `Some("Checkout flow. ")` trailing-whitespace,
13513        // `Some("Checkout\nflow.")` embedded-LF,
13514        // `Some("Checkout\tflow.")` embedded-TAB, and
13515        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
13516        // the accessor doesn't silently absorb the refusal cases into
13517        // a fallback).
13518        //
13519        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
13520        // accessor pin on the substrate primitive — sibling of the peer
13521        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
13522        // (cc7332d) pins that opened the "outer [`Caixa`]
13523        // `Option<&str>` scalar" projection pin pattern this pin folds
13524        // on. Sibling in shape to the peer per-`:placement`
13525        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13526        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13527        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13528        // axes, extended onto the outer top-level [`Caixa`] universal-
13529        // axis surface. Pins against a future silent detour that
13530        // returned an owned `Option<String>` (which would type-check
13531        // but silently allocate on every accessor call, breaking the
13532        // zero-cost projection every peer sibling accessor carries), a
13533        // `Some("") → None` collapse (which would silently absorb the
13534        // `DescricaoEmpty` refusal case at the accessor boundary and
13535        // the caixa-helm `Chart.yaml` `description:` fold would
13536        // silently render a `caixa.nome`-derived fallback on a
13537        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
13538        // `None → Some(<default>)` collapse (which would silently
13539        // reify the per-renderer `caixa.nome`-derived fallback at the
13540        // accessor boundary and every downstream consumer keying off
13541        // the `Option::is_none()` discriminator would lose the "author
13542        // omitted the slot" signal).
13543        for descricao in [
13544            None,
13545            Some(""),
13546            Some("Checkout flow."),
13547            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13548            Some("→ — · ✓"),
13549            Some(" Checkout flow."),
13550            Some("Checkout flow. "),
13551            Some("Checkout\nflow."),
13552            Some("Checkout\tflow."),
13553            Some("Checkout\x00flow."),
13554        ] {
13555            let c = caixa_with_descricao(descricao);
13556            assert_eq!(
13557                c.descricao(),
13558                descricao,
13559                "Caixa::descricao must return :descricao verbatim (got \
13560                 {:?}, expected {descricao:?})",
13561                c.descricao(),
13562            );
13563            assert_eq!(
13564                c.descricao(),
13565                c.descricao.as_deref(),
13566                "Caixa::descricao must byte-equal the raw \
13567                 `self.descricao.as_deref()` field access across every \
13568                 value in the Option<&str> accept-set",
13569            );
13570        }
13571    }
13572
13573    #[test]
13574    fn validate_descricao_empty_arm_routes_through_accessor() {
13575        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
13576        // gate must key off [`Caixa::descricao`], not the raw
13577        // `self.descricao.as_deref()` field access. Structurally: a
13578        // `Caixa { descricao: Some(""), .. }` must surface the
13579        // `DescricaoEmpty` refusal exactly, and a
13580        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
13581        // canonical one-line-prose form) must pass validate. The pair
13582        // jointly pins the accessor + validate-gate composition: any
13583        // future silent detour that had the accessor return `None` on
13584        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
13585        // silently absorb the `DescricaoEmpty` refusal at the accessor
13586        // boundary and the validate gate would accept a struct-literal
13587        // `Caixa { descricao: Some(""), .. }` — the composition pin
13588        // catches that at caixa-core build time.
13589        //
13590        // Peer of the [`Caixa::licenca`] (6d5bc28)
13591        // `validate_licenca_empty_arm_routes_through_accessor` and
13592        // [`Caixa::repositorio`] (cc7332d)
13593        // `validate_repositorio_empty_arm_routes_through_accessor`
13594        // composition pins on the sibling outer top-level [`Caixa`]
13595        // `Option<&str>` universal-axis surface — same "the validate /
13596        // shape-gate predicate must route through the substrate-
13597        // primitive typed dispatch" discipline extended onto the third
13598        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
13599        // composition surface.
13600        let c = caixa_with_descricao(Some(""));
13601        assert!(
13602            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
13603            "validate_descricao must reject descricao == Some(\"\") \
13604             with DescricaoEmpty — the accessor and the validate gate \
13605             must route through the same substrate-primitive typed \
13606             dispatch on the :descricao empty arm",
13607        );
13608        let c = caixa_with_descricao(Some("Checkout flow."));
13609        assert!(
13610            c.validate_descricao().is_ok(),
13611            "validate_descricao must accept descricao == \
13612             Some(\"Checkout flow.\") (the canonical one-line-prose \
13613             chart-description shape)",
13614        );
13615    }
13616
13617    #[test]
13618    fn descricao_projects_option_str_by_borrow() {
13619        // The by-borrow pin: [`Caixa::descricao`] returns
13620        // `Option<&str>` by borrow — the `&str` borrows the underlying
13621        // `String` storage of the `Option<String>` slot and the
13622        // accessor must not allocate a fresh `String` on every call.
13623        // Peer of the [`Caixa::licenca`] (6d5bc28) and
13624        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
13625        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
13626        // the per-`:placement`
13627        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13628        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13629        // return axis, extended onto the third outer top-level
13630        // [`Caixa`] universal-axis `Option<&str>` shape — the
13631        // accessor's returned `&str` must borrow from `&self` (the
13632        // returned reference's lifetime is tied to `&self`), and
13633        // calling the accessor twice on the same [`Caixa`] must yield
13634        // the same `Option<&str>` verbatim (idempotent, no side
13635        // effects on `&self`).
13636        //
13637        // Pins against a future silent detour that returned an owned
13638        // `Option<String>` (which would type-check but silently
13639        // allocate on every call, breaking the zero-cost projection
13640        // every peer sibling accessor carries), or a one-arm-only
13641        // accessor that returned a saturating value on some sentinel
13642        // input (breaking the pass-through invariant the sibling
13643        // required-scalar accessors carry).
13644        for descricao in [
13645            None,
13646            Some(""),
13647            Some("Checkout flow."),
13648            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13649        ] {
13650            let c = caixa_with_descricao(descricao);
13651            let first = c.descricao();
13652            let second = c.descricao();
13653            assert_eq!(
13654                first, second,
13655                "Caixa::descricao must be idempotent — two successive \
13656                 calls on the same &self must return the same \
13657                 Option<&str>",
13658            );
13659            assert_eq!(
13660                first, descricao,
13661                "Caixa::descricao must return :descricao verbatim by \
13662                 borrow — got {first:?}, expected {descricao:?}",
13663            );
13664        }
13665    }
13666
13667    // ── validate_edicao — universal-axis language-edition shape ──
13668
13669    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
13670        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13671        c.edicao = edicao.map(String::from);
13672        c
13673    }
13674
13675    #[test]
13676    fn validate_edicao_accepts_none() {
13677        // The omit-the-slot identity: `:edicao` is optional. The
13678        // gate is a no-op when the author didn't declare a value —
13679        // every caixa without an `:edicao` line trivially passes,
13680        // and the substrate-side build pipeline falls back to the
13681        // documented default edition. Mirrors the peer
13682        // `validate_licenca_accepts_none` posture on the sibling
13683        // `Option<String>` Caixa slot.
13684        let c = caixa_with_edicao(None);
13685        c.validate_edicao().unwrap();
13686    }
13687
13688    #[test]
13689    fn validate_edicao_accepts_canonical_value() {
13690        // Positive control: the canonical `"2026"` edition every
13691        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
13692        // `caixa-mesh`) carries by construction passes the gate.
13693        // Future-introduced sibling editions (`"2027"`, `"2030"`,
13694        // `"2049"`) that match the same 4-digit ASCII decimal year
13695        // shape must also trivially pass — the structural shape
13696        // predicate accepts every well-formed year regardless of
13697        // whether the substrate yet understands the specific value
13698        // (a future known-edition allowlist tightens that).
13699        for ed in ["2026", "2027", "2030", "2049"] {
13700            let c = caixa_with_edicao(Some(ed));
13701            c.validate_edicao()
13702                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
13703        }
13704    }
13705
13706    #[test]
13707    fn validate_edicao_rejects_empty_some() {
13708        // Canonical paste-from-blank-doc footgun. Without this gate
13709        // the empty `Some("")` silently lands as `(:edicao "")` in
13710        // the rendered caixa.lisp and a future renderer-side
13711        // consumer's `Option::unwrap_or_else` (which only fires on
13712        // `None`) skips its fallback. Mirrors the peer
13713        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
13714        // `Option<String>` Caixa slot.
13715        let c = caixa_with_edicao(Some(""));
13716        let err = c.validate_edicao().unwrap_err();
13717        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13718    }
13719
13720    #[test]
13721    fn validate_edicao_rejects_free_form_non_year() {
13722        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
13723        // `"nightly"` shapes carry no operational meaning on the
13724        // substrate's build-time edition selector. Until this gate
13725        // landed the bare empty-arm check let every such value
13726        // through and broke far from the source caixa.lisp. Peer
13727        // with the shape-predicate cascade
13728        // `validate_repositorio_rejects_missing_colon_separator`
13729        // establishes past its own empty arm.
13730        for ed in ["x", "latest", "nightly", "stable"] {
13731            let c = caixa_with_edicao(Some(ed));
13732            let err = c.validate_edicao().unwrap_err();
13733            assert!(
13734                matches!(err, ManifestError::EdicaoInvalid { .. }),
13735                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13736            );
13737        }
13738    }
13739
13740    #[test]
13741    fn validate_edicao_rejects_trailing_whitespace() {
13742        // Paste-from-doc whitespace footgun. A trailing space in
13743        // the `:edicao` value would silently break the substrate's
13744        // build-time edition match-table lookup at the rendered
13745        // artifact's edition-selector consumer. The shape predicate
13746        // refuses every whitespace byte by construction (any byte
13747        // outside `0-9` fails `is_ascii_digit`). Peer with
13748        // `validate_repositorio_rejects_whitespace`.
13749        let c = caixa_with_edicao(Some("2026 "));
13750        let err = c.validate_edicao().unwrap_err();
13751        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
13752            panic!("expected EdicaoInvalid, got {err:?}");
13753        };
13754        assert_eq!(edicao, "2026 ");
13755    }
13756
13757    #[test]
13758    fn validate_edicao_rejects_leading_whitespace() {
13759        // Symmetric paste-from-doc whitespace footgun on the leading
13760        // boundary — the gate refuses every shape with a non-digit
13761        // byte by construction.
13762        let c = caixa_with_edicao(Some(" 2026"));
13763        let err = c.validate_edicao().unwrap_err();
13764        assert!(
13765            matches!(err, ManifestError::EdicaoInvalid { .. }),
13766            "got {err:?}",
13767        );
13768    }
13769
13770    #[test]
13771    fn validate_edicao_rejects_control_char() {
13772        // Paste-from-multiline-doc CRLF footgun — control characters
13773        // at the value boundary break the substrate's build-time
13774        // edition-selector parser. Peer with
13775        // `validate_repositorio_rejects_control_char`.
13776        let c = caixa_with_edicao(Some("2026\n"));
13777        let err = c.validate_edicao().unwrap_err();
13778        assert!(
13779            matches!(err, ManifestError::EdicaoInvalid { .. }),
13780            "got {err:?}",
13781        );
13782    }
13783
13784    #[test]
13785    fn validate_edicao_rejects_non_ascii_lookalike() {
13786        // Fullwidth-keyboard look-alike footgun — `"2026"` is
13787        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
13788        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
13789        // edition selector wants an ASCII year, and the gate
13790        // refuses every non-ASCII shape by construction (length in
13791        // bytes is 12 ≠ 4, *and* every byte falls outside
13792        // `is_ascii_digit`'s `0-9` range).
13793        let c = caixa_with_edicao(Some("2026"));
13794        let err = c.validate_edicao().unwrap_err();
13795        assert!(
13796            matches!(err, ManifestError::EdicaoInvalid { .. }),
13797            "got {err:?}",
13798        );
13799    }
13800
13801    #[test]
13802    fn validate_edicao_rejects_version_tag_prefix() {
13803        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
13804        // / `"r2026"` are familiar shapes from git-tag / Rust
13805        // edition / release-tag conventions that don't apply to
13806        // the year-shaped edition axis. The shape predicate refuses
13807        // every leading non-digit prefix.
13808        for ed in ["v2026", "e2026", "r2026"] {
13809            let c = caixa_with_edicao(Some(ed));
13810            let err = c.validate_edicao().unwrap_err();
13811            assert!(
13812                matches!(err, ManifestError::EdicaoInvalid { .. }),
13813                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13814            );
13815        }
13816    }
13817
13818    #[test]
13819    fn validate_edicao_rejects_decimal_shape() {
13820        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
13821        // `"2026.0"` are familiar shapes from semver / float
13822        // conventions that don't apply to the year-shaped edition
13823        // axis. The shape predicate refuses every non-digit byte
13824        // (`.` falls outside `is_ascii_digit`).
13825        for ed in ["2026.1", "2026.0", "2026.0.1"] {
13826            let c = caixa_with_edicao(Some(ed));
13827            let err = c.validate_edicao().unwrap_err();
13828            assert!(
13829                matches!(err, ManifestError::EdicaoInvalid { .. }),
13830                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13831            );
13832        }
13833    }
13834
13835    #[test]
13836    fn validate_edicao_rejects_wrong_length_numeric() {
13837        // Wrong-length numeric footgun — `"26"` (truncated) /
13838        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
13839        // (zero-padded too wide) all parse as integers but don't
13840        // name a 4-digit year. The shape predicate refuses every
13841        // value whose length isn't exactly 4 bytes.
13842        for ed in ["26", "202", "20260", "00026", "9"] {
13843            let c = caixa_with_edicao(Some(ed));
13844            let err = c.validate_edicao().unwrap_err();
13845            assert!(
13846                matches!(err, ManifestError::EdicaoInvalid { .. }),
13847                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13848            );
13849        }
13850    }
13851
13852    #[test]
13853    fn validate_edicao_empty_takes_precedence_over_shape() {
13854        // Empty-first cascade pin: the empty `Some("")` surfaces
13855        // the narrower `EdicaoEmpty` not the shape-predicate-
13856        // wrapped `EdicaoInvalid`, mirroring the peer
13857        // `validate_repositorio_empty_takes_precedence_over_shape`
13858        // (`RepositorioEmpty` → `RepositorioInvalid`),
13859        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
13860        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
13861        // cascades. The shape predicate also refuses the empty
13862        // input (defensively — `s.len() != 4`), but the
13863        // manifest-layer empty arm runs first to surface the
13864        // narrower diagnostic verbatim.
13865        let c = caixa_with_edicao(Some(""));
13866        let err = c.validate_edicao().unwrap_err();
13867        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13868    }
13869
13870    #[test]
13871    fn validate_edicao_template_passes() {
13872        // Round-trip pin: the bare `Caixa::template` shape (which
13873        // carries `:edicao "2026"` verbatim) passes the gate by
13874        // construction. A future template-shape change that
13875        // introduced `(:edicao "")` or a non-year value would
13876        // surface here as a regression. Mirrors the peer
13877        // `validate_licenca_template_passes` pin.
13878        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13879        c.validate_edicao().unwrap();
13880    }
13881
13882    #[test]
13883    fn validate_edicao_diagnostic_names_offending_slot() {
13884        // Diagnostic-shape pin (peer with
13885        // `validate_licenca_diagnostic_names_offending_slot`): the
13886        // error's Display surfaces the `:edicao` slot name verbatim,
13887        // so a `feira lint` run can render the diagnostic without
13888        // re-parsing and the author can grep their caixa.lisp for
13889        // the offending `:edicao` line.
13890        let c = caixa_with_edicao(Some(""));
13891        let rendered = c.validate_edicao().unwrap_err().to_string();
13892        assert!(
13893            rendered.contains(":edicao"),
13894            "diagnostic must name the offending slot: {rendered}",
13895        );
13896    }
13897
13898    #[test]
13899    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
13900        // Diagnostic-shape pin on the shape-predicate arm (peer
13901        // with `validate_repositorio_diagnostic_carries_offending_value`):
13902        // the error's Display surfaces the offending value + slot
13903        // name verbatim, so a `feira lint` run can render the
13904        // diagnostic without re-parsing and the author can grep
13905        // their caixa.lisp for the offending `:edicao` value.
13906        let c = caixa_with_edicao(Some("v2026"));
13907        let rendered = c.validate_edicao().unwrap_err().to_string();
13908        assert!(
13909            rendered.contains(":edicao"),
13910            "diagnostic must name the offending slot: {rendered}",
13911        );
13912        assert!(
13913            rendered.contains("v2026"),
13914            "diagnostic must quote the offending value: {rendered}",
13915        );
13916    }
13917
13918    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
13919
13920    #[test]
13921    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
13922        // The canonical per-`Caixa` `:edicao` language-edition scalar
13923        // pin: [`Caixa::edicao`] must return the `:edicao` typed
13924        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13925        // raw `self.edicao.as_deref()` access across every representative
13926        // value in the accept-set — `None` (the "omit the slot to defer
13927        // to the substrate's default edition" arm every existing
13928        // [`caixa-resolver`] fixture without an `:edicao` line carries),
13929        // `Some("")` (a past-the-guard sentinel that pins the accessor
13930        // doesn't perform a silent `Some("") → None` collapse on the
13931        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
13932        // but the accessor must ship the raw slot verbatim so a
13933        // validate-time gate regression surfaces at any future edition-
13934        // aware consumer's boundary rather than being silently absorbed
13935        // into the substrate's default edition), `Some("2026")` (the
13936        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
13937        // template scaffolds via [`Caixa::template`] and every
13938        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
13939        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
13940        // carries by construction), `Some("2018")` / `Some("2021")` /
13941        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
13942        // peer with Cargo's `[package] edition` grammar every future-
13943        // introduced sibling to `"2026"` will follow), and eight
13944        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
13945        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
13946        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
13947        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
13948        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
13949        // length-numeric, `Some("latest")` free-form-non-year — the
13950        // sentinels pin the accessor doesn't silently absorb the
13951        // refusal cases into a substrate-default-edition fallback).
13952        //
13953        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
13954        // return scalar accessor pin on the substrate primitive —
13955        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
13956        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13957        // (3f16e2f) pins that opened the "outer [`Caixa`]
13958        // `Option<&str>` scalar" projection pin pattern this pin folds
13959        // on. Sibling in shape to the peer per-`:placement`
13960        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13961        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13962        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13963        // axes, extended onto the outer top-level [`Caixa`] universal-
13964        // axis surface's last unlifted `Option<String>` slot. Pins
13965        // against a future silent detour that returned an owned
13966        // `Option<String>` (which would type-check but silently
13967        // allocate on every accessor call, breaking the zero-cost
13968        // projection every peer sibling accessor carries), a
13969        // `Some("") → None` collapse (which would silently absorb the
13970        // `EdicaoEmpty` refusal case at the accessor boundary and any
13971        // future edition-aware consumer would silently fall back to
13972        // the substrate's default edition on a struct-literal
13973        // `Caixa { edicao: Some(""), .. }`), or a
13974        // `None → Some("2026")` collapse (which would silently reify
13975        // the substrate's default edition at the accessor boundary
13976        // and every downstream consumer keying off the
13977        // `Option::is_none()` discriminator would lose the "author
13978        // omitted the slot" signal).
13979        for edicao in [
13980            None,
13981            Some(""),
13982            Some("2026"),
13983            Some("2018"),
13984            Some("2021"),
13985            Some("2024"),
13986            Some("2026 "),
13987            Some(" 2026"),
13988            Some("2026\n"),
13989            Some("2026"),
13990            Some("v2026"),
13991            Some("2026.1"),
13992            Some("26"),
13993            Some("latest"),
13994        ] {
13995            let c = caixa_with_edicao(edicao);
13996            assert_eq!(
13997                c.edicao(),
13998                edicao,
13999                "Caixa::edicao must return :edicao verbatim (got {:?}, \
14000                 expected {edicao:?})",
14001                c.edicao(),
14002            );
14003            assert_eq!(
14004                c.edicao(),
14005                c.edicao.as_deref(),
14006                "Caixa::edicao must byte-equal the raw \
14007                 `self.edicao.as_deref()` field access across every \
14008                 value in the Option<&str> accept-set",
14009            );
14010        }
14011    }
14012
14013    #[test]
14014    fn validate_edicao_empty_arm_routes_through_accessor() {
14015        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
14016        // must key off [`Caixa::edicao`], not the raw
14017        // `self.edicao.as_deref()` field access. Structurally: a
14018        // `Caixa { edicao: Some(""), .. }` must surface the
14019        // `EdicaoEmpty` refusal exactly, and a
14020        // `Caixa { edicao: Some("2026"), .. }` (the canonical
14021        // 4-digit-ASCII-decimal-year form) must pass validate. The
14022        // pair jointly pins the accessor + validate-gate composition:
14023        // any future silent detour that had the accessor return `None`
14024        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
14025        // would silently absorb the `EdicaoEmpty` refusal at the
14026        // accessor boundary and the validate gate would accept a
14027        // struct-literal `Caixa { edicao: Some(""), .. }` — the
14028        // composition pin catches that at caixa-core build time.
14029        //
14030        // Peer of the [`Caixa::licenca`] (6d5bc28)
14031        // `validate_licenca_empty_arm_routes_through_accessor`,
14032        // [`Caixa::repositorio`] (cc7332d)
14033        // `validate_repositorio_empty_arm_routes_through_accessor`,
14034        // and [`Caixa::descricao`] (3f16e2f)
14035        // `validate_descricao_empty_arm_routes_through_accessor`
14036        // composition pins on the sibling outer top-level [`Caixa`]
14037        // `Option<&str>` universal-axis surface — same "the validate /
14038        // shape-gate predicate must route through the substrate-
14039        // primitive typed dispatch" discipline extended onto the
14040        // fourth and final outer top-level [`Caixa`] universal-axis
14041        // `Option<&str>`-composition surface, closing the accessor-
14042        // composition family.
14043        let c = caixa_with_edicao(Some(""));
14044        assert!(
14045            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
14046            "validate_edicao must reject edicao == Some(\"\") with \
14047             EdicaoEmpty — the accessor and the validate gate must \
14048             route through the same substrate-primitive typed dispatch \
14049             on the :edicao empty arm",
14050        );
14051        let c = caixa_with_edicao(Some("2026"));
14052        assert!(
14053            c.validate_edicao().is_ok(),
14054            "validate_edicao must accept edicao == Some(\"2026\") \
14055             (the canonical 4-digit-ASCII-decimal-year shape)",
14056        );
14057    }
14058
14059    #[test]
14060    fn edicao_projects_option_str_by_borrow() {
14061        // The by-borrow pin: [`Caixa::edicao`] returns
14062        // `Option<&str>` by borrow — the `&str` borrows the underlying
14063        // `String` storage of the `Option<String>` slot and the
14064        // accessor must not allocate a fresh `String` on every call.
14065        // Peer of the [`Caixa::licenca`] (6d5bc28),
14066        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
14067        // (3f16e2f) by-borrow pins on the peer outer top-level
14068        // [`Caixa`] `Option<&str>`-return axes, and of the
14069        // per-`:placement`
14070        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
14071        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
14072        // return axis, extended onto the fourth and final outer top-
14073        // level [`Caixa`] universal-axis `Option<&str>` shape — the
14074        // accessor's returned `&str` must borrow from `&self` (the
14075        // returned reference's lifetime is tied to `&self`), and
14076        // calling the accessor twice on the same [`Caixa`] must yield
14077        // the same `Option<&str>` verbatim (idempotent, no side
14078        // effects on `&self`).
14079        //
14080        // Pins against a future silent detour that returned an owned
14081        // `Option<String>` (which would type-check but silently
14082        // allocate on every call, breaking the zero-cost projection
14083        // every peer sibling accessor carries), or a one-arm-only
14084        // accessor that returned a saturating value on some sentinel
14085        // input (breaking the pass-through invariant the sibling
14086        // required-scalar accessors carry).
14087        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
14088            let c = caixa_with_edicao(edicao);
14089            let first = c.edicao();
14090            let second = c.edicao();
14091            assert_eq!(
14092                first, second,
14093                "Caixa::edicao must be idempotent — two successive \
14094                 calls on the same &self must return the same \
14095                 Option<&str>",
14096            );
14097            assert_eq!(
14098                first, edicao,
14099                "Caixa::edicao must return :edicao verbatim by \
14100                 borrow — got {first:?}, expected {edicao:?}",
14101            );
14102        }
14103    }
14104
14105    #[test]
14106    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
14107        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
14108        // label caixa-identity scalar pin: [`Caixa::nome`] must return
14109        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
14110        // the raw field access across every representative value in
14111        // the accept-set — the canonical `"demo"` template baseline
14112        // (the same `feira init`-scaffolded default the sibling
14113        // `validate_nome_accepts_canonical_template` positive-control
14114        // gate pins), plus every sibling per-typed-slot atom accessor's
14115        // canonical positive-arm byte-string (`"catalog"` per
14116        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
14117        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
14118        // `caixa-helm`/`caixa-flux` cross-crate integration-test
14119        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
14120        // canonical example), plus every past-the-guard sentinel for
14121        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
14122        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
14123        // the bare DNS-1123 63-byte cap but overflows the joint
14124        // `lareira-<nome>` chart-name budget the sibling
14125        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
14126        //
14127        // The past-the-guard sentinels pin the accessor doesn't
14128        // silently absorb the refusal cases into a template-derived
14129        // fallback (a future `.nome().is_empty().then(|| "demo")`
14130        // collapse would silently absorb the `NomeEmpty` refusal at
14131        // the accessor boundary and the validate gate would accept a
14132        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
14133        // catches that at caixa-core build time).
14134        //
14135        // First outer top-level [`Caixa`] `&str`-return required-
14136        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
14137        // required-scalar" projection pattern the sibling per-`Caixa`
14138        // `:versao` future lift folds on. Sibling in shape to the peer
14139        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
14140        // required-`String`-carry accessor pin on the sibling per-
14141        // sub-struct required-axis, extended onto the outer top-level
14142        // [`Caixa`] universal-axis required-`String`-carry axis.
14143        for nome in [
14144            "demo",
14145            "catalog",
14146            "cart",
14147            "hello-rio",
14148            "checkout",
14149            "",
14150            "Bad_Name",
14151            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
14152        ] {
14153            let c = caixa_with_nome(nome);
14154            assert_eq!(
14155                c.nome(),
14156                nome,
14157                "Caixa::nome must return :nome verbatim (got {}, \
14158                 expected {nome})",
14159                c.nome(),
14160            );
14161            assert_eq!(
14162                c.nome(),
14163                c.nome.as_str(),
14164                "Caixa::nome must byte-equal the raw .nome field \
14165                 access across every value in the String accept-set",
14166            );
14167        }
14168    }
14169
14170    #[test]
14171    fn validate_nome_empty_arm_routes_through_accessor() {
14172        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
14173        // key off [`Caixa::nome`], not the raw `.nome` field access.
14174        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
14175        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
14176        // template baseline (the peer positive-arm the sibling
14177        // `validate_nome_accepts_canonical_template` gate carves out)
14178        // must pass validate. The pair jointly pins the accessor +
14179        // validate-gate composition: any future silent detour that
14180        // had the accessor return a fresh `"demo"` on the empty arm
14181        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
14182        // would silently absorb the `NomeEmpty` refusal at the
14183        // accessor boundary and the validate gate would accept a
14184        // struct-literal `Caixa { nome: "".into(), .. }` — the
14185        // composition pin catches that at caixa-core build time.
14186        //
14187        // Peer of the sibling per-`Caixa`
14188        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
14189        // / `validate_repositorio_empty_arm_routes_through_accessor`
14190        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
14191        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
14192        // (2641cbd) composition pins on the sibling outer top-level
14193        // [`Caixa`] `Option<&str>` axes — same "the validate /
14194        // shape-gate predicate must route through the substrate-
14195        // primitive typed dispatch" discipline extended onto the peer
14196        // outer top-level [`Caixa`] required-`&str` composition axis.
14197        let c = caixa_with_nome("");
14198        assert!(
14199            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
14200            "validate_nome must reject nome == \"\" with NomeEmpty — \
14201             the accessor and the validate gate must route through the \
14202             same substrate-primitive typed dispatch on the :nome \
14203             empty-arm",
14204        );
14205        let c = caixa_with_nome("demo");
14206        assert!(
14207            c.validate_nome().is_ok(),
14208            "validate_nome must accept nome == \"demo\" (the canonical \
14209             DNS-1123-label template baseline)",
14210        );
14211    }
14212
14213    #[test]
14214    fn nome_projects_str_by_borrow() {
14215        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
14216        // — the `&str` borrows the underlying `String` storage of the
14217        // required `nome` slot and the accessor must not allocate a
14218        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
14219        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
14220        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
14221        // by-borrow pins on the peer outer top-level [`Caixa`]
14222        // `Option<&str>`-return axes, extended onto the first outer
14223        // top-level [`Caixa`] required-`&str`-return axis — the
14224        // accessor's returned `&str` must borrow from `&self` (the
14225        // returned reference's lifetime is tied to `&self`), and
14226        // calling the accessor twice on the same [`Caixa`] must yield
14227        // the same `&str` verbatim (idempotent, no side effects on
14228        // `&self`).
14229        //
14230        // Pins against a future silent detour that returned an owned
14231        // `String` (which would type-check but silently allocate on
14232        // every call, breaking the zero-cost projection every peer
14233        // sibling accessor carries), an accidental
14234        // `.nome.to_lowercase()` detour that returned a fresh
14235        // allocation through an already-DNS-1123-lowercase-only
14236        // string (breaking a future `const fn` regression), or a
14237        // one-arm-only accessor that returned a canonicalized value
14238        // on some sentinel input (breaking the pass-through invariant
14239        // the sibling required-scalar accessors carry).
14240        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
14241            let c = caixa_with_nome(nome);
14242            let first = c.nome();
14243            let second = c.nome();
14244            assert_eq!(
14245                first, second,
14246                "Caixa::nome must be idempotent — two successive calls \
14247                 on the same &self must return the same &str",
14248            );
14249            assert_eq!(
14250                first, nome,
14251                "Caixa::nome must return :nome verbatim by borrow — \
14252                 got {first}, expected {nome}",
14253            );
14254        }
14255    }
14256
14257    #[test]
14258    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
14259        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
14260        // pinned-version scalar pin: [`Caixa::versao`] must return the
14261        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
14262        // raw `.versao` field access across every representative value
14263        // in the accept-set — the canonical `"0.1.0"` template baseline
14264        // (the same `feira init`-scaffolded default the sibling
14265        // `validate_versao_accepts_canonical_template` positive-control
14266        // gate pins), plus every canonical SemVer-2 shape the sibling
14267        // `validate_versao_accepts_canonical_forms` positive-arm sweep
14268        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
14269        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
14270        // `"10.20.30"`), plus every past-the-guard sentinel for the
14271        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
14272        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
14273        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
14274        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
14275        // `"latest"` the docker-tag-shape footgun — the sentinels pin
14276        // the accessor doesn't silently absorb the refusal cases into a
14277        // template-derived fallback like `"0.1.0"`).
14278        //
14279        // The past-the-guard sentinels pin the accessor doesn't silently
14280        // absorb the refusal cases into a template-derived fallback (a
14281        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
14282        // silently absorb the `VersaoEmpty` refusal at the accessor
14283        // boundary and the validate gate would accept a struct-literal
14284        // `Caixa { versao: "".into(), .. }` — the pin catches that at
14285        // caixa-core build time).
14286        //
14287        // Second outer top-level [`Caixa`] `&str`-return required-scalar
14288        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
14289        // scalar" projection pattern the sibling per-`Caixa`
14290        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
14291        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
14292        // (4127bb6) / per-`:children`
14293        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
14294        // / per-`:upgrade-from`
14295        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
14296        // struct `:versao`-shaped `&str`-return accessor pins on the
14297        // sibling per-typed-slot version-carrier axes, extended onto the
14298        // second outer top-level [`Caixa`] universal-axis required-
14299        // `String`-carry axis so the two universal-axis identity-
14300        // carrying scalars every `defcaixa` form supplies (`:nome` +
14301        // `:versao`) share the same "one typed dispatch per axis" pin
14302        // discipline.
14303        for versao in [
14304            "0.1.0",
14305            "0.0.0",
14306            "1.0.0",
14307            "0.2.0-rc.1",
14308            "1.0.0-alpha.0",
14309            "1.0.0+build.42",
14310            "1.0.0-rc.1+build.42",
14311            "10.20.30",
14312            "",
14313            "v0.1.0",
14314            "0.1",
14315            "^0.1",
14316            "0.1.0.0",
14317            "latest",
14318        ] {
14319            let c = caixa_with_versao(versao);
14320            assert_eq!(
14321                c.versao(),
14322                versao,
14323                "Caixa::versao must return :versao verbatim (got {}, \
14324                 expected {versao})",
14325                c.versao(),
14326            );
14327            assert_eq!(
14328                c.versao(),
14329                c.versao.as_str(),
14330                "Caixa::versao must byte-equal the raw .versao field \
14331                 access across every value in the String accept-set",
14332            );
14333        }
14334    }
14335
14336    #[test]
14337    fn validate_versao_empty_arm_routes_through_accessor() {
14338        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
14339        // must key off [`Caixa::versao`], not the raw `.versao` field
14340        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
14341        // surface the `VersaoEmpty` refusal exactly, and the canonical
14342        // `"0.1.0"` template baseline (the peer positive-arm the sibling
14343        // `validate_versao_accepts_canonical_template` gate carves out)
14344        // must pass validate. The pair jointly pins the accessor +
14345        // validate-gate composition: any future silent detour that had
14346        // the accessor return a fresh `"0.1.0"` on the empty arm
14347        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
14348        // would silently absorb the `VersaoEmpty` refusal at the
14349        // accessor boundary and the validate gate would accept a
14350        // struct-literal `Caixa { versao: "".into(), .. }` — the
14351        // composition pin catches that at caixa-core build time.
14352        //
14353        // Peer of the sibling per-`Caixa`
14354        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
14355        // composition pin on the sibling outer top-level [`Caixa`]
14356        // required-`&str` universal-axis surface — same "the validate /
14357        // shape-gate predicate must route through the substrate-
14358        // primitive typed dispatch" discipline extended onto the peer
14359        // outer top-level [`Caixa`] required-`&str` universal-axis
14360        // pinned-version composition axis, closing the second
14361        // coordinate of the "one canonical typed dispatch per per-Caixa
14362        // required-`&str` universal-axis" discipline.
14363        let c = caixa_with_versao("");
14364        assert!(
14365            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
14366            "validate_versao must reject versao == \"\" with VersaoEmpty — \
14367             the accessor and the validate gate must route through the \
14368             same substrate-primitive typed dispatch on the :versao \
14369             empty-arm",
14370        );
14371        let c = caixa_with_versao("0.1.0");
14372        assert!(
14373            c.validate_versao().is_ok(),
14374            "validate_versao must accept versao == \"0.1.0\" (the \
14375             canonical SemVer-2 template baseline)",
14376        );
14377    }
14378
14379    #[test]
14380    fn versao_projects_str_by_borrow() {
14381        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
14382        // — the `&str` borrows the underlying `String` storage of the
14383        // required `versao` slot and the accessor must not allocate a
14384        // fresh `String` on every call. Peer of the [`Caixa::nome`]
14385        // (e6b7d97) by-borrow pin on the sibling outer top-level
14386        // [`Caixa`] required-`&str`-return axis, extended onto the
14387        // second outer top-level [`Caixa`] required-`&str`-return
14388        // universal-axis pinned-version surface — the accessor's
14389        // returned `&str` must borrow from `&self` (the returned
14390        // reference's lifetime is tied to `&self`), and calling the
14391        // accessor twice on the same [`Caixa`] must yield the same
14392        // `&str` verbatim (idempotent, no side effects on `&self`).
14393        //
14394        // Pins against a future silent detour that returned an owned
14395        // `String` (which would type-check but silently allocate on
14396        // every call, breaking the zero-cost projection every peer
14397        // sibling accessor carries), an accidental
14398        // `semver::Version::parse(&self.versao).unwrap().to_string()`
14399        // detour that returned a canonicalized fresh allocation through
14400        // an already-canonical byte-string (breaking a future `const fn`
14401        // regression and silently absorbing the `VersaoInvalid` refusal
14402        // at the accessor boundary), or a one-arm-only accessor that
14403        // returned a canonicalized value on some sentinel input
14404        // (breaking the pass-through invariant the sibling required-
14405        // scalar accessors carry).
14406        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
14407            let c = caixa_with_versao(versao);
14408            let first = c.versao();
14409            let second = c.versao();
14410            assert_eq!(
14411                first, second,
14412                "Caixa::versao must be idempotent — two successive \
14413                 calls on the same &self must return the same &str",
14414            );
14415            assert_eq!(
14416                first, versao,
14417                "Caixa::versao must return :versao verbatim by borrow \
14418                 — got {first}, expected {versao}",
14419            );
14420        }
14421    }
14422
14423    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
14424        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14425        c.kind = kind;
14426        c
14427    }
14428
14429    #[test]
14430    fn kind_returns_kind_variant_verbatim_across_permutations() {
14431        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
14432        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
14433        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
14434        // the raw `.kind` field access across every variant in the
14435        // closed accept-set (`Biblioteca` — the library kind that
14436        // exports lisp forms; `Binario` — the nix-built executable kind
14437        // under `exe/`; `Servico` — the wasm-component daemon kind
14438        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
14439        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
14440        // composition kind).
14441        //
14442        // Pins against a future silent detour that re-derived the kind
14443        // from a peer axis (an accidental fallback to
14444        // `if !servicos.is_empty() { Servico } else if
14445        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
14446        // collapse that read the code-surface / mesh-slot columns into
14447        // the kind discriminator), a variant remap the operator
14448        // authors on one consumer without the other, or a stale-derive
14449        // detour that substituted [`CaixaKind::Biblioteca`] as the
14450        // default when the field held any other variant (which would
14451        // silently collapse the distinction between "author explicitly
14452        // declared `:kind Servico`" and "author declared any other
14453        // kind" every downstream renderer-dispatch site depends on).
14454        //
14455        // First outer top-level [`Caixa`] `Copy`-return required-enum-
14456        // discriminant accessor pin — opens the "outer [`Caixa`]
14457        // `Copy`-return required-discriminant" projection pattern.
14458        // Sibling in shape to the peer per-`:supervisor`
14459        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
14460        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
14461        // (921fe1b), and per-`:children`
14462        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
14463        // `Copy`-return closed-set-enum discriminant accessor pins on
14464        // the sibling nested-spec typed-slot discriminator axes,
14465        // extended here to the outer top-level [`Caixa`] universal-
14466        // axis surface.
14467        for kind in [
14468            CaixaKind::Biblioteca,
14469            CaixaKind::Binario,
14470            CaixaKind::Servico,
14471            CaixaKind::Supervisor,
14472            CaixaKind::Aplicacao,
14473        ] {
14474            let c = caixa_with_kind(kind);
14475            assert_eq!(
14476                c.kind(),
14477                kind,
14478                "Caixa::kind must return :kind verbatim (got {:?}, \
14479                 expected {kind:?})",
14480                c.kind(),
14481            );
14482            assert_eq!(
14483                c.kind(),
14484                c.kind,
14485                "Caixa::kind accessor and .kind field access must \
14486                 byte-equal — the accessor is the substrate-primitive \
14487                 typed dispatch every downstream kind-gate consumer \
14488                 must route through",
14489            );
14490        }
14491    }
14492
14493    #[test]
14494    fn require_kind_reads_through_lifted_kind_accessor() {
14495        // Two-consumer coherence pin: the [`crate::render::require_kind`]
14496        // entry-gate predicate (the canonical two-line
14497        // `require_kind(caixa, Servico)?` prelude every per-Servico /
14498        // per-Aplicacao renderer runs at its entry-point) and the
14499        // sibling [`crate::render::KindMismatch`] error carrier's
14500        // `actual:` field (which names the offending caixa's variant
14501        // in the diagnostic) must both key off the lifted accessor, so
14502        // any future rebrand on the typed slot's reader shape lands at
14503        // exactly one place. Pins the two-site coherence by exercising
14504        // every off-diagonal `(actual, expected)` pair across the
14505        // closed accept-set — the `KindMismatch { actual, expected }`
14506        // surfaced on the mismatch arm must byte-equal the pair the
14507        // accessor returns for each side.
14508        //
14509        // Peer of the sibling per-`:placement`
14510        // `validate_placement_reads_through_lifted_estrategia_accessor`
14511        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
14512        // `Copy`-return discriminant axis — same "the entry-gate
14513        // predicate and the error carrier's `actual:` field must route
14514        // through the substrate-primitive typed dispatch" discipline
14515        // extended onto the outer top-level [`Caixa`] universal-axis
14516        // discriminant surface.
14517        for expected in [
14518            CaixaKind::Biblioteca,
14519            CaixaKind::Binario,
14520            CaixaKind::Servico,
14521            CaixaKind::Supervisor,
14522            CaixaKind::Aplicacao,
14523        ] {
14524            for actual in [
14525                CaixaKind::Biblioteca,
14526                CaixaKind::Binario,
14527                CaixaKind::Servico,
14528                CaixaKind::Supervisor,
14529                CaixaKind::Aplicacao,
14530            ] {
14531                let c = caixa_with_kind(actual);
14532                let result = crate::render::require_kind(&c, expected);
14533                if expected == actual {
14534                    assert!(
14535                        result.is_ok(),
14536                        "require_kind must accept when actual == expected \
14537                         (actual={actual:?}, expected={expected:?})",
14538                    );
14539                } else {
14540                    let err = result.expect_err("require_kind must reject when actual != expected");
14541                    assert_eq!(
14542                        err.actual,
14543                        c.kind(),
14544                        "KindMismatch.actual must byte-equal Caixa::kind() \
14545                         — the error carrier's `actual:` field reads \
14546                         through the lifted accessor",
14547                    );
14548                    assert_eq!(
14549                        err.expected, expected,
14550                        "KindMismatch.expected must byte-equal the \
14551                         expected variant passed to require_kind",
14552                    );
14553                }
14554            }
14555        }
14556    }
14557
14558    #[test]
14559    fn aplicacao_view_kind_gate_routes_through_accessor() {
14560        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
14561        // must key off [`Caixa::kind`], not the raw `.kind` field
14562        // access. Structurally: a `Caixa { kind: X, .. }` for any
14563        // non-`Aplicacao` variant must fold to `None` on the
14564        // `aplicacao_view` composer (the "kind mismatch → no typed
14565        // view" contract every downstream Aplicacao consumer keys off
14566        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
14567        // `Some(_)`. The pair jointly pins the accessor + view-gate
14568        // composition: any future silent detour that had the accessor
14569        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
14570        // input would silently absorb the kind-mismatch case at the
14571        // accessor boundary and every per-Aplicacao renderer would
14572        // silently render a non-Aplicacao caixa's mesh slots — the
14573        // composition pin catches that at caixa-core build time.
14574        //
14575        // Peer of the sibling per-`Caixa`
14576        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
14577        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
14578        // composition pins on the sibling outer top-level [`Caixa`]
14579        // required-`&str` universal-axis surfaces — same "the
14580        // composer / validate gate must route through the substrate-
14581        // primitive typed dispatch" discipline extended onto the
14582        // outer top-level [`Caixa`] `Copy`-return required-
14583        // discriminant composition axis.
14584        for kind in [
14585            CaixaKind::Biblioteca,
14586            CaixaKind::Binario,
14587            CaixaKind::Servico,
14588            CaixaKind::Supervisor,
14589        ] {
14590            let c = caixa_with_kind(kind);
14591            assert!(
14592                c.aplicacao_view().is_none(),
14593                "aplicacao_view must return None on non-Aplicacao \
14594                 kind {kind:?} — the composer's kind-gate must route \
14595                 through Caixa::kind()",
14596            );
14597        }
14598        let c = caixa_with_kind(CaixaKind::Aplicacao);
14599        assert!(
14600            c.aplicacao_view().is_some(),
14601            "aplicacao_view must return Some on kind Aplicacao — \
14602             the composer's kind-gate must accept the matching arm \
14603             through Caixa::kind()",
14604        );
14605    }
14606
14607    #[test]
14608    fn supervisor_view_kind_gate_routes_through_accessor() {
14609        // Composition pin (mirror of the sibling
14610        // `aplicacao_view_kind_gate_routes_through_accessor` on the
14611        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
14612        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
14613        // field access. A `Caixa { kind: X, .. }` for any non-
14614        // `Supervisor` variant must fold to `None` on the
14615        // `supervisor_view` composer, and a `Caixa { kind:
14616        // Supervisor, .. }` must fold to `Some(_)`. Same peer
14617        // composition pin discipline on the second `_view` composer
14618        // axis.
14619        for kind in [
14620            CaixaKind::Biblioteca,
14621            CaixaKind::Binario,
14622            CaixaKind::Servico,
14623            CaixaKind::Aplicacao,
14624        ] {
14625            let c = caixa_with_kind(kind);
14626            assert!(
14627                c.supervisor_view().is_none(),
14628                "supervisor_view must return None on non-Supervisor \
14629                 kind {kind:?} — the composer's kind-gate must route \
14630                 through Caixa::kind()",
14631            );
14632        }
14633        let mut c = caixa_with_kind(CaixaKind::Supervisor);
14634        // A Supervisor caixa needs a strategy + at least one child to
14635        // fold to a Some(_) that also validates; the composer itself
14636        // requires only the kind arm, so bare kind flip is enough to
14637        // pin the `Some(_)` return, but we populate the minimum
14638        // supervisor shape so a future strengthening of the composer
14639        // to reject an empty spec doesn't false-positive this pin.
14640        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
14641        c.children = vec![crate::supervisor::ChildSpec {
14642            caixa: "child".into(),
14643            versao: "^0.1".into(),
14644            restart: crate::supervisor::RestartPolicy::Permanent,
14645        }];
14646        assert!(
14647            c.supervisor_view().is_some(),
14648            "supervisor_view must return Some on kind Supervisor — \
14649             the composer's kind-gate must accept the matching arm \
14650             through Caixa::kind()",
14651        );
14652    }
14653
14654    #[test]
14655    fn kind_projects_by_copy() {
14656        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
14657        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
14658        // `&self` (the returned value is owned, `Copy`-projected from
14659        // the underlying [`CaixaKind`] storage; two calls on the same
14660        // [`Caixa`] must yield byte-equal values). Peer of the peer
14661        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
14662        // `SupervisorSpec::estrategia` / per-`:children`
14663        // `ChildSpec::restart` `Copy`-return discriminant accessor
14664        // pins on the sibling nested-spec typed-slot discriminator
14665        // axes, extended onto the first outer top-level [`Caixa`]
14666        // required-`Copy`-return axis — pins against a future silent
14667        // detour that returned `&CaixaKind` (which would type-check
14668        // but silently constrain every consumer's callsite to a
14669        // borrow-shaped dispatch, breaking the zero-cost `Copy`
14670        // projection every peer sibling accessor carries).
14671        for kind in [
14672            CaixaKind::Biblioteca,
14673            CaixaKind::Binario,
14674            CaixaKind::Servico,
14675            CaixaKind::Supervisor,
14676            CaixaKind::Aplicacao,
14677        ] {
14678            let c = caixa_with_kind(kind);
14679            let first: CaixaKind = c.kind();
14680            let second: CaixaKind = c.kind();
14681            assert_eq!(
14682                first, second,
14683                "Caixa::kind must be idempotent — two successive \
14684                 calls on the same &self must return the same \
14685                 CaixaKind variant",
14686            );
14687            assert_eq!(
14688                first, kind,
14689                "Caixa::kind must return :kind verbatim by Copy — \
14690                 got {first:?}, expected {kind:?}",
14691            );
14692        }
14693    }
14694
14695    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
14696
14697    #[test]
14698    fn autores_returns_autores_slice_verbatim_across_permutations() {
14699        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
14700        // name-list slice pin: [`Caixa::autores`] must return the
14701        // `:autores` typed [`Vec<String>`] list verbatim as a
14702        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
14703        // access across every representative value in the accept-set —
14704        // `[]` (the "no maintainers declared" arm every existing
14705        // fixture without an `:autores` line carries), `[""]` (a past-
14706        // the-guard sentinel that pins the accessor doesn't perform a
14707        // silent `[""] → []` collapse on the empty-entry arm — validate
14708        // rejects `[""]` through `AutorEmpty` but the accessor must
14709        // ship the raw slot verbatim so a validate-time gate regression
14710        // surfaces at the caixa-helm emit boundary rather than being
14711        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
14712        // canonical single-maintainer form every `feira init` template
14713        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
14714        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
14715        // (the canonical RFC-5322 `<name> <email>` form the
14716        // `is_chart_maintainer_name_shape` predicate accepts), and
14717        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
14718        // sentinel — validate rejects through `AutorDuplicate` but the
14719        // accessor must ship the raw slot verbatim).
14720        //
14721        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
14722        // pin on the substrate primitive — opens the "outer [`Caixa`]
14723        // `&[T]` slice" projection pattern the sibling per-`Caixa`
14724        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
14725        // / `:servicos` / `:upgrade-from` / `:children` future lifts
14726        // fold on. Sibling in shape to the peer per-`:supervisor`
14727        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
14728        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
14729        // (a6e18d7), per-`:membros`
14730        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
14731        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14732        // (0dcc926), and per-`:upgrade-from :instructions`
14733        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
14734        // `&[T]`-return slice accessor pins on the sibling per-M2 /
14735        // per-M3 typed-slot list axes, extended onto the outer top-
14736        // level [`Caixa`] universal-axis surface. Pins against a future
14737        // silent detour that returned an owned `Vec<String>` (which
14738        // would type-check but silently clone on every accessor call,
14739        // breaking the zero-cost projection every peer sibling slice
14740        // accessor carries), a `[""] → []` collapse (which would
14741        // silently absorb the `AutorEmpty` refusal case at the accessor
14742        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
14743        // would silently absorb the `AutorDuplicate` refusal case at
14744        // the accessor boundary and the caixa-helm `maintainers:` fold
14745        // would silently render a dedupped list on a struct-literal
14746        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
14747        for autores in [
14748            vec![],
14749            vec![""],
14750            vec!["pleme-io"],
14751            vec!["alice", "bob"],
14752            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
14753            vec!["pleme-io", "pleme-io"],
14754        ] {
14755            let c = caixa_with_autores(autores.clone());
14756            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14757            assert_eq!(
14758                c.autores(),
14759                expected.as_slice(),
14760                "Caixa::autores must return :autores verbatim (got {:?}, \
14761                 expected {expected:?})",
14762                c.autores(),
14763            );
14764            assert_eq!(
14765                c.autores(),
14766                c.autores.as_slice(),
14767                "Caixa::autores must byte-equal the raw \
14768                 `self.autores.as_slice()` field access across every \
14769                 value in the Vec<String> accept-set",
14770            );
14771        }
14772    }
14773
14774    #[test]
14775    fn validate_autores_empty_entry_arm_routes_through_accessor() {
14776        // Composition pin: [`Caixa::validate_autores`]'s per-entry
14777        // empty-arm gate must key off [`Caixa::autores`], not the raw
14778        // `&self.autores` field-borrow walk. Structurally: a
14779        // `Caixa { autores: vec!["".into()], .. }` must surface the
14780        // `AutorEmpty` refusal exactly, and a
14781        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
14782        // canonical single-maintainer form) must pass validate. The
14783        // pair jointly pins the accessor + validate-gate composition:
14784        // any future silent detour that had the accessor return an
14785        // empty slice on the `[""]` arm (a
14786        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
14787        // would silently absorb the `AutorEmpty` refusal at the
14788        // accessor boundary and the validate gate would accept a
14789        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
14790        // the composition pin catches that at caixa-core build time.
14791        //
14792        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
14793        // accessor-composition pin
14794        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
14795        // sibling `Option<&str>`-composition axis and the
14796        // per-`:politicas :circuit-breaker`
14797        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
14798        // accessor-composition pin
14799        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
14800        // on the sibling required-`u32`-composition axis — same "the
14801        // validate / shape-gate predicate must route through the
14802        // substrate-primitive typed dispatch" discipline extended onto
14803        // the outer top-level [`Caixa`] universal-axis `&[T]`-
14804        // composition surface.
14805        let c = caixa_with_autores(vec![""]);
14806        assert!(
14807            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
14808            "validate_autores must reject autores == vec![\"\"] with \
14809             AutorEmpty — the accessor and the validate gate must \
14810             route through the same substrate-primitive typed dispatch \
14811             on the :autores per-entry empty arm",
14812        );
14813        let c = caixa_with_autores(vec!["pleme-io"]);
14814        assert!(
14815            c.validate_autores().is_ok(),
14816            "validate_autores must accept autores == vec![\"pleme-io\"] \
14817             (the canonical single-maintainer shape every `feira init` \
14818             template scaffolds)",
14819        );
14820    }
14821
14822    #[test]
14823    fn autores_projects_slice_by_borrow() {
14824        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
14825        // borrow — the returned slice borrows the underlying
14826        // `Vec<String>` storage of the `:autores` slot and the
14827        // accessor must not clone the backing `Vec` on every call.
14828        // Peer of the per-`:membros`
14829        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
14830        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14831        // (0dcc926) / per-`:placement`
14832        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
14833        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
14834        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
14835        // typed-slot `&[T]`-return axes, extended onto the outer top-
14836        // level [`Caixa`] universal-axis `&[String]` shape — the
14837        // accessor's returned slice must borrow from `&self` (the
14838        // returned reference's lifetime is tied to `&self`), and
14839        // calling the accessor twice on the same [`Caixa`] must yield
14840        // slices that are pointer-equal (the underlying byte-buffer is
14841        // the storage `Vec`'s allocation, not a fresh copy) as well as
14842        // value-equal (idempotent, no side effects on `&self`).
14843        //
14844        // Pins against a future silent detour that returned an owned
14845        // `Vec<String>` (which would type-check but silently clone on
14846        // every call, breaking the zero-cost projection every peer
14847        // sibling slice accessor carries), a `&Vec<String>` return
14848        // (which would leak the backing `Vec`'s grow/push/reserve
14849        // surface no downstream consumer reaches for), or a one-arm-
14850        // only accessor that returned a saturating value on some
14851        // sentinel input (breaking the pass-through invariant the
14852        // sibling slice accessors carry).
14853        for autores in [
14854            vec![],
14855            vec!["pleme-io"],
14856            vec!["alice", "bob"],
14857            vec!["pleme-io", "pleme-io"],
14858        ] {
14859            let c = caixa_with_autores(autores.clone());
14860            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14861            let first = c.autores();
14862            let second = c.autores();
14863            assert_eq!(
14864                first, second,
14865                "Caixa::autores must be idempotent — two successive \
14866                 calls on the same &self must return the same \
14867                 &[String]",
14868            );
14869            assert_eq!(
14870                first.as_ptr(),
14871                second.as_ptr(),
14872                "Caixa::autores must borrow the underlying Vec<String> \
14873                 storage — two successive calls must return slices \
14874                 with the same backing pointer (a fresh Vec<String> \
14875                 clone would change the pointer on every call)",
14876            );
14877            assert_eq!(
14878                first,
14879                expected.as_slice(),
14880                "Caixa::autores must return :autores verbatim by \
14881                 borrow — got {first:?}, expected {expected:?}",
14882            );
14883        }
14884    }
14885
14886    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
14887
14888    #[test]
14889    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
14890        // The canonical per-`Caixa` `:etiquetas` universal-axis
14891        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
14892        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
14893        // as a `&[String]`, byte-equal to the raw
14894        // `self.etiquetas.as_slice()` access across every representative
14895        // value in the accept-set — `[]` (the "no tags declared" arm
14896        // every existing fixture without an `:etiquetas` line carries),
14897        // `[""]` (a past-the-guard sentinel that pins the accessor
14898        // doesn't perform a silent `[""] → []` collapse on the empty-
14899        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
14900        // but the accessor must ship the raw slot verbatim so a
14901        // validate-time gate regression surfaces at the caixa-helm emit
14902        // boundary rather than being silently absorbed into a keyword-
14903        // drop), `["demo"]` (the canonical single-tag form every
14904        // `feira init` template scaffolds), `["example", "aplicacao",
14905        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
14906        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
14907        // (a past-the-guard duplicate sentinel — validate rejects
14908        // through `EtiquetaDuplicate` but the accessor must ship the
14909        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
14910        // at chart-render time isn't silently promoted into the
14911        // accessor boundary and struct-literal
14912        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
14913        // fixtures continue to expose the duplicate at the accessor).
14914        //
14915        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
14916        // pin on the substrate primitive — folds on the "outer
14917        // [`Caixa`] `&[T]` slice" projection pattern
14918        // `autores_returns_autores_slice_verbatim_across_permutations`
14919        // (b5d813f) opened, sibling in shape and idiom. Pins against a
14920        // future silent detour that returned an owned `Vec<String>`
14921        // (which would type-check but silently clone on every accessor
14922        // call, breaking the zero-cost projection every peer sibling
14923        // slice accessor carries), a `[""] → []` collapse (which would
14924        // silently absorb the `EtiquetaEmpty` refusal case at the
14925        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
14926        // (which would silently absorb the `EtiquetaDuplicate` refusal
14927        // case at the accessor boundary — the caixa-helm chart-render
14928        // `BTreeSet::collect` dedup is downstream of the accessor and
14929        // must not be silently promoted into it).
14930        for etiquetas in [
14931            vec![],
14932            vec![""],
14933            vec!["demo"],
14934            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
14935            vec!["demo", "demo"],
14936        ] {
14937            let c = caixa_with_etiquetas(etiquetas.clone());
14938            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14939            assert_eq!(
14940                c.etiquetas(),
14941                expected.as_slice(),
14942                "Caixa::etiquetas must return :etiquetas verbatim (got \
14943                 {:?}, expected {expected:?})",
14944                c.etiquetas(),
14945            );
14946            assert_eq!(
14947                c.etiquetas(),
14948                c.etiquetas.as_slice(),
14949                "Caixa::etiquetas must byte-equal the raw \
14950                 `self.etiquetas.as_slice()` field access across every \
14951                 value in the Vec<String> accept-set",
14952            );
14953        }
14954    }
14955
14956    #[test]
14957    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
14958        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
14959        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
14960        // `&self.etiquetas` field-borrow walk. Structurally: a
14961        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
14962        // `EtiquetaEmpty` refusal exactly, and a
14963        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
14964        // single-tag form) must pass validate. The pair jointly pins
14965        // the accessor + validate-gate composition: any future silent
14966        // detour that had the accessor return an empty slice on the
14967        // `[""]` arm (a
14968        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
14969        // silently absorb the `EtiquetaEmpty` refusal at the accessor
14970        // boundary and the validate gate would accept a struct-literal
14971        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
14972        // pin catches that at caixa-core build time.
14973        //
14974        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
14975        // through_accessor` (b5d813f) accessor-composition pin on the
14976        // sibling `&[T]`-composition axis — same "the validate / shape-
14977        // gate predicate must route through the substrate-primitive
14978        // typed dispatch" discipline extended onto the sibling outer
14979        // top-level [`Caixa`] `&[T]`-composition surface.
14980        let c = caixa_with_etiquetas(vec![""]);
14981        assert!(
14982            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
14983            "validate_etiquetas must reject etiquetas == vec![\"\"] \
14984             with EtiquetaEmpty — the accessor and the validate gate \
14985             must route through the same substrate-primitive typed \
14986             dispatch on the :etiquetas per-entry empty arm",
14987        );
14988        let c = caixa_with_etiquetas(vec!["demo"]);
14989        assert!(
14990            c.validate_etiquetas().is_ok(),
14991            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
14992             (the canonical single-tag shape every `feira init` \
14993             template scaffolds)",
14994        );
14995    }
14996
14997    #[test]
14998    fn etiquetas_projects_slice_by_borrow() {
14999        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
15000        // by borrow — the returned slice borrows the underlying
15001        // `Vec<String>` storage of the `:etiquetas` slot and the
15002        // accessor must not clone the backing `Vec` on every call.
15003        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15004        // (b5d813f) by-borrow pin on the sibling outer top-level
15005        // [`Caixa`] `&[String]`-return axis — the accessor's returned
15006        // slice must borrow from `&self` (the returned reference's
15007        // lifetime is tied to `&self`), and calling the accessor twice
15008        // on the same [`Caixa`] must yield slices that are pointer-
15009        // equal (the underlying byte-buffer is the storage `Vec`'s
15010        // allocation, not a fresh copy) as well as value-equal
15011        // (idempotent, no side effects on `&self`).
15012        //
15013        // Pins against a future silent detour that returned an owned
15014        // `Vec<String>` (which would type-check but silently clone on
15015        // every call, breaking the zero-cost projection every peer
15016        // sibling slice accessor carries), a `&Vec<String>` return
15017        // (which would leak the backing `Vec`'s grow/push/reserve
15018        // surface no downstream consumer reaches for), or a one-arm-
15019        // only accessor that returned a saturating value on some
15020        // sentinel input (breaking the pass-through invariant the
15021        // sibling slice accessors carry).
15022        for etiquetas in [
15023            vec![],
15024            vec!["demo"],
15025            vec!["example", "aplicacao", "mesh"],
15026            vec!["demo", "demo"],
15027        ] {
15028            let c = caixa_with_etiquetas(etiquetas.clone());
15029            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
15030            let first = c.etiquetas();
15031            let second = c.etiquetas();
15032            assert_eq!(
15033                first, second,
15034                "Caixa::etiquetas must be idempotent — two successive \
15035                 calls on the same &self must return the same \
15036                 &[String]",
15037            );
15038            assert_eq!(
15039                first.as_ptr(),
15040                second.as_ptr(),
15041                "Caixa::etiquetas must borrow the underlying \
15042                 Vec<String> storage — two successive calls must \
15043                 return slices with the same backing pointer (a fresh \
15044                 Vec<String> clone would change the pointer on every \
15045                 call)",
15046            );
15047            assert_eq!(
15048                first,
15049                expected.as_slice(),
15050                "Caixa::etiquetas must return :etiquetas verbatim by \
15051                 borrow — got {first:?}, expected {expected:?}",
15052            );
15053        }
15054    }
15055
15056    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
15057
15058    #[test]
15059    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
15060        // The canonical per-`Caixa` `:bibliotecas` universal-axis
15061        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
15062        // must return the `:bibliotecas` typed [`Vec<String>`] list
15063        // verbatim as a `&[String]`, byte-equal to the raw
15064        // `self.bibliotecas.as_slice()` access across every
15065        // representative value in the accept-set — `[]` (the "no
15066        // libraries declared" arm every `:kind` other than `Biblioteca`
15067        // + every `Biblioteca` relying on the canonical
15068        // `lib/<nome>.lisp` implicit-default path carries; the
15069        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
15070        // fires exactly on this empty-slot + `Biblioteca`-kind
15071        // combination), `[""]` (a past-the-guard sentinel that pins
15072        // the accessor doesn't perform a silent `[""] → []` collapse
15073        // on the empty-entry arm — validate rejects `[""]` through
15074        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
15075        // must ship the raw slot verbatim so a validate-time gate
15076        // regression surfaces at the `feira build` phase-1 parse
15077        // boundary rather than being silently absorbed into a
15078        // library-drop), `["lib/demo.lisp"]` (the canonical single-
15079        // entry form `Caixa::template` scaffolds and every `feira init`
15080        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
15081        // (the canonical multi-library form the
15082        // `validate_code_paths_accepts_explicit_relative_paths_on_
15083        // every_slot` fixture emits), and `["lib/foo.lisp",
15084        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
15085        // validate rejects through `CodePathDuplicate { slot:
15086        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
15087        // but the accessor must ship the raw slot verbatim so the
15088        // `feira build` `for entry in caixa.bibliotecas()` parse walk
15089        // sees the duplicate at the accessor boundary and struct-
15090        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
15091        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
15092        // the duplicate at the accessor).
15093        //
15094        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
15095        // pin on the substrate primitive — folds on the "outer
15096        // [`Caixa`] `&[T]` slice" projection pattern
15097        // `autores_returns_autores_slice_verbatim_across_permutations`
15098        // (b5d813f) opened and
15099        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15100        // (78c7d3c) folded on, sibling in shape and idiom. Pins
15101        // against a future silent detour that returned an owned
15102        // `Vec<String>` (which would type-check but silently clone on
15103        // every accessor call, breaking the zero-cost projection
15104        // every peer sibling slice accessor carries), a `[""] → []`
15105        // collapse (which would silently absorb the `CodePathEmpty`
15106        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
15107        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
15108        // would silently absorb the `CodePathDuplicate` refusal case
15109        // at the accessor boundary — the per-slot set-not-multiset
15110        // gate is downstream of the accessor and must not be silently
15111        // promoted into it).
15112        for bibliotecas in [
15113            vec![],
15114            vec![""],
15115            vec!["lib/demo.lisp"],
15116            vec!["lib/demo.lisp", "lib/helpers.lisp"],
15117            vec!["lib/foo.lisp", "lib/foo.lisp"],
15118        ] {
15119            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
15120            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
15121            assert_eq!(
15122                c.bibliotecas(),
15123                expected.as_slice(),
15124                "Caixa::bibliotecas must return :bibliotecas verbatim \
15125                 (got {:?}, expected {expected:?})",
15126                c.bibliotecas(),
15127            );
15128            assert_eq!(
15129                c.bibliotecas(),
15130                c.bibliotecas.as_slice(),
15131                "Caixa::bibliotecas must byte-equal the raw \
15132                 `self.bibliotecas.as_slice()` field access across \
15133                 every value in the Vec<String> accept-set",
15134            );
15135        }
15136    }
15137
15138    #[test]
15139    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
15140        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15141        // empty-arm gate on the `:bibliotecas` slot must key off
15142        // [`Caixa::bibliotecas`], not a divergent raw
15143        // `&self.bibliotecas` field-borrow walk. Structurally: a
15144        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
15145        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
15146        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
15147        // into()], .. }` (the canonical single-library form
15148        // `Caixa::template` scaffolds) must pass validate. The pair
15149        // jointly pins the accessor + validate-gate composition: any
15150        // future silent detour that had the accessor return an empty
15151        // slice on the `[""]` arm (a `.iter().filter(|s|
15152        // !s.is_empty()).collect()` collapse) would silently absorb
15153        // the `CodePathEmpty` refusal at the accessor boundary and
15154        // the validate gate would accept a struct-literal
15155        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
15156        // composition pin catches that at caixa-core build time.
15157        //
15158        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
15159        // through_accessor` (b5d813f) and
15160        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15161        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15162        // composition axes — same "the validate / shape-gate
15163        // predicate must route through the substrate-primitive typed
15164        // dispatch" discipline extended onto the sibling outer top-
15165        // level [`Caixa`] `&[T]`-composition surface. Nominally the
15166        // in-tree `validate_code_paths` production body still keys
15167        // off the internal `[(":bibliotecas", &self.bibliotecas,
15168        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15169        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15170        // (the tuple's homogeneous slice-typed shape blocks a per-
15171        // element accessor swap in isolation — a future companion
15172        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
15173        // `&[T]` slice-accessor axis closes that tuple onto the
15174        // triple of typed dispatches as a unit); the composition pin
15175        // catches any future accessor-side silent filter drop against
15176        // that eventual tuple-closure regardless of whether the
15177        // `:bibliotecas` slot is threaded through the accessor or the
15178        // raw field access at the tuple's construction site.
15179        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
15180        assert!(
15181            matches!(
15182                c.validate_code_paths(),
15183                Err(ManifestError::CodePathEmpty {
15184                    slot: ":bibliotecas"
15185                })
15186            ),
15187            "validate_code_paths must reject bibliotecas == vec![\"\"] \
15188             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
15189             accessor and the validate gate must route through the \
15190             same substrate-primitive typed dispatch on the \
15191             :bibliotecas per-entry empty arm",
15192        );
15193        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
15194        assert!(
15195            c.validate_code_paths().is_ok(),
15196            "validate_code_paths must accept bibliotecas == \
15197             vec![\"lib/demo.lisp\"] (the canonical single-library \
15198             shape every `feira init` template scaffolds)",
15199        );
15200    }
15201
15202    #[test]
15203    fn bibliotecas_projects_slice_by_borrow() {
15204        // The by-borrow pin: [`Caixa::bibliotecas`] returns
15205        // `&[String]` by borrow — the returned slice borrows the
15206        // underlying `Vec<String>` storage of the `:bibliotecas` slot
15207        // and the accessor must not clone the backing `Vec` on every
15208        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15209        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
15210        // by-borrow pins on the sibling outer top-level [`Caixa`]
15211        // `&[String]`-return axes — the accessor's returned slice
15212        // must borrow from `&self` (the returned reference's lifetime
15213        // is tied to `&self`), and calling the accessor twice on the
15214        // same [`Caixa`] must yield slices that are pointer-equal
15215        // (the underlying byte-buffer is the storage `Vec`'s
15216        // allocation, not a fresh copy) as well as value-equal
15217        // (idempotent, no side effects on `&self`).
15218        //
15219        // Pins against a future silent detour that returned an owned
15220        // `Vec<String>` (which would type-check but silently clone on
15221        // every call, breaking the zero-cost projection every peer
15222        // sibling slice accessor carries), a `&Vec<String>` return
15223        // (which would leak the backing `Vec`'s grow/push/reserve
15224        // surface no downstream consumer reaches for), or a one-arm-
15225        // only accessor that returned a saturating value on some
15226        // sentinel input (breaking the pass-through invariant the
15227        // sibling slice accessors carry).
15228        for bibliotecas in [
15229            vec![],
15230            vec!["lib/demo.lisp"],
15231            vec!["lib/demo.lisp", "lib/helpers.lisp"],
15232            vec!["lib/foo.lisp", "lib/foo.lisp"],
15233        ] {
15234            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
15235            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
15236            let first = c.bibliotecas();
15237            let second = c.bibliotecas();
15238            assert_eq!(
15239                first, second,
15240                "Caixa::bibliotecas must be idempotent — two \
15241                 successive calls on the same &self must return the \
15242                 same &[String]",
15243            );
15244            assert_eq!(
15245                first.as_ptr(),
15246                second.as_ptr(),
15247                "Caixa::bibliotecas must borrow the underlying \
15248                 Vec<String> storage — two successive calls must \
15249                 return slices with the same backing pointer (a \
15250                 fresh Vec<String> clone would change the pointer on \
15251                 every call)",
15252            );
15253            assert_eq!(
15254                first,
15255                expected.as_slice(),
15256                "Caixa::bibliotecas must return :bibliotecas verbatim \
15257                 by borrow — got {first:?}, expected {expected:?}",
15258            );
15259        }
15260    }
15261
15262    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
15263
15264    #[test]
15265    fn exe_returns_exe_slice_verbatim_across_permutations() {
15266        // The canonical per-`Caixa` `:exe` universal-axis
15267        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
15268        // must return the `:exe` typed [`Vec<String>`] list verbatim as
15269        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
15270        // access across every representative value in the accept-set —
15271        // `[]` (the "no executable declared" arm every `:kind` other
15272        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
15273        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
15274        // + `Binario`-kind combination), `[""]` (a past-the-guard
15275        // sentinel that pins the accessor doesn't perform a silent
15276        // `[""] → []` collapse on the empty-entry arm — validate rejects
15277        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
15278        // accessor must ship the raw slot verbatim so a validate-time
15279        // gate regression surfaces at the layout / `feira nix` boundary
15280        // rather than being silently absorbed into an executable-drop),
15281        // `["exe/cli"]` (the canonical single-entry Binario form every
15282        // in-tree `caixa_with_code_paths` positive control uses),
15283        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
15284        // form the `validate_code_paths_accepts_explicit_relative_paths_
15285        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
15286        // (a past-the-guard duplicate sentinel — validate rejects
15287        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
15288        // set-not-multiset gate, but the accessor must ship the raw
15289        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
15290        // into(), "exe/cli".into()], .. }` fixtures continue to expose
15291        // the duplicate at the accessor).
15292        //
15293        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
15294        // pin on the substrate primitive — folds on the "outer
15295        // [`Caixa`] `&[T]` slice" projection pattern
15296        // `autores_returns_autores_slice_verbatim_across_permutations`
15297        // (b5d813f) opened,
15298        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15299        // (78c7d3c) folded on, and
15300        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15301        // (8a36c23) closed the universal-axis text-tag family of.
15302        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
15303        // the sibling `:servicos` future lift closes onto. Pins against
15304        // a future silent detour that returned an owned `Vec<String>`
15305        // (which would type-check but silently clone on every accessor
15306        // call, breaking the zero-cost projection every peer sibling
15307        // slice accessor carries), a `[""] → []` collapse (which would
15308        // silently absorb the `CodePathEmpty` refusal case at the
15309        // accessor boundary), or an `["exe/cli", "exe/cli"] →
15310        // ["exe/cli"]` dedup collapse (which would silently absorb the
15311        // `CodePathDuplicate` refusal case at the accessor boundary —
15312        // the per-slot set-not-multiset gate is downstream of the
15313        // accessor and must not be silently promoted into it).
15314        for exe in [
15315            vec![],
15316            vec![""],
15317            vec!["exe/cli"],
15318            vec!["exe/cli", "exe/serve"],
15319            vec!["exe/cli", "exe/cli"],
15320        ] {
15321            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15322            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15323            assert_eq!(
15324                c.exe(),
15325                expected.as_slice(),
15326                "Caixa::exe must return :exe verbatim (got {:?}, \
15327                 expected {expected:?})",
15328                c.exe(),
15329            );
15330            assert_eq!(
15331                c.exe(),
15332                c.exe.as_slice(),
15333                "Caixa::exe must byte-equal the raw \
15334                 `self.exe.as_slice()` field access across every value \
15335                 in the Vec<String> accept-set",
15336            );
15337        }
15338    }
15339
15340    #[test]
15341    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
15342        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15343        // empty-arm gate on the `:exe` slot must key off
15344        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
15345        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
15346        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
15347        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
15348        // (the canonical single-executable form every in-tree
15349        // `caixa_with_code_paths` positive control uses) must pass
15350        // validate. The pair jointly pins the accessor + validate-gate
15351        // composition: any future silent detour that had the accessor
15352        // return an empty slice on the `[""]` arm (a
15353        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
15354        // silently absorb the `CodePathEmpty` refusal at the accessor
15355        // boundary and the validate gate would accept a struct-literal
15356        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
15357        // catches that at caixa-core build time.
15358        //
15359        // Peer of the per-`Caixa`
15360        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15361        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
15362        // (b5d813f), and
15363        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15364        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15365        // composition axes — same "the validate / shape-gate predicate
15366        // must route through the substrate-primitive typed dispatch"
15367        // discipline extended onto the sibling outer top-level [`Caixa`]
15368        // `&[T]`-composition surface. Nominally the in-tree
15369        // `validate_code_paths` production body still keys off the
15370        // internal `[(":bibliotecas", &self.bibliotecas,
15371        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15372        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15373        // (the tuple's homogeneous slice-typed shape blocks a per-
15374        // element accessor swap in isolation — a future companion lift
15375        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
15376        // accessor axis closes that tuple onto the triple of typed
15377        // dispatches as a unit); the composition pin catches any future
15378        // accessor-side silent filter drop against that eventual tuple-
15379        // closure regardless of whether the `:exe` slot is threaded
15380        // through the accessor or the raw field access at the tuple's
15381        // construction site.
15382        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
15383        assert!(
15384            matches!(
15385                c.validate_code_paths(),
15386                Err(ManifestError::CodePathEmpty { slot: ":exe" })
15387            ),
15388            "validate_code_paths must reject exe == vec![\"\"] \
15389             with CodePathEmpty {{ slot: \":exe\" }} — the \
15390             accessor and the validate gate must route through the \
15391             same substrate-primitive typed dispatch on the \
15392             :exe per-entry empty arm",
15393        );
15394        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
15395        assert!(
15396            c.validate_code_paths().is_ok(),
15397            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
15398             (the canonical single-executable shape every in-tree \
15399             `caixa_with_code_paths` positive control uses)",
15400        );
15401    }
15402
15403    #[test]
15404    fn exe_projects_slice_by_borrow() {
15405        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
15406        // borrow — the returned slice borrows the underlying
15407        // `Vec<String>` storage of the `:exe` slot and the accessor
15408        // must not clone the backing `Vec` on every call. Peer of the
15409        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
15410        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
15411        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
15412        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
15413        // return axes — the accessor's returned slice must borrow from
15414        // `&self` (the returned reference's lifetime is tied to
15415        // `&self`), and calling the accessor twice on the same
15416        // [`Caixa`] must yield slices that are pointer-equal (the
15417        // underlying byte-buffer is the storage `Vec`'s allocation,
15418        // not a fresh copy) as well as value-equal (idempotent, no
15419        // side effects on `&self`).
15420        //
15421        // Pins against a future silent detour that returned an owned
15422        // `Vec<String>` (which would type-check but silently clone on
15423        // every call, breaking the zero-cost projection every peer
15424        // sibling slice accessor carries), a `&Vec<String>` return
15425        // (which would leak the backing `Vec`'s grow/push/reserve
15426        // surface no downstream consumer reaches for), or a one-arm-
15427        // only accessor that returned a saturating value on some
15428        // sentinel input (breaking the pass-through invariant the
15429        // sibling slice accessors carry).
15430        for exe in [
15431            vec![],
15432            vec!["exe/cli"],
15433            vec!["exe/cli", "exe/serve"],
15434            vec!["exe/cli", "exe/cli"],
15435        ] {
15436            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15437            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15438            let first = c.exe();
15439            let second = c.exe();
15440            assert_eq!(
15441                first, second,
15442                "Caixa::exe must be idempotent — two successive calls \
15443                 on the same &self must return the same &[String]",
15444            );
15445            assert_eq!(
15446                first.as_ptr(),
15447                second.as_ptr(),
15448                "Caixa::exe must borrow the underlying Vec<String> \
15449                 storage — two successive calls must return slices \
15450                 with the same backing pointer (a fresh Vec<String> \
15451                 clone would change the pointer on every call)",
15452            );
15453            assert_eq!(
15454                first,
15455                expected.as_slice(),
15456                "Caixa::exe must return :exe verbatim by borrow — \
15457                 got {first:?}, expected {expected:?}",
15458            );
15459        }
15460    }
15461
15462    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
15463
15464    #[test]
15465    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
15466        // The canonical per-`Caixa` `:servicos` universal-axis
15467        // ComputeUnit-CR-YAML-entry-path-list slice pin:
15468        // [`Caixa::servicos`] must return the `:servicos` typed
15469        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
15470        // the raw `self.servicos.as_slice()` access across every
15471        // representative value in the accept-set — `[]` (the "no
15472        // ComputeUnit-CR declared" arm every `:kind` other than
15473        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
15474        // `ServicoWithoutServicos` arm-gate fires exactly on this
15475        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
15476        // guard sentinel that pins the accessor doesn't perform a
15477        // silent `[""] → []` collapse on the empty-entry arm — validate
15478        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
15479        // but the accessor must ship the raw slot verbatim so a
15480        // validate-time gate regression surfaces at the layout /
15481        // per-Servico renderer boundary rather than being silently
15482        // absorbed into a component-drop),
15483        // `["servicos/demo.computeunit.yaml"]` (the canonical
15484        // singleton V0-shape every in-tree `caixa_with_code_paths`
15485        // positive control uses; the same shape
15486        // [`crate::require_single_servico`] admits),
15487        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
15488        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
15489        // singularity gate rejects through `ServicoCountMismatch
15490        // { count: 2 }` but the accessor must ship the raw slot
15491        // verbatim so struct-literal `Caixa { servicos: vec![...,
15492        // ...], .. }` fixtures continue to expose the count at the
15493        // accessor), and `["servicos/a.computeunit.yaml",
15494        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
15495        // sentinel — validate rejects through
15496        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
15497        // set-not-multiset gate, but the accessor must ship the raw
15498        // slot verbatim so struct-literal fixtures continue to expose
15499        // the duplicate at the accessor).
15500        //
15501        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
15502        // slice accessor pin on the substrate primitive — folds on the
15503        // "outer [`Caixa`] `&[T]` slice" projection pattern
15504        // `autores_returns_autores_slice_verbatim_across_permutations`
15505        // (b5d813f) opened,
15506        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15507        // (78c7d3c) folded on,
15508        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15509        // (8a36c23) closed the universal-axis text-tag family of, and
15510        // `exe_returns_exe_slice_verbatim_across_permutations`
15511        // (65d9527) opened the foreign-code-slot sub-family of. Closes
15512        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
15513        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
15514        // `:servicos`) now each carries a substrate-canonical slice
15515        // accessor. Pins against a future silent detour that returned
15516        // an owned `Vec<String>` (which would type-check but silently
15517        // clone on every accessor call, breaking the zero-cost
15518        // projection every peer sibling slice accessor carries), a
15519        // `[""] → []` collapse (which would silently absorb the
15520        // `CodePathEmpty` refusal case at the accessor boundary), an
15521        // `[a, a] → [a]` dedup collapse (which would silently absorb
15522        // the `CodePathDuplicate` refusal case at the accessor
15523        // boundary — the per-slot set-not-multiset gate is downstream
15524        // of the accessor and must not be silently promoted into it),
15525        // or a `[a, b] → [a]` singleton collapse (which would silently
15526        // absorb the V0 `ServicoCountMismatch` refusal case at the
15527        // accessor boundary — the V0 singularity gate is downstream of
15528        // the accessor and must not be silently promoted into it).
15529        for servicos in [
15530            vec![],
15531            vec![""],
15532            vec!["servicos/demo.computeunit.yaml"],
15533            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15534            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15535        ] {
15536            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15537            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15538            assert_eq!(
15539                c.servicos(),
15540                expected.as_slice(),
15541                "Caixa::servicos must return :servicos verbatim (got \
15542                 {:?}, expected {expected:?})",
15543                c.servicos(),
15544            );
15545            assert_eq!(
15546                c.servicos(),
15547                c.servicos.as_slice(),
15548                "Caixa::servicos must byte-equal the raw \
15549                 `self.servicos.as_slice()` field access across every \
15550                 value in the Vec<String> accept-set",
15551            );
15552        }
15553    }
15554
15555    #[test]
15556    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
15557        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15558        // empty-arm gate on the `:servicos` slot must key off
15559        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
15560        // field-borrow walk. Structurally: a `Caixa { servicos:
15561        // vec!["".into()], .. }` must surface the `CodePathEmpty
15562        // { slot: ":servicos" }` refusal exactly, and a `Caixa
15563        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
15564        // .. }` (the canonical singleton V0-shape every in-tree
15565        // `caixa_with_code_paths` positive control uses) must pass
15566        // validate. The pair jointly pins the accessor + validate-gate
15567        // composition: any future silent detour that had the accessor
15568        // return an empty slice on the `[""]` arm (a `.iter().filter
15569        // (|s| !s.is_empty()).collect()` collapse) would silently
15570        // absorb the `CodePathEmpty` refusal at the accessor boundary
15571        // and the validate gate would accept a struct-literal
15572        // `Caixa { servicos: vec!["".into()], .. }` — the composition
15573        // pin catches that at caixa-core build time.
15574        //
15575        // Peer of the per-`Caixa`
15576        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15577        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15578        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
15579        // (b5d813f), and
15580        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15581        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15582        // composition axes — same "the validate / shape-gate predicate
15583        // must route through the substrate-primitive typed dispatch"
15584        // discipline extended onto the sibling outer top-level
15585        // [`Caixa`] `&[T]`-composition surface, closing the trio of
15586        // code-surface accessor-composition pins on the same axis.
15587        // Nominally the in-tree `validate_code_paths` production body
15588        // still keys off the internal
15589        // `[(":bibliotecas", &self.bibliotecas,
15590        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15591        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15592        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
15593        // per-element accessor swap in isolation — a future companion
15594        // lift promotes the tuple's element type to `&[String]` and
15595        // threads the triple of typed dispatches through as a unit);
15596        // the composition pin catches any future accessor-side silent
15597        // filter drop against that eventual tuple-closure regardless
15598        // of whether the `:servicos` slot is threaded through the
15599        // accessor or the raw field access at the tuple's construction
15600        // site.
15601        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
15602        assert!(
15603            matches!(
15604                c.validate_code_paths(),
15605                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
15606            ),
15607            "validate_code_paths must reject servicos == vec![\"\"] \
15608             with CodePathEmpty {{ slot: \":servicos\" }} — the \
15609             accessor and the validate gate must route through the \
15610             same substrate-primitive typed dispatch on the \
15611             :servicos per-entry empty arm",
15612        );
15613        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
15614        assert!(
15615            c.validate_code_paths().is_ok(),
15616            "validate_code_paths must accept servicos == \
15617             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
15618             singleton V0-shape every in-tree `caixa_with_code_paths` \
15619             positive control uses)",
15620        );
15621    }
15622
15623    #[test]
15624    fn servicos_projects_slice_by_borrow() {
15625        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
15626        // borrow — the returned slice borrows the underlying
15627        // `Vec<String>` storage of the `:servicos` slot and the
15628        // accessor must not clone the backing `Vec` on every call.
15629        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15630        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
15631        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
15632        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
15633        // the sibling outer top-level [`Caixa`] `&[String]`-return
15634        // axes — the accessor's returned slice must borrow from
15635        // `&self` (the returned reference's lifetime is tied to
15636        // `&self`), and calling the accessor twice on the same
15637        // [`Caixa`] must yield slices that are pointer-equal (the
15638        // underlying byte-buffer is the storage `Vec`'s allocation,
15639        // not a fresh copy) as well as value-equal (idempotent, no
15640        // side effects on `&self`).
15641        //
15642        // Pins against a future silent detour that returned an owned
15643        // `Vec<String>` (which would type-check but silently clone on
15644        // every call, breaking the zero-cost projection every peer
15645        // sibling slice accessor carries), a `&Vec<String>` return
15646        // (which would leak the backing `Vec`'s grow/push/reserve
15647        // surface no downstream consumer reaches for), or a one-arm-
15648        // only accessor that returned a saturating value on some
15649        // sentinel input (breaking the pass-through invariant the
15650        // sibling slice accessors carry).
15651        for servicos in [
15652            vec![],
15653            vec!["servicos/demo.computeunit.yaml"],
15654            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15655            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15656        ] {
15657            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15658            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15659            let first = c.servicos();
15660            let second = c.servicos();
15661            assert_eq!(
15662                first, second,
15663                "Caixa::servicos must be idempotent — two successive \
15664                 calls on the same &self must return the same &[String]",
15665            );
15666            assert_eq!(
15667                first.as_ptr(),
15668                second.as_ptr(),
15669                "Caixa::servicos must borrow the underlying \
15670                 Vec<String> storage — two successive calls must \
15671                 return slices with the same backing pointer (a fresh \
15672                 Vec<String> clone would change the pointer on every \
15673                 call)",
15674            );
15675            assert_eq!(
15676                first,
15677                expected.as_slice(),
15678                "Caixa::servicos must return :servicos verbatim by \
15679                 borrow — got {first:?}, expected {expected:?}",
15680            );
15681        }
15682    }
15683
15684    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
15685
15686    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
15687        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15688        c.deps = deps;
15689        c
15690    }
15691
15692    #[test]
15693    fn deps_returns_deps_slice_verbatim_across_permutations() {
15694        // The canonical per-`Caixa` `:deps` universal-axis runtime-
15695        // dependency-declaration-list slice pin: [`Caixa::deps`] must
15696        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
15697        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
15698        // access across every representative value in the accept-set —
15699        // `[]` (the "no runtime deps declared" arm every existing
15700        // fixture without a `:deps` line carries; the
15701        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
15702        // single-entry list (the shape most consumer caixas carry), a
15703        // canonical two-entry list (the multi-dep runtime closure), and
15704        // two past-the-guard sentinels — a `[""]`-`:nome` entry
15705        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15706        // `NomeInvalid` but the accessor must ship the raw slot
15707        // verbatim) and a `[a, a]` duplicate (validate rejects through
15708        // `DuplicateNome { list: ":deps" }` but the accessor must ship
15709        // the raw slot verbatim so struct-literal fixtures continue to
15710        // expose the duplicate at the accessor).
15711        //
15712        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
15713        // pin on the substrate primitive — opens the outer-`Caixa`
15714        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
15715        // future lift closes on. Peer of the closed outer-`Caixa`
15716        // foreign-code-slot `&[String]` sub-family
15717        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15718        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
15719        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
15720        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
15721        // (`autores_returns_autores_slice_verbatim_across_permutations`
15722        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15723        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
15724        // projection pattern onto a novel element-type axis (`Dep`
15725        // composite vs the prior sibling family's `String` scalar).
15726        // Pins against a future silent detour that returned an owned
15727        // `Vec<Dep>` (which would type-check but silently clone on every
15728        // accessor call, breaking the zero-cost projection every peer
15729        // sibling slice accessor carries), a `[""] → []` collapse (which
15730        // would silently absorb the `NomeEmpty` refusal case at the
15731        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15732        // would silently absorb the `DuplicateNome` refusal case at the
15733        // accessor boundary).
15734        for deps in [
15735            vec![],
15736            vec![Dep::simple("", "^0.1")],
15737            vec![Dep::simple("caixa-teia", "^0.1")],
15738            vec![
15739                Dep::simple("caixa-teia", "^0.1"),
15740                Dep::simple("caixa-core", "^0.1"),
15741            ],
15742            vec![
15743                Dep::simple("caixa-teia", "^0.1"),
15744                Dep::simple("caixa-teia", "^0.2"),
15745            ],
15746        ] {
15747            let c = caixa_with_deps(deps.clone());
15748            assert_eq!(
15749                c.deps(),
15750                deps.as_slice(),
15751                "Caixa::deps must return :deps verbatim (got {:?}, \
15752                 expected {deps:?})",
15753                c.deps(),
15754            );
15755            assert_eq!(
15756                c.deps(),
15757                c.deps.as_slice(),
15758                "Caixa::deps must element-equal the raw \
15759                 `self.deps.as_slice()` field access across every \
15760                 value in the Vec<Dep> accept-set",
15761            );
15762        }
15763    }
15764
15765    #[test]
15766    fn validate_deps_duplicate_arm_routes_through_accessor() {
15767        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
15768        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
15769        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
15770        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
15771        // "^0.2")], .. }` must surface the `DuplicateNome { list:
15772        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
15773        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
15774        // form) must pass validate. The pair jointly pins the accessor +
15775        // validate-gate composition: any future silent detour that had
15776        // the accessor return a dedupped slice on the `[a, a]` arm (a
15777        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15778        // would silently absorb the `DuplicateNome` refusal at the
15779        // accessor boundary and the validate gate would accept a
15780        // struct-literal `Caixa` carrying the drift — the composition
15781        // pin catches that at caixa-core build time.
15782        //
15783        // Peer of the per-`Caixa`
15784        // `validate_autores_empty_entry_arm_routes_through_accessor`
15785        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15786        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15787        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15788        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
15789        // (611f78b) accessor-composition pins on the sibling `&[T]`-
15790        // composition axes — same "the validate gate must route through
15791        // the substrate-primitive typed dispatch" discipline extended
15792        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
15793        // composition surface, opening the outer-`Caixa` dependency-slot
15794        // arm of the composition-pin family.
15795        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15796        let err = c.validate_deps().unwrap_err();
15797        assert!(
15798            matches!(
15799                err,
15800                DepError::DuplicateNome { ref nome, list } if nome == "d"
15801                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
15802            ),
15803            "validate_deps must reject deps == \
15804             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15805             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
15806             accessor and the validate gate must route through the \
15807             same substrate-primitive typed dispatch on the :deps \
15808             within-list duplicate arm (got {err:?})",
15809        );
15810        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
15811        assert!(
15812            c.validate_deps().is_ok(),
15813            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
15814             (the canonical single-entry form)",
15815        );
15816    }
15817
15818    #[test]
15819    fn deps_projects_slice_by_borrow() {
15820        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
15821        // — the returned slice borrows the underlying `Vec<Dep>` storage
15822        // of the `:deps` slot and the accessor must not clone the
15823        // backing `Vec` on every call. Peer of the per-`Caixa`
15824        // `autores_projects_slice_by_borrow` (b5d813f),
15825        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15826        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15827        // `exe_projects_slice_by_borrow` (65d9527), and
15828        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15829        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15830        // axes — the accessor's returned slice must borrow from `&self`
15831        // (the returned reference's lifetime is tied to `&self`), and
15832        // calling the accessor twice on the same [`Caixa`] must yield
15833        // slices that are pointer-equal (the underlying byte-buffer is
15834        // the storage `Vec`'s allocation, not a fresh copy) as well as
15835        // value-equal (idempotent, no side effects on `&self`).
15836        //
15837        // Pins against a future silent detour that returned an owned
15838        // `Vec<Dep>` (which would type-check but silently clone on
15839        // every call), a `&Vec<Dep>` return (which would leak the
15840        // backing `Vec`'s grow/push/reserve surface no downstream
15841        // consumer reaches for), or a one-arm-only accessor that
15842        // returned a saturating value on some sentinel input.
15843        for deps in [
15844            vec![],
15845            vec![Dep::simple("caixa-teia", "^0.1")],
15846            vec![
15847                Dep::simple("caixa-teia", "^0.1"),
15848                Dep::simple("caixa-core", "^0.1"),
15849            ],
15850        ] {
15851            let c = caixa_with_deps(deps.clone());
15852            let first = c.deps();
15853            let second = c.deps();
15854            assert_eq!(
15855                first, second,
15856                "Caixa::deps must be idempotent — two successive calls \
15857                 on the same &self must return the same &[Dep]",
15858            );
15859            assert_eq!(
15860                first.as_ptr(),
15861                second.as_ptr(),
15862                "Caixa::deps must borrow the underlying Vec<Dep> \
15863                 storage — two successive calls must return slices \
15864                 with the same backing pointer (a fresh Vec<Dep> clone \
15865                 would change the pointer on every call)",
15866            );
15867            assert_eq!(
15868                first,
15869                deps.as_slice(),
15870                "Caixa::deps must return :deps verbatim by borrow — \
15871                 got {first:?}, expected {deps:?}",
15872            );
15873        }
15874    }
15875
15876    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
15877
15878    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
15879        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15880        c.deps_dev = deps_dev;
15881        c
15882    }
15883
15884    #[test]
15885    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
15886        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
15887        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
15888        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
15889        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
15890        // access across every representative value in the accept-set —
15891        // `[]` (the "no dev deps declared" arm every existing fixture
15892        // without a `:deps-dev` line carries; the [`Caixa::template`]
15893        // scaffold emits `:deps-dev ()`), a canonical single-entry list
15894        // (the shape most consumer caixas carry — a `tatara-check` dev
15895        // pin), a canonical two-entry list (the multi-dev-dep closure),
15896        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
15897        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15898        // `NomeInvalid` but the accessor must ship the raw slot
15899        // verbatim) and a `[a, a]` duplicate (validate rejects through
15900        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
15901        // ship the raw slot verbatim so struct-literal fixtures continue
15902        // to expose the duplicate at the accessor).
15903        //
15904        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
15905        // pin on the substrate primitive — closes the outer-`Caixa`
15906        // dependency-slot `&[Dep]` sub-family the sibling
15907        // `deps_returns_deps_slice_verbatim_across_permutations`
15908        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
15909        // slice" projection pattern onto the sibling dev-dep axis —
15910        // pins against a future silent detour that returned an owned
15911        // `Vec<Dep>` (which would type-check but silently clone on every
15912        // accessor call, breaking the zero-cost projection every peer
15913        // sibling slice accessor carries), a `[""] → []` collapse (which
15914        // would silently absorb the `NomeEmpty` refusal case at the
15915        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15916        // would silently absorb the `DuplicateNome` refusal case at the
15917        // accessor boundary).
15918        for deps_dev in [
15919            vec![],
15920            vec![Dep::simple("", "^0.1")],
15921            vec![Dep::simple("tatara-check", "^0.1")],
15922            vec![
15923                Dep::simple("tatara-check", "^0.1"),
15924                Dep::simple("caixa-lint", "^0.1"),
15925            ],
15926            vec![
15927                Dep::simple("tatara-check", "^0.1"),
15928                Dep::simple("tatara-check", "^0.2"),
15929            ],
15930        ] {
15931            let c = caixa_with_deps_dev(deps_dev.clone());
15932            assert_eq!(
15933                c.deps_dev(),
15934                deps_dev.as_slice(),
15935                "Caixa::deps_dev must return :deps-dev verbatim (got \
15936                 {:?}, expected {deps_dev:?})",
15937                c.deps_dev(),
15938            );
15939            assert_eq!(
15940                c.deps_dev(),
15941                c.deps_dev.as_slice(),
15942                "Caixa::deps_dev must element-equal the raw \
15943                 `self.deps_dev.as_slice()` field access across every \
15944                 value in the Vec<Dep> accept-set",
15945            );
15946        }
15947    }
15948
15949    #[test]
15950    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
15951        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
15952        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
15953        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
15954        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
15955        // Dep::simple("d", "^0.2")], .. }` must surface the
15956        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
15957        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
15958        // canonical single-entry form) must pass validate. The pair
15959        // jointly pins the accessor + validate-gate composition: any
15960        // future silent detour that had the accessor return a dedupped
15961        // slice on the `[a, a]` arm (a
15962        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15963        // would silently absorb the `DuplicateNome` refusal at the
15964        // accessor boundary and the validate gate would accept a
15965        // struct-literal `Caixa` carrying the drift — the composition
15966        // pin catches that at caixa-core build time.
15967        //
15968        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
15969        // (ad34b4e) on the sibling `:deps` axis — same "the validate
15970        // gate must route through the substrate-primitive typed
15971        // dispatch" discipline folded onto the sibling `:deps-dev`
15972        // axis, closing the two-list dep-graph composition-pin family.
15973        // The `:deps-dev` diagnostic must carry the
15974        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
15975        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
15976        // offending list unambiguously.
15977        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15978        let err = c.validate_deps().unwrap_err();
15979        assert!(
15980            matches!(
15981                err,
15982                DepError::DuplicateNome { ref nome, list } if nome == "d"
15983                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
15984            ),
15985            "validate_deps must reject deps_dev == \
15986             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15987             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
15988             accessor and the validate gate must route through the \
15989             same substrate-primitive typed dispatch on the :deps-dev \
15990             within-list duplicate arm (got {err:?})",
15991        );
15992        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
15993        assert!(
15994            c.validate_deps().is_ok(),
15995            "validate_deps must accept deps_dev == \
15996             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
15997        );
15998    }
15999
16000    #[test]
16001    fn deps_dev_projects_slice_by_borrow() {
16002        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
16003        // borrow — the returned slice borrows the underlying `Vec<Dep>`
16004        // storage of the `:deps-dev` slot and the accessor must not
16005        // clone the backing `Vec` on every call. Peer of
16006        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
16007        // `:deps` axis, and of the per-`Caixa`
16008        // `autores_projects_slice_by_borrow` (b5d813f),
16009        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
16010        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
16011        // `exe_projects_slice_by_borrow` (65d9527), and
16012        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
16013        // on the sibling outer top-level [`Caixa`] `&[String]`-return
16014        // axes — the accessor's returned slice must borrow from `&self`
16015        // (the returned reference's lifetime is tied to `&self`), and
16016        // calling the accessor twice on the same [`Caixa`] must yield
16017        // slices that are pointer-equal (the underlying byte-buffer is
16018        // the storage `Vec`'s allocation, not a fresh copy) as well as
16019        // value-equal (idempotent, no side effects on `&self`).
16020        //
16021        // Pins against a future silent detour that returned an owned
16022        // `Vec<Dep>` (which would type-check but silently clone on
16023        // every call), a `&Vec<Dep>` return (which would leak the
16024        // backing `Vec`'s grow/push/reserve surface no downstream
16025        // consumer reaches for), or a one-arm-only accessor that
16026        // returned a saturating value on some sentinel input.
16027        for deps_dev in [
16028            vec![],
16029            vec![Dep::simple("tatara-check", "^0.1")],
16030            vec![
16031                Dep::simple("tatara-check", "^0.1"),
16032                Dep::simple("caixa-lint", "^0.1"),
16033            ],
16034        ] {
16035            let c = caixa_with_deps_dev(deps_dev.clone());
16036            let first = c.deps_dev();
16037            let second = c.deps_dev();
16038            assert_eq!(
16039                first, second,
16040                "Caixa::deps_dev must be idempotent — two successive \
16041                 calls on the same &self must return the same &[Dep]",
16042            );
16043            assert_eq!(
16044                first.as_ptr(),
16045                second.as_ptr(),
16046                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
16047                 storage — two successive calls must return slices \
16048                 with the same backing pointer (a fresh Vec<Dep> clone \
16049                 would change the pointer on every call)",
16050            );
16051            assert_eq!(
16052                first,
16053                deps_dev.as_slice(),
16054                "Caixa::deps_dev must return :deps-dev verbatim by \
16055                 borrow — got {first:?}, expected {deps_dev:?}",
16056            );
16057        }
16058    }
16059
16060    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
16061
16062    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
16063        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16064        c.limits = limits;
16065        c
16066    }
16067
16068    #[test]
16069    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
16070        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
16071        // composite optional-composite-reference-shape pin:
16072        // [`Caixa::limits`] must return the `:limits` typed
16073        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
16074        // reference over the same backing storage the raw
16075        // `self.limits.as_ref()` field access borrows from, byte-equal
16076        // across every representative fixture in the accept-set — the
16077        // author-omitted `None` shape (the "engine-default applies"
16078        // partition every downstream Servico M2 overlay emitter treats
16079        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
16080        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
16081        // per-axis cap is `None`, so the peer M2 overlay emitter's
16082        // `.is_empty()`-gated projection still emits nothing but the
16083        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
16084        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
16085        // fixture (only `:memory` set — the canonical shape most
16086        // memory-heavy Servicos carry), and a fully-populated composite
16087        // (every per-axis cap set — the canonical shape a
16088        // sandboxed-by-default Servico carries).
16089        //
16090        // Pins against a future silent detour that returned a fresh-
16091        // cloned [`LimitsSpec`] copy (which would type-check via the
16092        // `Clone` impl but silently break every downstream caller that
16093        // relied on the reference sharing the composite's backing
16094        // identity), a reference to an operator-resolved overlay (the
16095        // future per-cluster `:limits-overrides` slot — its resolution
16096        // must land at exactly this accessor body, not silently divert
16097        // the raw slot away from a second consumer), a
16098        // `None` → `Some(LimitsSpec::default)` cluster-default
16099        // projection (which would collapse the load-bearing
16100        // "author-omitted `:limits` ⇒ engine-default applies" partition
16101        // the peer [`crate::render::servico_m2_overlay`] emitter and
16102        // the peer [`Caixa::declared_servico_slots`] enumerator both
16103        // read), or an axis-shuffled projection (a future detour that
16104        // swapped `memory` and `fuel` through the accessor would
16105        // silently split the paired [`crate::StandardLayout::verify`]
16106        // per-`:limits` shape gate's traversal input from the peer
16107        // `servico_m2_overlay` emitter's projection input).
16108        //
16109        // First outer top-level [`Caixa`] `Option<&Composite>`-return
16110        // composite-reference accessor pin on the substrate primitive
16111        // — opens the outer-`Caixa` `Option<&Composite>` composite-
16112        // reference projection pattern the sibling `:behavior`
16113        // [`crate::BehaviorSpec`] / `:politicas`
16114        // [`crate::aplicacao::MeshPolicy`] / `:placement`
16115        // [`crate::aplicacao::Placement`] / `:entrada`
16116        // [`crate::aplicacao::Entrada`] future outer-composite lifts
16117        // fold on. Peer of the closed M3 outer-composite family the
16118        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
16119        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
16120        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
16121        // reference accessor pins already carry on the outer
16122        // [`crate::AplicacaoSpec`] altitude — extends the outer-
16123        // accessor byte-equal-projection discipline onto the outer
16124        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
16125        use crate::LimitsSpec;
16126        use std::time::Duration;
16127        let fixtures: Vec<Option<LimitsSpec>> = vec![
16128            None,
16129            Some(LimitsSpec::default()),
16130            Some(LimitsSpec {
16131                memory: Some(64 * 1024 * 1024),
16132                ..Default::default()
16133            }),
16134            Some(LimitsSpec {
16135                memory: Some(64 * 1024 * 1024),
16136                fuel: Some(1_000_000),
16137                wall_clock: Some(Duration::from_secs(30)),
16138                cpu: Some(500),
16139            }),
16140        ];
16141        for limits in fixtures {
16142            let c = caixa_with_limits(limits.clone());
16143            assert_eq!(
16144                c.limits(),
16145                limits.as_ref(),
16146                "Caixa::limits must return :limits verbatim (got {:?}, \
16147                 expected {:?})",
16148                c.limits(),
16149                limits.as_ref(),
16150            );
16151            match (c.limits(), c.limits.as_ref()) {
16152                (Some(a), Some(b)) => assert!(
16153                    std::ptr::eq(a, b),
16154                    "Caixa::limits accessor and self.limits.as_ref() \
16155                     field access must borrow the same backing storage \
16156                     — the accessor is the substrate-primitive typed \
16157                     dispatch every downstream Servico-M2-overlay \
16158                     composite consumer must route through, and a \
16159                     reference-identity split would silently break \
16160                     every consumer that relied on the borrow sharing \
16161                     the composite's storage",
16162                ),
16163                (None, None) => {}
16164                _ => panic!(
16165                    "Caixa::limits presence bit must byte-equal \
16166                     self.limits.is_some() — a presence-bit drift would \
16167                     silently split the paired StandardLayout::verify \
16168                     per-`:limits` shape gate's traversal head from \
16169                     the peer render::servico_m2_overlay M2 overlay \
16170                     emitter's traversal head from the peer \
16171                     Caixa::declared_servico_slots M2 declared-slot \
16172                     enumerator's presence probe",
16173                ),
16174            }
16175            assert_eq!(
16176                c.limits().is_some(),
16177                c.limits.is_some(),
16178                "Caixa::limits().is_some() must byte-equal \
16179                 self.limits.is_some() — a presence-bit drift would \
16180                 silently split every downstream Option<&LimitsSpec> \
16181                 consumer's partition on the engine-default arm",
16182            );
16183        }
16184    }
16185
16186    #[test]
16187    fn declared_servico_slots_limits_arm_routes_through_accessor() {
16188        // Composition pin: [`Caixa::declared_servico_slots`]'s
16189        // `:limits` presence-probe arm must key off [`Caixa::limits`],
16190        // not the raw `self.limits.is_some()` field-probe. Structurally:
16191        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
16192        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
16193        // (the presence bit is `Some`, so the M2 kind-coherence gate
16194        // must surface the slot as "declared" even when every per-axis
16195        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
16196        // push the label (the "author omitted the slot entirely"
16197        // partition). The pair jointly pins the accessor + declared-
16198        // slot enumerator composition: any future silent detour that
16199        // had the accessor collapse `Some(LimitsSpec::default())` to
16200        // `None` (a `.filter(|l| !l.is_empty())` projection) would
16201        // silently absorb the "declared but empty" arm at the
16202        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
16203        // kind-coherence gate would silently accept a
16204        // struct-literal `Caixa` carrying the drift.
16205        //
16206        // Peer of the sibling per-`Caixa`
16207        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
16208        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
16209        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
16210        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
16211        // enumerator gate must route through the substrate-primitive
16212        // typed dispatch" discipline extended onto the outer top-level
16213        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
16214        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
16215        // composition-pin family.
16216        use crate::LimitsSpec;
16217        let c = caixa_with_limits(Some(LimitsSpec::default()));
16218        let slots = c.declared_servico_slots();
16219        assert!(
16220            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
16221            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
16222             when `:limits` is Some (even for LimitsSpec::default()) \
16223             — the accessor and the enumerator gate must route through \
16224             the same substrate-primitive typed dispatch on the outer \
16225             :limits presence bit (got slots={slots:?})",
16226        );
16227        let c = caixa_with_limits(None);
16228        let slots = c.declared_servico_slots();
16229        assert!(
16230            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
16231            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
16232             when `:limits` is None — the author-omitted arm must \
16233             route through the accessor's None-return unchanged (got \
16234             slots={slots:?})",
16235        );
16236    }
16237
16238    #[test]
16239    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
16240        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16241        // per-`:limits` M2 overlay emit arm must key off
16242        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
16243        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
16244        // Some(64 MiB), .. default }), .. }` must surface the
16245        // `M2_KEY_LIMITS` key with the per-axis
16246        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
16247        // limits: Some(LimitsSpec::default()), .. }` must omit the
16248        // key entirely (the `.is_empty()`-gated inner arm elides an
16249        // empty composite even when the outer presence bit is `Some`),
16250        // and a `Caixa { limits: None, .. }` must also omit the key
16251        // (the "author omitted the slot entirely" partition). The
16252        // three-fixture family jointly pins the accessor + M2 overlay
16253        // emitter composition: any future silent detour that had the
16254        // accessor return a fresh-cloned copy on the `Some` arm (a
16255        // `LimitsSpec::clone()` projection) would silently break the
16256        // reference-identity pin the peer per-axis
16257        // `serde_yaml::to_value(limits)` projection reads from.
16258        use crate::LimitsSpec;
16259        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
16260        let c = caixa_with_limits(Some(LimitsSpec {
16261            memory: Some(64 * 1024 * 1024),
16262            ..Default::default()
16263        }));
16264        let overlay = servico_m2_overlay(&c).unwrap();
16265        assert!(
16266            overlay.contains_key(M2_KEY_LIMITS),
16267            "servico_m2_overlay must surface M2_KEY_LIMITS when \
16268             `:limits` carries a non-empty composite — the accessor \
16269             and the M2 overlay emitter must route through the same \
16270             substrate-primitive typed dispatch on the outer :limits \
16271             composite (got overlay={overlay:?})",
16272        );
16273        let c = caixa_with_limits(Some(LimitsSpec::default()));
16274        let overlay = servico_m2_overlay(&c).unwrap();
16275        assert!(
16276            !overlay.contains_key(M2_KEY_LIMITS),
16277            "servico_m2_overlay must omit M2_KEY_LIMITS when \
16278             `:limits` is Some(LimitsSpec::default()) — the empty \
16279             composite's `.is_empty()`-gated inner arm must elide \
16280             the key regardless of the outer presence bit (got \
16281             overlay={overlay:?})",
16282        );
16283        let c = caixa_with_limits(None);
16284        let overlay = servico_m2_overlay(&c).unwrap();
16285        assert!(
16286            !overlay.contains_key(M2_KEY_LIMITS),
16287            "servico_m2_overlay must omit M2_KEY_LIMITS when \
16288             `:limits` is None — the author-omitted arm must route \
16289             through the accessor's None-return unchanged (got \
16290             overlay={overlay:?})",
16291        );
16292    }
16293
16294    #[test]
16295    fn limits_projects_option_ref_by_borrow() {
16296        // The by-borrow pin: [`Caixa::limits`] returns
16297        // `Option<&LimitsSpec>` by borrow — the returned reference
16298        // borrows the underlying `Option<LimitsSpec>` storage of the
16299        // `:limits` slot and the accessor must not clone the backing
16300        // composite on every call. Peer of the sibling
16301        // `deps_projects_slice_by_borrow` (ad34b4e) /
16302        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
16303        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
16304        // extended here to the outer [`Caixa`] `Option<&Composite>`-
16305        // return axis: the accessor's returned reference must borrow
16306        // from `&self` (the returned reference's lifetime is tied to
16307        // `&self`), and calling the accessor twice on the same
16308        // [`Caixa`] must yield references that are pointer-equal (the
16309        // underlying byte-buffer is the storage `LimitsSpec`'s
16310        // allocation, not a fresh copy) as well as value-equal
16311        // (idempotent, no side effects on `&self`).
16312        //
16313        // Pins against a future silent detour that returned an owned
16314        // `LimitsSpec` (which would type-check via the `Clone` impl
16315        // but silently clone on every call), a `&LimitsSpec` panic-
16316        // return on the `None` arm (which would collapse the load-
16317        // bearing `Option` presence-bit into a runtime panic), or a
16318        // one-arm-only accessor that returned a saturating composite
16319        // on some sentinel input.
16320        use crate::LimitsSpec;
16321        use std::time::Duration;
16322        for limits in [
16323            Some(LimitsSpec::default()),
16324            Some(LimitsSpec {
16325                memory: Some(64 * 1024 * 1024),
16326                fuel: Some(1_000_000),
16327                wall_clock: Some(Duration::from_secs(30)),
16328                cpu: Some(500),
16329            }),
16330        ] {
16331            let c = caixa_with_limits(limits.clone());
16332            let first = c.limits().unwrap();
16333            let second = c.limits().unwrap();
16334            assert_eq!(
16335                first, second,
16336                "Caixa::limits must be idempotent — two successive \
16337                 calls on the same &self must return the same \
16338                 &LimitsSpec",
16339            );
16340            assert!(
16341                std::ptr::eq(first, second),
16342                "Caixa::limits must borrow the underlying \
16343                 Option<LimitsSpec> storage — two successive calls \
16344                 must return references with the same backing pointer \
16345                 (a fresh LimitsSpec clone would change the pointer \
16346                 on every call)",
16347            );
16348            assert_eq!(
16349                Some(first),
16350                limits.as_ref(),
16351                "Caixa::limits must return :limits verbatim by borrow \
16352                 — got {first:?}, expected {:?}",
16353                limits.as_ref(),
16354            );
16355        }
16356        let c = caixa_with_limits(None);
16357        assert!(
16358            c.limits().is_none(),
16359            "Caixa::limits must return None when :limits is absent — \
16360             the author-omitted arm must project through the \
16361             accessor's Option::None unchanged",
16362        );
16363    }
16364
16365    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
16366
16367    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
16368        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16369        c.behavior = behavior;
16370        c
16371    }
16372
16373    #[test]
16374    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
16375        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
16376        // composite optional-composite-reference-shape pin:
16377        // [`Caixa::behavior`] must return the `:behavior` typed
16378        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
16379        // reference over the same backing storage the raw
16380        // `self.behavior.as_ref()` field access borrows from, byte-equal
16381        // across every representative fixture in the accept-set — the
16382        // author-omitted `None` shape (the "runtime-default applies"
16383        // partition every downstream Servico M2 overlay emitter treats
16384        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
16385        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
16386        // every per-callback path is `None`, so the peer M2 overlay
16387        // emitter's `.is_empty()`-gated projection still emits nothing
16388        // but the outer presence-bit is `Some`, so
16389        // [`Caixa::declared_servico_slots`] still pushes the
16390        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
16391        // (only `:on-state-change` set — the canonical shape a caixa
16392        // that only wires the hot-upgrade migration path carries), and
16393        // a fully-populated composite (every per-callback path set —
16394        // the canonical shape a fully-instrumented gen_server-shaped
16395        // Servico carries).
16396        //
16397        // Peer of the sibling
16398        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16399        // (b2bd9d7) opening fixture-family + reference-identity +
16400        // presence-bit tetrad pin on the outer top-level [`Caixa`]
16401        // `Option<&Composite>`-return sub-family — extended here to the
16402        // second axis of that sub-family so both of the currently-lifted
16403        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
16404        // `:behavior`) carry the same "byte-equal, borrow-shared,
16405        // presence-bit-preserved" outer-accessor discipline.
16406        //
16407        // Pins against a future silent detour that returned a fresh-
16408        // cloned [`crate::BehaviorSpec`] copy (which would type-check
16409        // via the `Clone` impl but silently break every downstream
16410        // caller that relied on the reference sharing the composite's
16411        // backing identity), a reference to an operator-resolved
16412        // overlay (a future per-cluster `:behavior-overrides` slot —
16413        // its resolution must land at exactly this accessor body, not
16414        // silently divert the raw slot away from a second consumer), a
16415        // `None` → `Some(BehaviorSpec::default)` cluster-default
16416        // projection (which would collapse the load-bearing
16417        // "author-omitted `:behavior` ⇒ runtime-default applies"
16418        // partition the peer [`crate::render::servico_m2_overlay`]
16419        // emitter, the peer [`Caixa::declared_servico_slots`]
16420        // enumerator, and the cross-slot
16421        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
16422        // gate all read), or a callback-shuffled projection (a future
16423        // detour that swapped `on_init` and `on_terminate` through the
16424        // accessor would silently split the paired
16425        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
16426        // traversal input from the peer `servico_m2_overlay` emitter's
16427        // projection input from the cross-slot `:state-change`
16428        // composition gate's traversal input).
16429        use crate::BehaviorSpec;
16430        use std::path::PathBuf;
16431        let fixtures: Vec<Option<BehaviorSpec>> = vec![
16432            None,
16433            Some(BehaviorSpec::default()),
16434            Some(BehaviorSpec {
16435                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16436                ..Default::default()
16437            }),
16438            Some(BehaviorSpec {
16439                on_init: Some(PathBuf::from("lib/init.lisp")),
16440                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16441                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16442                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16443                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16444                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16445            }),
16446        ];
16447        for behavior in fixtures {
16448            let c = caixa_with_behavior(behavior.clone());
16449            assert_eq!(
16450                c.behavior(),
16451                behavior.as_ref(),
16452                "Caixa::behavior must return :behavior verbatim (got \
16453                 {:?}, expected {:?})",
16454                c.behavior(),
16455                behavior.as_ref(),
16456            );
16457            match (c.behavior(), c.behavior.as_ref()) {
16458                (Some(a), Some(b)) => assert!(
16459                    std::ptr::eq(a, b),
16460                    "Caixa::behavior accessor and self.behavior.as_ref() \
16461                     field access must borrow the same backing storage \
16462                     — the accessor is the substrate-primitive typed \
16463                     dispatch every downstream Servico-M2-overlay \
16464                     composite consumer must route through, and a \
16465                     reference-identity split would silently break \
16466                     every consumer that relied on the borrow sharing \
16467                     the composite's storage",
16468                ),
16469                (None, None) => {}
16470                _ => panic!(
16471                    "Caixa::behavior presence bit must byte-equal \
16472                     self.behavior.is_some() — a presence-bit drift \
16473                     would silently split the paired \
16474                     StandardLayout::verify per-`:behavior` shape \
16475                     gate's traversal head from the peer \
16476                     render::servico_m2_overlay M2 overlay emitter's \
16477                     traversal head from the cross-slot \
16478                     validate_upgrade_from_against_behavior \
16479                     composition gate's traversal head from the peer \
16480                     Caixa::declared_servico_slots M2 declared-slot \
16481                     enumerator's presence probe",
16482                ),
16483            }
16484            assert_eq!(
16485                c.behavior().is_some(),
16486                c.behavior.is_some(),
16487                "Caixa::behavior().is_some() must byte-equal \
16488                 self.behavior.is_some() — a presence-bit drift would \
16489                 silently split every downstream Option<&BehaviorSpec> \
16490                 consumer's partition on the runtime-default arm",
16491            );
16492        }
16493    }
16494
16495    #[test]
16496    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
16497        // Composition pin: [`Caixa::declared_servico_slots`]'s
16498        // `:behavior` presence-probe arm must key off
16499        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
16500        // field-probe. Structurally: a `Caixa { behavior:
16501        // Some(BehaviorSpec::default()), .. }` must still push
16502        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
16503        // presence bit is `Some`, so the M2 kind-coherence gate must
16504        // surface the slot as "declared" even when every per-callback
16505        // path is unset), and a `Caixa { behavior: 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(BehaviorSpec::default())`
16510        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
16511        // silently absorb the "declared but empty" arm at the
16512        // accessor boundary and the
16513        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
16514        // kind-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) composition pin on the sibling `:limits` outer-
16520        // `Option<&LimitsSpec>` arm of the same
16521        // [`Caixa::declared_servico_slots`] M2 declared-slot
16522        // enumerator's traversal — same "the enumerator gate must
16523        // route through the substrate-primitive typed dispatch"
16524        // discipline extended onto the outer top-level [`Caixa`]
16525        // `Option<&BehaviorSpec>`-composition surface.
16526        use crate::BehaviorSpec;
16527        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16528        let slots = c.declared_servico_slots();
16529        assert!(
16530            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16531            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
16532             when `:behavior` is Some (even for BehaviorSpec::default()) \
16533             — the accessor and the enumerator gate must route through \
16534             the same substrate-primitive typed dispatch on the outer \
16535             :behavior presence bit (got slots={slots:?})",
16536        );
16537        let c = caixa_with_behavior(None);
16538        let slots = c.declared_servico_slots();
16539        assert!(
16540            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16541            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
16542             when `:behavior` is None — the author-omitted arm must \
16543             route through the accessor's None-return unchanged (got \
16544             slots={slots:?})",
16545        );
16546    }
16547
16548    #[test]
16549    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
16550        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16551        // per-`:behavior` M2 overlay emit arm must key off
16552        // [`Caixa::behavior`], not the raw `&caixa.behavior`
16553        // field-borrow. Structurally: a `Caixa { behavior:
16554        // Some(BehaviorSpec { on_state_change: Some(...), .. default
16555        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
16556        // per-callback `onStateChange` sub-mapping in the overlay, a
16557        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
16558        // must omit the key entirely (the `.is_empty()`-gated inner
16559        // arm elides an empty composite even when the outer presence
16560        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
16561        // also omit the key (the "author omitted the slot entirely"
16562        // partition). The three-fixture family jointly pins the
16563        // accessor + M2 overlay emitter composition: any future
16564        // silent detour that had the accessor return a fresh-cloned
16565        // copy on the `Some` arm (a `BehaviorSpec::clone()`
16566        // projection) would silently break the reference-identity
16567        // pin the peer per-callback `serde_yaml::to_value(behavior)`
16568        // projection reads from.
16569        //
16570        // Peer of the sibling
16571        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16572        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16573        // `Option<&LimitsSpec>` arm of the same
16574        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
16575        // traversal — same "the emitter must route through the
16576        // substrate-primitive typed dispatch on the outer composite"
16577        // discipline extended onto the outer top-level [`Caixa`]
16578        // `Option<&BehaviorSpec>`-composition surface.
16579        use crate::BehaviorSpec;
16580        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
16581        use std::path::PathBuf;
16582        let c = caixa_with_behavior(Some(BehaviorSpec {
16583            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16584            ..Default::default()
16585        }));
16586        let overlay = servico_m2_overlay(&c).unwrap();
16587        assert!(
16588            overlay.contains_key(M2_KEY_BEHAVIOR),
16589            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
16590             `:behavior` carries a non-empty composite — the accessor \
16591             and the M2 overlay emitter must route through the same \
16592             substrate-primitive typed dispatch on the outer :behavior \
16593             composite (got overlay={overlay:?})",
16594        );
16595        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16596        let overlay = servico_m2_overlay(&c).unwrap();
16597        assert!(
16598            !overlay.contains_key(M2_KEY_BEHAVIOR),
16599            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16600             `:behavior` is Some(BehaviorSpec::default()) — the empty \
16601             composite's `.is_empty()`-gated inner arm must elide the \
16602             key regardless of the outer presence bit (got \
16603             overlay={overlay:?})",
16604        );
16605        let c = caixa_with_behavior(None);
16606        let overlay = servico_m2_overlay(&c).unwrap();
16607        assert!(
16608            !overlay.contains_key(M2_KEY_BEHAVIOR),
16609            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16610             `:behavior` is None — the author-omitted arm must route \
16611             through the accessor's None-return unchanged (got \
16612             overlay={overlay:?})",
16613        );
16614    }
16615
16616    #[test]
16617    fn behavior_projects_option_ref_by_borrow() {
16618        // The by-borrow pin: [`Caixa::behavior`] returns
16619        // `Option<&BehaviorSpec>` by borrow — the returned reference
16620        // borrows the underlying `Option<BehaviorSpec>` storage of the
16621        // `:behavior` slot and the accessor must not clone the backing
16622        // composite on every call. Peer of the sibling
16623        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
16624        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
16625        // return sub-family — extended here to the second axis of the
16626        // same sub-family: the accessor's returned reference must
16627        // borrow from `&self` (the returned reference's lifetime is
16628        // tied to `&self`), and calling the accessor twice on the same
16629        // [`Caixa`] must yield references that are pointer-equal (the
16630        // underlying byte-buffer is the storage `BehaviorSpec`'s
16631        // allocation, not a fresh copy) as well as value-equal
16632        // (idempotent, no side effects on `&self`).
16633        //
16634        // Pins against a future silent detour that returned an owned
16635        // `BehaviorSpec` (which would type-check via the `Clone` impl
16636        // but silently clone on every call), a `&BehaviorSpec` panic-
16637        // return on the `None` arm (which would collapse the load-
16638        // bearing `Option` presence-bit into a runtime panic), or a
16639        // one-arm-only accessor that returned a saturating composite
16640        // on some sentinel input.
16641        use crate::BehaviorSpec;
16642        use std::path::PathBuf;
16643        for behavior in [
16644            Some(BehaviorSpec::default()),
16645            Some(BehaviorSpec {
16646                on_init: Some(PathBuf::from("lib/init.lisp")),
16647                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16648                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16649                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16650                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16651                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16652            }),
16653        ] {
16654            let c = caixa_with_behavior(behavior.clone());
16655            let first = c.behavior().unwrap();
16656            let second = c.behavior().unwrap();
16657            assert_eq!(
16658                first, second,
16659                "Caixa::behavior must be idempotent — two successive \
16660                 calls on the same &self must return the same \
16661                 &BehaviorSpec",
16662            );
16663            assert!(
16664                std::ptr::eq(first, second),
16665                "Caixa::behavior must borrow the underlying \
16666                 Option<BehaviorSpec> storage — two successive calls \
16667                 must return references with the same backing pointer \
16668                 (a fresh BehaviorSpec clone would change the pointer \
16669                 on every call)",
16670            );
16671            assert_eq!(
16672                Some(first),
16673                behavior.as_ref(),
16674                "Caixa::behavior must return :behavior verbatim by \
16675                 borrow — got {first:?}, expected {:?}",
16676                behavior.as_ref(),
16677            );
16678        }
16679        let c = caixa_with_behavior(None);
16680        assert!(
16681            c.behavior().is_none(),
16682            "Caixa::behavior must return None when :behavior is absent \
16683             — the author-omitted arm must project through the \
16684             accessor's Option::None unchanged",
16685        );
16686    }
16687
16688    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
16689
16690    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
16691        use crate::aplicacao::{Membro, WitContract};
16692        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16693        c.kind = CaixaKind::Aplicacao;
16694        c.membros = vec![Membro {
16695            caixa: "a".into(),
16696            versao: "^0.1".into(),
16697        }];
16698        c.contratos = vec![WitContract {
16699            de: "a".into(),
16700            para: "a".into(),
16701            wit: "wasi:http/proxy".into(),
16702            endpoint: Some("/x".into()),
16703            subject: None,
16704            slot: None,
16705        }];
16706        c.politicas = politicas;
16707        c
16708    }
16709
16710    #[test]
16711    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
16712        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
16713        // composite optional-composite-reference-shape pin:
16714        // [`Caixa::politicas`] must return the `:politicas` typed
16715        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
16716        // reference over the same backing storage the raw
16717        // `self.politicas.as_ref()` field access borrows from,
16718        // byte-equal across every representative fixture in the
16719        // accept-set — the author-omitted `None` shape (the "cluster-
16720        // default applies" partition every downstream mesh-artifact
16721        // emitter treats as "emit no `:politicas` overlay"), the
16722        // empty-composite `Some(MeshPolicy { .. default })` shape
16723        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
16724        // per-axis mesh-policy scalar is `None`, so the peer inner
16725        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
16726        // caixa-mesh overlay elides every per-axis emit but the outer
16727        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
16728        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
16729        // single-axis fixture (only `:timeout` set — the canonical
16730        // shape a latency-sensitive Aplicacao carries), and a
16731        // fully-populated composite (every per-axis mesh-policy
16732        // scalar set — the canonical shape a fully-governed
16733        // Aplicacao carries).
16734        //
16735        // Pins against a future silent detour that returned a fresh-
16736        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
16737        // type-check via the `Clone` impl but silently break every
16738        // downstream caller that relied on the reference sharing the
16739        // composite's backing identity), a reference to an operator-
16740        // resolved overlay (the future per-cluster
16741        // `:politicas-overrides` slot — its resolution must land at
16742        // exactly this accessor body, not silently divert the raw
16743        // slot away from the peer [`Caixa::declared_mesh_slots`]
16744        // enumerator's presence probe), a
16745        // `None` → `Some(MeshPolicy::default)` cluster-default
16746        // projection (which would collapse the load-bearing
16747        // "author-omitted `:politicas` ⇒ cluster-default applies"
16748        // partition the peer [`Caixa::declared_mesh_slots`]
16749        // enumerator and the peer [`Caixa::aplicacao_view`]
16750        // Aplicacao-composition seed both read), or an axis-shuffled
16751        // projection (a future detour that swapped `timeout` and
16752        // `retries` through the accessor would silently split the
16753        // paired [`Caixa::aplicacao_view`] seed's fold input from the
16754        // sibling M3 mesh-artifact emitter's projection input).
16755        //
16756        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
16757        // composite-reference accessor pin on the substrate primitive
16758        // — peer of the sibling
16759        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16760        // (b2bd9d7) and
16761        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16762        // (35d8b52) opening tetrad pins on the outer top-level
16763        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16764        // here to the first of the three M3 mesh-slot axes so the
16765        // opening third of the outer `Option<&Composite>` sub-family
16766        // carries the same "byte-equal, borrow-shared, presence-bit-
16767        // preserved" outer-accessor discipline.
16768        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16769        use std::time::Duration;
16770        let fixtures: Vec<Option<MeshPolicy>> = vec![
16771            None,
16772            Some(MeshPolicy::default()),
16773            Some(MeshPolicy {
16774                timeout: Some(Duration::from_secs(30)),
16775                ..Default::default()
16776            }),
16777            Some(MeshPolicy {
16778                timeout: Some(Duration::from_secs(30)),
16779                retries: Some(3),
16780                circuit_breaker: Some(CircuitBreaker {
16781                    max_failures: 5,
16782                    window: Duration::from_secs(60),
16783                }),
16784                mtls_required: Some(true),
16785                rate_limit: Some(RateLimit {
16786                    rate: 100,
16787                    window: Duration::from_secs(1),
16788                }),
16789            }),
16790        ];
16791        for politicas in fixtures {
16792            let c = caixa_aplicacao_with_politicas(politicas.clone());
16793            assert_eq!(
16794                c.politicas(),
16795                politicas.as_ref(),
16796                "Caixa::politicas must return :politicas verbatim (got \
16797                 {:?}, expected {:?})",
16798                c.politicas(),
16799                politicas.as_ref(),
16800            );
16801            match (c.politicas(), c.politicas.as_ref()) {
16802                (Some(a), Some(b)) => assert!(
16803                    std::ptr::eq(a, b),
16804                    "Caixa::politicas accessor and self.politicas.as_ref() \
16805                     field access must borrow the same backing storage \
16806                     — the accessor is the substrate-primitive typed \
16807                     dispatch every downstream Aplicacao-mesh-overlay \
16808                     composite consumer must route through, and a \
16809                     reference-identity split would silently break \
16810                     every consumer that relied on the borrow sharing \
16811                     the composite's storage",
16812                ),
16813                (None, None) => {}
16814                _ => panic!(
16815                    "Caixa::politicas presence bit must byte-equal \
16816                     self.politicas.is_some() — a presence-bit drift \
16817                     would silently split the paired \
16818                     Caixa::aplicacao_view Aplicacao-composition seed's \
16819                     traversal head from the peer \
16820                     Caixa::declared_mesh_slots M3 declared-slot \
16821                     enumerator's presence probe",
16822                ),
16823            }
16824            assert_eq!(
16825                c.politicas().is_some(),
16826                c.politicas.is_some(),
16827                "Caixa::politicas().is_some() must byte-equal \
16828                 self.politicas.is_some() — a presence-bit drift would \
16829                 silently split every downstream Option<&MeshPolicy> \
16830                 consumer's partition on the cluster-default arm",
16831            );
16832        }
16833    }
16834
16835    #[test]
16836    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
16837        // Composition pin: [`Caixa::declared_mesh_slots`]'s
16838        // `:politicas` presence-probe arm must key off
16839        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
16840        // field-probe. Structurally: a `Caixa { politicas:
16841        // Some(MeshPolicy::default()), .. }` must still push
16842        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
16843        // presence bit is `Some`, so the M3 kind-coherence gate must
16844        // surface the slot as "declared" even when every per-axis
16845        // scalar is unset), and a `Caixa { politicas: None, .. }` must
16846        // NOT push the label (the "author omitted the slot entirely"
16847        // partition). The pair jointly pins the accessor + declared-
16848        // slot enumerator composition: any future silent detour that
16849        // had the accessor collapse `Some(MeshPolicy::default())` to
16850        // `None` (a `.filter(|p| !p.is_empty())` projection) would
16851        // silently absorb the "declared but empty" arm at the
16852        // accessor boundary and the
16853        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16854        // coherence gate would silently accept a struct-literal
16855        // `Caixa` carrying the drift.
16856        //
16857        // Peer of the sibling
16858        // `declared_servico_slots_limits_arm_routes_through_accessor`
16859        // (b2bd9d7) and
16860        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16861        // (35d8b52) composition pins on the sibling `:limits` /
16862        // `:behavior` outer-`Option<&Composite>` arms of the peer
16863        // [`Caixa::declared_servico_slots`] M2 declared-slot
16864        // enumerator's traversal — same "the enumerator gate must
16865        // route through the substrate-primitive typed dispatch"
16866        // discipline extended onto the outer top-level [`Caixa`] M3
16867        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
16868        // enumerator carries the same routing invariant as its M2
16869        // sibling.
16870        use crate::aplicacao::MeshPolicy;
16871        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16872        let slots = c.declared_mesh_slots();
16873        assert!(
16874            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16875            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
16876             when `:politicas` is Some (even for MeshPolicy::default()) \
16877             — the accessor and the enumerator gate must route through \
16878             the same substrate-primitive typed dispatch on the outer \
16879             :politicas presence bit (got slots={slots:?})",
16880        );
16881        let c = caixa_aplicacao_with_politicas(None);
16882        let slots = c.declared_mesh_slots();
16883        assert!(
16884            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16885            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
16886             when `:politicas` is None — the author-omitted arm must \
16887             route through the accessor's None-return unchanged (got \
16888             slots={slots:?})",
16889        );
16890    }
16891
16892    #[test]
16893    fn aplicacao_view_politicas_arm_folds_through_accessor() {
16894        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
16895        // Aplicacao-composition seed must fold through
16896        // [`Caixa::politicas`], not the raw
16897        // `self.politicas.clone().unwrap_or_default()` field-borrow.
16898        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
16899        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
16900        // must surface a projected [`crate::AplicacaoSpec`] whose
16901        // `politicas().timeout()` field byte-equals the outer
16902        // composite's `timeout` scalar (the fold must project the
16903        // authored composite verbatim), a `Caixa { politicas:
16904        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
16905        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
16906        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
16907        // fold's empty-composite arm collapses to the same default the
16908        // author-omitted arm does), and a `Caixa { politicas: None,
16909        // kind: Aplicacao, .. }` must surface an
16910        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
16911        // [`crate::aplicacao::MeshPolicy::default`] (the "author
16912        // omitted the slot entirely" arm folds through the
16913        // `unwrap_or_default` onto the cluster-default). The triad
16914        // jointly pins the accessor + Aplicacao-composition seed
16915        // composition: any future silent detour that had the accessor
16916        // divert the raw slot away from the seed's fold (an operator-
16917        // resolved overlay's default-fold arm silently differing from
16918        // the raw slot's default-fold arm) would silently split the
16919        // build-time mesh-artifact emission gate from the caixa-mesh
16920        // renderer's Aplicacao-view input at the composition boundary.
16921        use crate::aplicacao::MeshPolicy;
16922        use std::time::Duration;
16923        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
16924            timeout: Some(Duration::from_secs(30)),
16925            ..Default::default()
16926        }));
16927        let view = c.aplicacao_view().unwrap();
16928        assert_eq!(
16929            view.politicas().timeout(),
16930            Some(Duration::from_secs(30)),
16931            "Caixa::aplicacao_view must fold the authored :politicas \
16932             :timeout scalar through the accessor verbatim onto the \
16933             projected AplicacaoSpec — a future silent detour at the \
16934             seed's fold arm would surface here as a projected-scalar \
16935             drift (got {:?})",
16936            view.politicas().timeout(),
16937        );
16938        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16939        let view = c.aplicacao_view().unwrap();
16940        assert_eq!(
16941            view.politicas(),
16942            &MeshPolicy::default(),
16943            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
16944             through the accessor onto MeshPolicy::default — the empty- \
16945             composite arm collapses to the same default the author- \
16946             omitted arm does (got {:?})",
16947            view.politicas(),
16948        );
16949        let c = caixa_aplicacao_with_politicas(None);
16950        let view = c.aplicacao_view().unwrap();
16951        assert_eq!(
16952            view.politicas(),
16953            &MeshPolicy::default(),
16954            "Caixa::aplicacao_view must fold None through the accessor's \
16955             unwrap_or_default onto MeshPolicy::default — the author- \
16956             omitted arm must route through the accessor's None-return \
16957             unchanged (got {:?})",
16958            view.politicas(),
16959        );
16960    }
16961
16962    #[test]
16963    fn politicas_projects_option_ref_by_borrow() {
16964        // The by-borrow pin: [`Caixa::politicas`] returns
16965        // `Option<&MeshPolicy>` by borrow — the returned reference
16966        // borrows the underlying `Option<MeshPolicy>` storage of the
16967        // `:politicas` slot and the accessor must not clone the
16968        // backing composite on every call. Peer of the sibling
16969        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
16970        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
16971        // pins on the outer top-level [`Caixa`]
16972        // `Option<&Composite>`-return sub-family — extended here to
16973        // the third axis of the same sub-family: the accessor's
16974        // returned reference must borrow from `&self` (the returned
16975        // reference's lifetime is tied to `&self`), and calling the
16976        // accessor twice on the same [`Caixa`] must yield references
16977        // that are pointer-equal (the underlying byte-buffer is the
16978        // storage `MeshPolicy`'s allocation, not a fresh copy) as
16979        // well as value-equal (idempotent, no side effects on
16980        // `&self`).
16981        //
16982        // Pins against a future silent detour that returned an owned
16983        // `MeshPolicy` (which would type-check via the `Clone` impl
16984        // but silently clone on every call), a `&MeshPolicy` panic-
16985        // return on the `None` arm (which would collapse the load-
16986        // bearing `Option` presence-bit into a runtime panic), or a
16987        // one-arm-only accessor that returned a saturating composite
16988        // on some sentinel input.
16989        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16990        use std::time::Duration;
16991        for politicas in [
16992            Some(MeshPolicy::default()),
16993            Some(MeshPolicy {
16994                timeout: Some(Duration::from_secs(30)),
16995                retries: Some(3),
16996                circuit_breaker: Some(CircuitBreaker {
16997                    max_failures: 5,
16998                    window: Duration::from_secs(60),
16999                }),
17000                mtls_required: Some(true),
17001                rate_limit: Some(RateLimit {
17002                    rate: 100,
17003                    window: Duration::from_secs(1),
17004                }),
17005            }),
17006        ] {
17007            let c = caixa_aplicacao_with_politicas(politicas.clone());
17008            let first = c.politicas().unwrap();
17009            let second = c.politicas().unwrap();
17010            assert_eq!(
17011                first, second,
17012                "Caixa::politicas must be idempotent — two successive \
17013                 calls on the same &self must return the same \
17014                 &MeshPolicy",
17015            );
17016            assert!(
17017                std::ptr::eq(first, second),
17018                "Caixa::politicas must borrow the underlying \
17019                 Option<MeshPolicy> storage — two successive calls \
17020                 must return references with the same backing pointer \
17021                 (a fresh MeshPolicy clone would change the pointer on \
17022                 every call)",
17023            );
17024            assert_eq!(
17025                Some(first),
17026                politicas.as_ref(),
17027                "Caixa::politicas must return :politicas verbatim by \
17028                 borrow — got {first:?}, expected {:?}",
17029                politicas.as_ref(),
17030            );
17031        }
17032        let c = caixa_aplicacao_with_politicas(None);
17033        assert!(
17034            c.politicas().is_none(),
17035            "Caixa::politicas must return None when :politicas is \
17036             absent — the author-omitted arm must project through the \
17037             accessor's Option::None unchanged",
17038        );
17039    }
17040
17041    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
17042
17043    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
17044        use crate::aplicacao::{Membro, WitContract};
17045        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17046        c.kind = CaixaKind::Aplicacao;
17047        c.membros = vec![Membro {
17048            caixa: "a".into(),
17049            versao: "^0.1".into(),
17050        }];
17051        c.contratos = vec![WitContract {
17052            de: "a".into(),
17053            para: "a".into(),
17054            wit: "wasi:http/proxy".into(),
17055            endpoint: Some("/x".into()),
17056            subject: None,
17057            slot: None,
17058        }];
17059        c.placement = placement;
17060        c
17061    }
17062
17063    #[test]
17064    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
17065        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
17066        // composite optional-composite-reference-shape pin:
17067        // [`Caixa::placement`] must return the `:placement` typed
17068        // `Option<Placement>` verbatim as an `Option<&Placement>`
17069        // reference over the same backing storage the raw
17070        // `self.placement.as_ref()` field access borrows from,
17071        // byte-equal across every representative fixture in the
17072        // accept-set — the author-omitted `None` shape (the
17073        // "cluster-default applies" partition every downstream mesh-
17074        // artifact emitter treats as "emit no `:placement` overlay"),
17075        // the empty-composite `Some(Placement { .. default })` shape
17076        // (`estrategia: SingleNode`, empty clusters, no shard-key /
17077        // affinity — the outer presence-bit is `Some` so
17078        // [`Caixa::declared_mesh_slots`] still pushes the
17079        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
17080        // `Replicated`-on-two-clusters fixture (the canonical shape a
17081        // stateless HTTP Aplicacao carries), and a fully-populated
17082        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
17083        // shape a stateful Akka-style cluster-sharding Aplicacao
17084        // carries).
17085        //
17086        // Pins against a future silent detour that returned a fresh-
17087        // cloned [`crate::aplicacao::Placement`] copy (which would
17088        // type-check via the `Clone` impl but silently break every
17089        // downstream caller that relied on the reference sharing the
17090        // composite's backing identity), a reference to an operator-
17091        // resolved overlay (the future per-cluster
17092        // `:placement-overrides` slot — its resolution must land at
17093        // exactly this accessor body, not silently divert the raw
17094        // slot away from the peer [`Caixa::declared_mesh_slots`]
17095        // enumerator's presence probe), a `None` →
17096        // `Some(Placement::default)` cluster-default projection (which
17097        // would collapse the load-bearing "author-omitted `:placement`
17098        // ⇒ cluster-default applies" partition the peer
17099        // [`Caixa::declared_mesh_slots`] enumerator and the peer
17100        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
17101        // read), or an axis-shuffled projection (a future detour that
17102        // swapped `clusters` and `affinity` through the accessor would
17103        // silently split the paired [`Caixa::aplicacao_view`] seed's
17104        // fold input from the sibling M3 mesh-artifact emitter's
17105        // projection input).
17106        //
17107        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
17108        // composite-reference accessor pin on the substrate primitive
17109        // — peer of the sibling
17110        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17111        // (b2bd9d7),
17112        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17113        // (35d8b52), and
17114        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17115        // (5d23d29) opening triad pins on the outer top-level
17116        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17117        // here to the second of the three M3 mesh-slot axes so the
17118        // opening four-fifths of the outer `Option<&Composite>` sub-
17119        // family carries the same "byte-equal, borrow-shared,
17120        // presence-bit-preserved" outer-accessor discipline.
17121        use crate::aplicacao::{Placement, PlacementStrategy};
17122        let fixtures: Vec<Option<Placement>> = vec![
17123            None,
17124            Some(Placement::default()),
17125            Some(Placement {
17126                estrategia: PlacementStrategy::Replicated,
17127                clusters: vec!["rio".into(), "sao-paulo".into()],
17128                affinity: None,
17129                shard_key: None,
17130            }),
17131            Some(Placement {
17132                estrategia: PlacementStrategy::Sharded,
17133                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
17134                affinity: Some("data-locality".into()),
17135                shard_key: Some("$tenantId".into()),
17136            }),
17137        ];
17138        for placement in fixtures {
17139            let c = caixa_aplicacao_with_placement(placement.clone());
17140            assert_eq!(
17141                c.placement(),
17142                placement.as_ref(),
17143                "Caixa::placement must return :placement verbatim (got \
17144                 {:?}, expected {:?})",
17145                c.placement(),
17146                placement.as_ref(),
17147            );
17148            match (c.placement(), c.placement.as_ref()) {
17149                (Some(a), Some(b)) => assert!(
17150                    std::ptr::eq(a, b),
17151                    "Caixa::placement accessor and self.placement.as_ref() \
17152                     field access must borrow the same backing storage \
17153                     — the accessor is the substrate-primitive typed \
17154                     dispatch every downstream Aplicacao-distribution- \
17155                     overlay composite consumer must route through, and \
17156                     a reference-identity split would silently break \
17157                     every consumer that relied on the borrow sharing \
17158                     the composite's storage",
17159                ),
17160                (None, None) => {}
17161                _ => panic!(
17162                    "Caixa::placement presence bit must byte-equal \
17163                     self.placement.is_some() — a presence-bit drift \
17164                     would silently split the paired \
17165                     Caixa::aplicacao_view Aplicacao-composition seed's \
17166                     traversal head from the peer \
17167                     Caixa::declared_mesh_slots M3 declared-slot \
17168                     enumerator's presence probe",
17169                ),
17170            }
17171            assert_eq!(
17172                c.placement().is_some(),
17173                c.placement.is_some(),
17174                "Caixa::placement().is_some() must byte-equal \
17175                 self.placement.is_some() — a presence-bit drift would \
17176                 silently split every downstream Option<&Placement> \
17177                 consumer's partition on the cluster-default arm",
17178            );
17179        }
17180    }
17181
17182    #[test]
17183    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
17184        // Composition pin: [`Caixa::declared_mesh_slots`]'s
17185        // `:placement` presence-probe arm must key off
17186        // [`Caixa::placement`], not the raw `self.placement.is_some()`
17187        // field-probe. Structurally: a `Caixa { placement:
17188        // Some(Placement::default()), .. }` must still push
17189        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
17190        // presence bit is `Some`, so the M3 kind-coherence gate must
17191        // surface the slot as "declared" even when every per-axis
17192        // scalar defers to the cluster-default arm), and a `Caixa {
17193        // placement: None, .. }` must NOT push the label (the "author
17194        // omitted the slot entirely" partition). The pair jointly pins
17195        // the accessor + declared-slot enumerator composition: any
17196        // future silent detour that had the accessor collapse
17197        // `Some(Placement::default())` to `None` (a `.filter(|p|
17198        // p.clusters().is_empty().not())` projection) would silently
17199        // absorb the "declared but empty" arm at the accessor boundary
17200        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
17201        // kind-coherence gate would silently accept a struct-literal
17202        // `Caixa` carrying the drift.
17203        //
17204        // Peer of the sibling
17205        // `declared_servico_slots_limits_arm_routes_through_accessor`
17206        // (b2bd9d7),
17207        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17208        // (35d8b52), and
17209        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17210        // (5d23d29) composition pins on the sibling `:limits` /
17211        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
17212        // — same "the enumerator gate must route through the
17213        // substrate-primitive typed dispatch" discipline extended onto
17214        // the second of the three M3 mesh-slot axes so the
17215        // [`Caixa::declared_mesh_slots`] enumerator carries the same
17216        // routing invariant on the `:placement` arm as the peer
17217        // `:politicas` arm.
17218        use crate::aplicacao::Placement;
17219        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
17220        let slots = c.declared_mesh_slots();
17221        assert!(
17222            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
17223            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
17224             when `:placement` is Some (even for Placement::default()) \
17225             — the accessor and the enumerator gate must route through \
17226             the same substrate-primitive typed dispatch on the outer \
17227             :placement presence bit (got slots={slots:?})",
17228        );
17229        let c = caixa_aplicacao_with_placement(None);
17230        let slots = c.declared_mesh_slots();
17231        assert!(
17232            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
17233            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
17234             when `:placement` is None — the author-omitted arm must \
17235             route through the accessor's None-return unchanged (got \
17236             slots={slots:?})",
17237        );
17238    }
17239
17240    #[test]
17241    fn aplicacao_view_placement_arm_folds_through_accessor() {
17242        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
17243        // Aplicacao-composition seed must fold through
17244        // [`Caixa::placement`], not the raw
17245        // `self.placement.clone().unwrap_or_default()` field-borrow.
17246        // Structurally: a `Caixa { placement: Some(Placement {
17247        // estrategia: Replicated, clusters: ["rio"], .. default }),
17248        // kind: Aplicacao, .. }` must surface a projected
17249        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
17250        // `placement().clusters()` byte-equal the outer composite's
17251        // authored values (the fold must project the authored
17252        // composite verbatim), a `Caixa { placement:
17253        // Some(Placement::default()), kind: Aplicacao, .. }` must
17254        // surface an [`crate::AplicacaoSpec`] whose `placement()`
17255        // byte-equals [`crate::aplicacao::Placement::default`] (the
17256        // fold's empty-composite arm collapses to the same default
17257        // the author-omitted arm does), and a `Caixa { placement:
17258        // None, kind: Aplicacao, .. }` must surface an
17259        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
17260        // [`crate::aplicacao::Placement::default`] (the "author
17261        // omitted the slot entirely" arm folds through the
17262        // `unwrap_or_default` onto the cluster-default). The triad
17263        // jointly pins the accessor + Aplicacao-composition seed
17264        // composition: any future silent detour that had the accessor
17265        // divert the raw slot away from the seed's fold (an operator-
17266        // resolved overlay's default-fold arm silently differing from
17267        // the raw slot's default-fold arm) would silently split the
17268        // build-time distribution-artifact emission gate from the
17269        // caixa-mesh renderer's Aplicacao-view input at the
17270        // composition boundary.
17271        use crate::aplicacao::{Placement, PlacementStrategy};
17272        let c = caixa_aplicacao_with_placement(Some(Placement {
17273            estrategia: PlacementStrategy::Replicated,
17274            clusters: vec!["rio".into()],
17275            affinity: None,
17276            shard_key: None,
17277        }));
17278        let view = c.aplicacao_view().unwrap();
17279        assert_eq!(
17280            view.placement().estrategia(),
17281            PlacementStrategy::Replicated,
17282            "Caixa::aplicacao_view must fold the authored :placement \
17283             :estrategia scalar through the accessor verbatim onto the \
17284             projected AplicacaoSpec — a future silent detour at the \
17285             seed's fold arm would surface here as a projected-scalar \
17286             drift (got {:?})",
17287            view.placement().estrategia(),
17288        );
17289        assert_eq!(
17290            view.placement().clusters(),
17291            &["rio"],
17292            "Caixa::aplicacao_view must fold the authored :placement \
17293             :clusters list through the accessor verbatim onto the \
17294             projected AplicacaoSpec — a future silent detour at the \
17295             seed's fold arm would surface here as a projected-list \
17296             drift (got {:?})",
17297            view.placement().clusters(),
17298        );
17299        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
17300        let view = c.aplicacao_view().unwrap();
17301        assert_eq!(
17302            view.placement(),
17303            &Placement::default(),
17304            "Caixa::aplicacao_view must fold Some(Placement::default()) \
17305             through the accessor onto Placement::default — the empty- \
17306             composite arm collapses to the same default the author- \
17307             omitted arm does (got {:?})",
17308            view.placement(),
17309        );
17310        let c = caixa_aplicacao_with_placement(None);
17311        let view = c.aplicacao_view().unwrap();
17312        assert_eq!(
17313            view.placement(),
17314            &Placement::default(),
17315            "Caixa::aplicacao_view must fold None through the accessor's \
17316             unwrap_or_default onto Placement::default — the author- \
17317             omitted arm must route through the accessor's None-return \
17318             unchanged (got {:?})",
17319            view.placement(),
17320        );
17321    }
17322
17323    #[test]
17324    fn placement_projects_option_ref_by_borrow() {
17325        // The by-borrow pin: [`Caixa::placement`] returns
17326        // `Option<&Placement>` by borrow — the returned reference
17327        // borrows the underlying `Option<Placement>` storage of the
17328        // `:placement` slot and the accessor must not clone the
17329        // backing composite on every call. Peer of the sibling
17330        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17331        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
17332        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
17333        // pins on the outer top-level [`Caixa`]
17334        // `Option<&Composite>`-return sub-family — extended here to
17335        // the fourth axis of the same sub-family: the accessor's
17336        // returned reference must borrow from `&self` (the returned
17337        // reference's lifetime is tied to `&self`), and calling the
17338        // accessor twice on the same [`Caixa`] must yield references
17339        // that are pointer-equal (the underlying byte-buffer is the
17340        // storage `Placement`'s allocation, not a fresh copy) as well
17341        // as value-equal (idempotent, no side effects on `&self`).
17342        //
17343        // Pins against a future silent detour that returned an owned
17344        // `Placement` (which would type-check via the `Clone` impl
17345        // but silently clone on every call), a `&Placement` panic-
17346        // return on the `None` arm (which would collapse the load-
17347        // bearing `Option` presence-bit into a runtime panic), or a
17348        // one-arm-only accessor that returned a saturating composite
17349        // on some sentinel input.
17350        use crate::aplicacao::{Placement, PlacementStrategy};
17351        for placement in [
17352            Some(Placement::default()),
17353            Some(Placement {
17354                estrategia: PlacementStrategy::Sharded,
17355                clusters: vec!["rio".into(), "sao-paulo".into()],
17356                affinity: Some("data-locality".into()),
17357                shard_key: Some("$tenantId".into()),
17358            }),
17359        ] {
17360            let c = caixa_aplicacao_with_placement(placement.clone());
17361            let first = c.placement().unwrap();
17362            let second = c.placement().unwrap();
17363            assert_eq!(
17364                first, second,
17365                "Caixa::placement must be idempotent — two successive \
17366                 calls on the same &self must return the same \
17367                 &Placement",
17368            );
17369            assert!(
17370                std::ptr::eq(first, second),
17371                "Caixa::placement must borrow the underlying \
17372                 Option<Placement> storage — two successive calls \
17373                 must return references with the same backing pointer \
17374                 (a fresh Placement clone would change the pointer on \
17375                 every call)",
17376            );
17377            assert_eq!(
17378                Some(first),
17379                placement.as_ref(),
17380                "Caixa::placement must return :placement verbatim by \
17381                 borrow — got {first:?}, expected {:?}",
17382                placement.as_ref(),
17383            );
17384        }
17385        let c = caixa_aplicacao_with_placement(None);
17386        assert!(
17387            c.placement().is_none(),
17388            "Caixa::placement must return None when :placement is \
17389             absent — the author-omitted arm must project through the \
17390             accessor's Option::None unchanged",
17391        );
17392    }
17393
17394    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
17395
17396    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
17397        use crate::aplicacao::{Membro, WitContract};
17398        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17399        c.kind = CaixaKind::Aplicacao;
17400        c.membros = vec![Membro {
17401            caixa: "a".into(),
17402            versao: "^0.1".into(),
17403        }];
17404        c.contratos = vec![WitContract {
17405            de: "a".into(),
17406            para: "a".into(),
17407            wit: "wasi:http/proxy".into(),
17408            endpoint: Some("/x".into()),
17409            subject: None,
17410            slot: None,
17411        }];
17412        c.entrada = entrada;
17413        c
17414    }
17415
17416    #[test]
17417    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
17418        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
17419        // composite optional-composite-reference-shape pin:
17420        // [`Caixa::entrada`] must return the `:entrada` typed
17421        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
17422        // reference over the same backing storage the raw
17423        // `self.entrada.as_ref()` field access borrows from,
17424        // byte-equal across every representative fixture in the
17425        // accept-set — the author-omitted `None` shape (the
17426        // "cluster-internal Aplicacao" partition every downstream
17427        // Gateway-API emitter treats as "emit no listener + no
17428        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
17429        // (empty `paths` — the resolved-paths fallback the peer
17430        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
17431        // onto the substrate catch-all), and a fully-populated
17432        // multi-path-with-non-default-port fixture (the canonical
17433        // shape a public HTTP Aplicacao carries).
17434        //
17435        // Pins against a future silent detour that returned a fresh-
17436        // cloned [`crate::aplicacao::Entrada`] copy (which would
17437        // type-check via the `Clone` impl but silently break every
17438        // downstream caller that relied on the reference sharing the
17439        // composite's backing identity), a reference to an operator-
17440        // resolved overlay (the future per-cluster
17441        // `:entrada-overrides` slot — its resolution must land at
17442        // exactly this accessor body, not silently divert the raw
17443        // slot away from the peer [`Caixa::declared_mesh_slots`]
17444        // enumerator's presence probe), or an axis-shuffled projection
17445        // (a future detour that swapped `host` and `para` through the
17446        // accessor would silently split the paired
17447        // [`Caixa::aplicacao_view`] seed's forward input from the
17448        // sibling M3 gateway-artifact emitter's projection input).
17449        //
17450        // Fifth and final outer top-level [`Caixa`]
17451        // `Option<&Composite>`-return composite-reference accessor pin
17452        // on the substrate primitive — peer of the sibling
17453        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17454        // (b2bd9d7),
17455        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17456        // (35d8b52),
17457        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17458        // (5d23d29), and
17459        // `placement_returns_placement_option_ref_verbatim_across_permutations`
17460        // (4fb8074) opening tetrad pins on the outer top-level
17461        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17462        // here to the third and final M3 mesh-slot axis so the closed
17463        // outer `Option<&Composite>` sub-family carries the same
17464        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
17465        // accessor discipline across all five arms.
17466        use crate::aplicacao::Entrada;
17467        let fixtures: Vec<Option<Entrada>> = vec![
17468            None,
17469            Some(Entrada {
17470                host: "checkout.quero.cloud".into(),
17471                para: "gateway".into(),
17472                paths: Vec::new(),
17473                port: crate::DEFAULT_SERVICO_PORT,
17474            }),
17475            Some(Entrada {
17476                host: "api.pleme.io".into(),
17477                para: "public-api".into(),
17478                paths: vec!["/v1".into(), "/v2".into()],
17479                port: 8080,
17480            }),
17481        ];
17482        for entrada in fixtures {
17483            let c = caixa_aplicacao_with_entrada(entrada.clone());
17484            assert_eq!(
17485                c.entrada(),
17486                entrada.as_ref(),
17487                "Caixa::entrada must return :entrada verbatim (got \
17488                 {:?}, expected {:?})",
17489                c.entrada(),
17490                entrada.as_ref(),
17491            );
17492            match (c.entrada(), c.entrada.as_ref()) {
17493                (Some(a), Some(b)) => assert!(
17494                    std::ptr::eq(a, b),
17495                    "Caixa::entrada accessor and self.entrada.as_ref() \
17496                     field access must borrow the same backing storage \
17497                     — the accessor is the substrate-primitive typed \
17498                     dispatch every downstream Aplicacao-external- \
17499                     gateway composite consumer must route through, and \
17500                     a reference-identity split would silently break \
17501                     every consumer that relied on the borrow sharing \
17502                     the composite's storage",
17503                ),
17504                (None, None) => {}
17505                _ => panic!(
17506                    "Caixa::entrada presence bit must byte-equal \
17507                     self.entrada.is_some() — a presence-bit drift \
17508                     would silently split the paired \
17509                     Caixa::aplicacao_view Aplicacao-composition seed's \
17510                     traversal head from the peer \
17511                     Caixa::declared_mesh_slots M3 declared-slot \
17512                     enumerator's presence probe",
17513                ),
17514            }
17515            assert_eq!(
17516                c.entrada().is_some(),
17517                c.entrada.is_some(),
17518                "Caixa::entrada().is_some() must byte-equal \
17519                 self.entrada.is_some() — a presence-bit drift would \
17520                 silently split every downstream Option<&Entrada> \
17521                 consumer's partition on the cluster-internal arm",
17522            );
17523        }
17524    }
17525
17526    #[test]
17527    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
17528        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
17529        // presence-probe arm must key off [`Caixa::entrada`], not the
17530        // raw `self.entrada.is_some()` field-probe. Structurally: a
17531        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
17532        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
17533        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
17534        // presence bit is `Some`, so the M3 kind-coherence gate must
17535        // surface the slot as "declared" even when every per-axis
17536        // scalar defers to the substrate catch-all / default port),
17537        // and a `Caixa { entrada: None, .. }` must NOT push the label
17538        // (the "author omitted the slot entirely" partition). The pair
17539        // jointly pins the accessor + declared-slot enumerator
17540        // composition: any future silent detour that had the accessor
17541        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
17542        // `.filter(|e| !e.paths.is_empty())` projection) would silently
17543        // absorb the "declared but empty-paths" arm at the accessor
17544        // boundary and the
17545        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17546        // coherence gate would silently accept a struct-literal
17547        // `Caixa` carrying the drift.
17548        //
17549        // Peer of the sibling
17550        // `declared_servico_slots_limits_arm_routes_through_accessor`
17551        // (b2bd9d7),
17552        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17553        // (35d8b52),
17554        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17555        // (5d23d29), and
17556        // `declared_mesh_slots_placement_arm_routes_through_accessor`
17557        // (4fb8074) composition pins on the sibling `:limits` /
17558        // `:behavior` / `:politicas` / `:placement` outer-
17559        // `Option<&Composite>` arms — same "the enumerator gate must
17560        // route through the substrate-primitive typed dispatch"
17561        // discipline extended onto the third and final M3 mesh-slot
17562        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
17563        // carries the routing invariant on every M3 mesh-slot arm.
17564        use crate::aplicacao::Entrada;
17565        let c = caixa_aplicacao_with_entrada(Some(Entrada {
17566            host: "checkout.quero.cloud".into(),
17567            para: "gateway".into(),
17568            paths: Vec::new(),
17569            port: crate::DEFAULT_SERVICO_PORT,
17570        }));
17571        let slots = c.declared_mesh_slots();
17572        assert!(
17573            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17574            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
17575             `:entrada` is Some (even for empty-paths / default-port) \
17576             — the accessor and the enumerator gate must route through \
17577             the same substrate-primitive typed dispatch on the outer \
17578             :entrada presence bit (got slots={slots:?})",
17579        );
17580        let c = caixa_aplicacao_with_entrada(None);
17581        let slots = c.declared_mesh_slots();
17582        assert!(
17583            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17584            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
17585             when `:entrada` is None — the author-omitted arm must \
17586             route through the accessor's None-return unchanged (got \
17587             slots={slots:?})",
17588        );
17589    }
17590
17591    #[test]
17592    fn aplicacao_view_entrada_arm_folds_through_accessor() {
17593        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
17594        // Aplicacao-composition seed must fold through
17595        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
17596        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
17597        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
17598        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
17599        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
17600        // equals the outer composite's authored value (the fold must
17601        // project the authored composite verbatim), and a `Caixa {
17602        // entrada: None, kind: Aplicacao, .. }` must surface an
17603        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
17604        // "author omitted the slot entirely" arm folds through the
17605        // accessor's `Option::cloned` onto the same `None` presence
17606        // bit — unlike the peer `:politicas` / `:placement` arms
17607        // `:entrada` has no cluster-default fold, the omitted arm
17608        // stays omitted). The pair jointly pins the accessor +
17609        // Aplicacao-composition seed composition: any future silent
17610        // detour that had the accessor divert the raw slot away from
17611        // the seed's fold (an operator-resolved overlay's forward arm
17612        // silently differing from the raw slot's forward arm) would
17613        // silently split the build-time gateway-artifact emission gate
17614        // from the caixa-mesh renderer's Aplicacao-view input at the
17615        // composition boundary.
17616        use crate::aplicacao::Entrada;
17617        let authored = Entrada {
17618            host: "api.pleme.io".into(),
17619            para: "public-api".into(),
17620            paths: vec!["/v1".into()],
17621            port: 8080,
17622        };
17623        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
17624        let view = c.aplicacao_view().unwrap();
17625        assert_eq!(
17626            view.entrada(),
17627            Some(&authored),
17628            "Caixa::aplicacao_view must fold the authored :entrada \
17629             composite through the accessor verbatim onto the \
17630             projected AplicacaoSpec — a future silent detour at the \
17631             seed's fold arm would surface here as a projected- \
17632             composite drift (got {:?})",
17633            view.entrada(),
17634        );
17635        let c = caixa_aplicacao_with_entrada(None);
17636        let view = c.aplicacao_view().unwrap();
17637        assert!(
17638            view.entrada().is_none(),
17639            "Caixa::aplicacao_view must fold None through the \
17640             accessor's Option::cloned onto None — the author- \
17641             omitted arm must route through the accessor's None-return \
17642             unchanged (got {:?})",
17643            view.entrada(),
17644        );
17645    }
17646
17647    #[test]
17648    fn entrada_projects_option_ref_by_borrow() {
17649        // The by-borrow pin: [`Caixa::entrada`] returns
17650        // `Option<&Entrada>` by borrow — the returned reference
17651        // borrows the underlying `Option<Entrada>` storage of the
17652        // `:entrada` slot and the accessor must not clone the backing
17653        // composite on every call. Peer of the sibling
17654        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17655        // `behavior_projects_option_ref_by_borrow` (35d8b52),
17656        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
17657        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
17658        // borrow pins on the outer top-level [`Caixa`]
17659        // `Option<&Composite>`-return sub-family — extended here to
17660        // the fifth and final axis of the same sub-family, closing
17661        // the discipline: the accessor's returned reference must
17662        // borrow from `&self` (the returned reference's lifetime is
17663        // tied to `&self`), and calling the accessor twice on the
17664        // same [`Caixa`] must yield references that are pointer-equal
17665        // (the underlying byte-buffer is the storage `Entrada`'s
17666        // allocation, not a fresh copy) as well as value-equal
17667        // (idempotent, no side effects on `&self`).
17668        //
17669        // Pins against a future silent detour that returned an owned
17670        // `Entrada` (which would type-check via the `Clone` impl but
17671        // silently clone on every call), a `&Entrada` panic-return on
17672        // the `None` arm (which would collapse the load-bearing
17673        // `Option` presence-bit into a runtime panic), or a one-arm-
17674        // only accessor that returned a saturating composite on some
17675        // sentinel input.
17676        use crate::aplicacao::Entrada;
17677        for entrada in [
17678            Some(Entrada {
17679                host: "checkout.quero.cloud".into(),
17680                para: "gateway".into(),
17681                paths: Vec::new(),
17682                port: crate::DEFAULT_SERVICO_PORT,
17683            }),
17684            Some(Entrada {
17685                host: "api.pleme.io".into(),
17686                para: "public-api".into(),
17687                paths: vec!["/v1".into(), "/v2".into()],
17688                port: 8080,
17689            }),
17690        ] {
17691            let c = caixa_aplicacao_with_entrada(entrada.clone());
17692            let first = c.entrada().unwrap();
17693            let second = c.entrada().unwrap();
17694            assert_eq!(
17695                first, second,
17696                "Caixa::entrada must be idempotent — two successive \
17697                 calls on the same &self must return the same &Entrada",
17698            );
17699            assert!(
17700                std::ptr::eq(first, second),
17701                "Caixa::entrada must borrow the underlying \
17702                 Option<Entrada> storage — two successive calls must \
17703                 return references with the same backing pointer (a \
17704                 fresh Entrada clone would change the pointer on every \
17705                 call)",
17706            );
17707            assert_eq!(
17708                Some(first),
17709                entrada.as_ref(),
17710                "Caixa::entrada must return :entrada verbatim by \
17711                 borrow — got {first:?}, expected {:?}",
17712                entrada.as_ref(),
17713            );
17714        }
17715        let c = caixa_aplicacao_with_entrada(None);
17716        assert!(
17717            c.entrada().is_none(),
17718            "Caixa::entrada must return None when :entrada is absent \
17719             — the author-omitted arm must project through the \
17720             accessor's Option::None unchanged",
17721        );
17722    }
17723
17724    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
17725
17726    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
17727        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17728        c.estrategia = estrategia;
17729        c
17730    }
17731
17732    #[test]
17733    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
17734        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
17735        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
17736        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
17737        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
17738        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
17739        // over the same discriminant the raw `self.estrategia` field
17740        // access carries, byte-equal across every representative fixture
17741        // in the accept-set — the author-omitted `None` shape (the
17742        // "defer to [`RestartStrategy::default`] through the
17743        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
17744        // every non-`Supervisor`-kind `defcaixa` carries by
17745        // `#[serde(default)]`), and each of the four closed-set variants
17746        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
17747        // / [`RestartStrategy::RestForOne`] /
17748        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
17749        // partitions on.
17750        //
17751        // Pins against a future silent detour that re-derived the
17752        // strategy from a peer axis (an accidental fallback to
17753        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
17754        // collapse that read the outer `:children` list-length axis into
17755        // the strategy discriminator at the accessor boundary), a
17756        // stale-derive detour that substituted [`RestartStrategy::default`]
17757        // when the outer `Option` held `None` (which would silently
17758        // collapse the load-bearing "author explicitly declared
17759        // `:estrategia OneForOne`" vs "author omitted the slot and
17760        // inherited the default" partition the [`Self::declared_supervisor_slots`]
17761        // presence-probe reads — the enumerator gate would still push
17762        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
17763        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17764        // kind-coherence gate's traversal head from the
17765        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
17766        // composition head), a reference to an operator-resolved overlay
17767        // (the future per-cluster `:estrategia-overrides` slot — its
17768        // resolution must land at exactly this accessor body, not
17769        // silently divert the raw slot away from a second consumer), or
17770        // an axis-remap projection (a future detour that mapped
17771        // `OneForAll` through the accessor onto `OneForOne` would
17772        // silently split every downstream sibling-restart-strategy
17773        // consumer's per-arm fan-out).
17774        //
17775        // First outer top-level [`Caixa`] `Option<Copy>`-return
17776        // supervisor-tree-slot flat-spread accessor pin on the substrate
17777        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
17778        // projection pattern the sibling per-`Caixa` `:max-restarts` /
17779        // `:restart-window` future outer-scalar pins fold on. Peer of
17780        // the inner-altitude
17781        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17782        // (eafb619) pin on the post-composition [`SupervisorSpec`]
17783        // altitude — same "the substrate-primitive accessor must byte-
17784        // equal the raw field access verbatim across every author-
17785        // declared value" discipline extended onto the pre-composition
17786        // outer author-surface [`Caixa`] altitude. Peer of the closed
17787        // outer-`Caixa` `Option<&Composite>` composite-reference family
17788        // the sibling `limits` / `behavior` / `politicas` / `placement` /
17789        // `entrada`
17790        // `..._returns_..._option_ref_verbatim_across_permutations` pins
17791        // already carry on the outer `Option<&Composite>` altitude.
17792        use crate::supervisor::RestartStrategy;
17793        let fixtures: Vec<Option<RestartStrategy>> = vec![
17794            None,
17795            Some(RestartStrategy::OneForOne),
17796            Some(RestartStrategy::OneForAll),
17797            Some(RestartStrategy::RestForOne),
17798            Some(RestartStrategy::SimpleOneForOne),
17799        ];
17800        for estrategia in fixtures {
17801            let c = caixa_with_estrategia(estrategia);
17802            assert_eq!(
17803                c.estrategia(),
17804                estrategia,
17805                "Caixa::estrategia must return :estrategia verbatim (got \
17806                 {:?}, expected {:?})",
17807                c.estrategia(),
17808                estrategia,
17809            );
17810            assert_eq!(
17811                c.estrategia(),
17812                c.estrategia,
17813                "Caixa::estrategia accessor and self.estrategia field \
17814                 access must byte-equal — the accessor is the substrate-\
17815                 primitive typed dispatch every downstream supervisor-\
17816                 tree flat-spread consumer must route through, and a \
17817                 discriminant split would silently break every consumer \
17818                 that relied on the accessor sharing the field's own \
17819                 Option<Copy> shape",
17820            );
17821            assert_eq!(
17822                c.estrategia().is_some(),
17823                c.estrategia.is_some(),
17824                "Caixa::estrategia().is_some() must byte-equal \
17825                 self.estrategia.is_some() — a presence-bit drift would \
17826                 silently split the paired Caixa::declared_supervisor_slots \
17827                 presence-probe arm from the Caixa::supervisor_view \
17828                 unwrap_or_default() fold's composition input",
17829            );
17830        }
17831    }
17832
17833    #[test]
17834    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
17835        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17836        // `:estrategia` presence-probe arm must key off
17837        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
17838        // field-probe. Structurally: every `Caixa { estrategia:
17839        // Some(RestartStrategy::_), .. }` variant must push
17840        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
17841        // (the presence bit is `Some` for every closed-set variant, so
17842        // the M2 supervisor-tree kind-coherence gate must surface the
17843        // slot as "declared" regardless of which variant the author
17844        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
17845        // the label (the "author omitted the slot entirely, deferring
17846        // to [`RestartStrategy::default`] through the supervisor_view
17847        // fold" partition). The pair jointly pins the accessor +
17848        // declared-slot enumerator composition: any future silent detour
17849        // that had the accessor collapse `Some(RestartStrategy::default())`
17850        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
17851        // projection) would silently absorb the "declared but default-
17852        // valued" arm at the accessor boundary and the
17853        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
17854        // coherence gate would silently accept a struct-literal `Caixa`
17855        // carrying the drift.
17856        //
17857        // Peer of the sibling per-`Caixa`
17858        // `declared_servico_slots_limits_arm_routes_through_accessor`
17859        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
17860        // `Option<&LimitsSpec>` composition axis — same "the enumerator
17861        // gate must route through the substrate-primitive typed
17862        // dispatch" discipline extended onto the flat-spread M2
17863        // supervisor-tree `Option<RestartStrategy>`-composition surface,
17864        // opening the outer-`Caixa` supervisor-tree-slot arm of the
17865        // composition-pin family.
17866        use crate::supervisor::RestartStrategy;
17867        for estrategia in [
17868            RestartStrategy::OneForOne,
17869            RestartStrategy::OneForAll,
17870            RestartStrategy::RestForOne,
17871            RestartStrategy::SimpleOneForOne,
17872        ] {
17873            let c = caixa_with_estrategia(Some(estrategia));
17874            let slots = c.declared_supervisor_slots();
17875            assert!(
17876                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17877                "declared_supervisor_slots must push \
17878                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
17879                 Some({estrategia:?}) — the accessor and the enumerator \
17880                 gate must route through the same substrate-primitive \
17881                 typed dispatch on the outer :estrategia presence bit \
17882                 (got slots={slots:?})",
17883            );
17884        }
17885        let c = caixa_with_estrategia(None);
17886        let slots = c.declared_supervisor_slots();
17887        assert!(
17888            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17889            "declared_supervisor_slots must NOT push \
17890             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
17891             — the author-omitted arm must route through the accessor's \
17892             None-return unchanged (got slots={slots:?})",
17893        );
17894    }
17895
17896    #[test]
17897    fn supervisor_view_estrategia_arm_routes_through_accessor() {
17898        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
17899        // [`SupervisorSpec`] construction arm must key off
17900        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
17901        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
17902        // for every `:kind Supervisor` `Caixa` carrying an author-
17903        // declared `Some(RestartStrategy::_)` variant, the composed
17904        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
17905        // outer accessor's declared variant unchanged; and for a
17906        // `:kind Supervisor` `Caixa` carrying `None`, the composed
17907        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
17908        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
17909        // arm the flat-spread `unwrap_or_default()` fold projects to on
17910        // the author-omitted arm — this is the *composition* between the
17911        // outer `Option<RestartStrategy>` accessor's presence-bit
17912        // surface and the inner post-composition non-`Option`
17913        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
17914        // pins the accessor + supervisor_view composition: any future
17915        // silent detour that had the accessor promote `None` to
17916        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
17917        // projection) would silently collapse the two arms into one at
17918        // the accessor boundary and the [`Self::declared_supervisor_slots`]
17919        // presence probe would silently drift from the composition site.
17920        //
17921        // Peer of the sibling M2 supervisor-slot post-composition
17922        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
17923        // pin on the [`SupervisorSpec::validate`] altitude — this pin
17924        // extends that inner-altitude accessor-routing discipline onto
17925        // the pre-composition outer author-surface [`Caixa`] altitude,
17926        // pinning the composition edge between the flat-spread outer
17927        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
17928        // `RestartStrategy` axes.
17929        use crate::CaixaKind;
17930        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17931        for estrategia in [
17932            RestartStrategy::OneForOne,
17933            RestartStrategy::OneForAll,
17934            RestartStrategy::RestForOne,
17935            RestartStrategy::SimpleOneForOne,
17936        ] {
17937            let mut c = caixa_with_estrategia(Some(estrategia));
17938            c.kind = CaixaKind::Supervisor;
17939            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
17940            // shape partition through the [`gen_platform::IsVariant`]
17941            // derive-generated
17942            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
17943            // than the raw `matches!(estrategia, RestartStrategy::
17944            // SimpleOneForOne)` open-coded pattern-match — same closed-
17945            // set-typed-enum arm-discriminator dispatch discipline the
17946            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
17947            // convergence (915a934) extended onto its two paired positive
17948            // / negated `matches!` sites and the peer
17949            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
17950            // predicate convergence (766ec63) extended onto the M3 mesh-
17951            // slot per-`:placement` distribution-strategy discriminator
17952            // axis. See the sibling `supervisor::tests::
17953            // round_trip_all_strategies` and
17954            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17955            // fixtures — the three sites (all test-only,
17956            // acknowledged in 915a934's Prior-commits footnote as the
17957            // outstanding follow-up) now consult one typed dispatch on
17958            // the substrate primitive.
17959            c.children = if estrategia.is_simple_one_for_one() {
17960                Vec::new()
17961            } else {
17962                vec![ChildSpec {
17963                    caixa: "worker".into(),
17964                    versao: "^0.1".into(),
17965                    restart: RestartPolicy::Permanent,
17966                }]
17967            };
17968            let view = c.supervisor_view().expect(
17969                "supervisor_view must materialize a SupervisorSpec for a \
17970                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
17971            );
17972            assert_eq!(
17973                view.estrategia(),
17974                c.estrategia().unwrap(),
17975                "supervisor_view must carry the outer Caixa::estrategia() \
17976                 declared variant onto the composed SupervisorSpec.estrategia \
17977                 field verbatim on the Some arm (got {:?}, expected {:?})",
17978                view.estrategia(),
17979                c.estrategia().unwrap(),
17980            );
17981        }
17982        // The author-omitted arm: outer `None` → composed
17983        // `RestartStrategy::default()` through the flat-spread
17984        // `unwrap_or_default()` fold.
17985        let mut c = caixa_with_estrategia(None);
17986        c.kind = CaixaKind::Supervisor;
17987        // Populate children so the sibling supervisor slots are coherent
17988        // for the [`Self::supervisor_view`] projection; the `:estrategia`
17989        // arm still defers to [`RestartStrategy::default`] on the
17990        // author-omitted arm even when the sibling slots carry values.
17991        c.children = vec![ChildSpec {
17992            caixa: "worker".into(),
17993            versao: "^0.1".into(),
17994            restart: RestartPolicy::Permanent,
17995        }];
17996        let view = c.supervisor_view().expect(
17997            "supervisor_view must materialize a SupervisorSpec for a \
17998             :kind Supervisor Caixa carrying a None `:estrategia` slot",
17999        );
18000        assert_eq!(
18001            view.estrategia(),
18002            RestartStrategy::default(),
18003            "supervisor_view must project the outer Caixa::estrategia() \
18004             None arm onto RestartStrategy::default() through the flat-\
18005             spread unwrap_or_default() fold (got {:?}, expected {:?})",
18006            view.estrategia(),
18007            RestartStrategy::default(),
18008        );
18009        assert!(
18010            c.estrategia().is_none(),
18011            "Caixa::estrategia() must remain None on the author-omitted \
18012             arm — the supervisor_view fold must not mutate the outer \
18013             flat-spread presence bit",
18014        );
18015    }
18016
18017    #[test]
18018    fn estrategia_projects_option_by_copy() {
18019        // The by-`Copy` pin: [`Caixa::estrategia`] returns
18020        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
18021        // the accessor does not borrow `&self` past the call (no
18022        // lifetime on the return type), and calling the accessor twice
18023        // on the same [`Caixa`] must yield discriminant-equal values
18024        // (idempotent, no side effects on `&self`). Peer of the sibling
18025        // outer-`Caixa` `Option<&Composite>` by-borrow
18026        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
18027        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
18028        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
18029        // `placement_projects_option_ref_by_borrow` (4fb8074) /
18030        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
18031        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
18032        // extended here to the outer-`Caixa` `Option<Copy>`-return
18033        // flat-spread axis. The `Copy` discipline replaces the pointer-
18034        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
18035        // `Copy` discriminant is definitionally the same discriminant, so
18036        // the axis reduces to discriminant equality).
18037        //
18038        // Pins against a future silent detour that returned a fresh
18039        // `Option<&RestartStrategy>` (which would type-check but silently
18040        // introduce a borrow of `&self` past the call, collapsing the
18041        // load-bearing "no lifetime on the return type" `Copy` projection
18042        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
18043        // read side effect that flipped the outer discriminant on
18044        // successive calls, or an axis-remap projection that returned a
18045        // different variant than the field storage.
18046        use crate::supervisor::RestartStrategy;
18047        for estrategia in [
18048            Some(RestartStrategy::OneForOne),
18049            Some(RestartStrategy::OneForAll),
18050            Some(RestartStrategy::RestForOne),
18051            Some(RestartStrategy::SimpleOneForOne),
18052        ] {
18053            let c = caixa_with_estrategia(estrategia);
18054            let first = c.estrategia();
18055            let second = c.estrategia();
18056            assert_eq!(
18057                first, second,
18058                "Caixa::estrategia must be idempotent — two successive \
18059                 calls on the same &self must return the same \
18060                 Option<RestartStrategy>",
18061            );
18062            assert_eq!(
18063                first, estrategia,
18064                "Caixa::estrategia must return :estrategia verbatim by \
18065                 Copy — got {first:?}, expected {estrategia:?}",
18066            );
18067        }
18068        let c = caixa_with_estrategia(None);
18069        assert!(
18070            c.estrategia().is_none(),
18071            "Caixa::estrategia must return None when :estrategia is \
18072             absent — the author-omitted arm must project through the \
18073             accessor's Option::None unchanged",
18074        );
18075    }
18076
18077    // ── Caixa::max_restarts / Caixa::restart_window —
18078    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
18079    //    (Option<u32> / Option<&str>) folding on the ed04d3c
18080    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
18081
18082    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
18083        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18084        c.max_restarts = max_restarts;
18085        c
18086    }
18087
18088    fn caixa_supervisor_with_max_restarts_and_window(
18089        max_restarts: Option<u32>,
18090        restart_window: Option<&str>,
18091    ) -> Caixa {
18092        use crate::CaixaKind;
18093        use crate::supervisor::{ChildSpec, RestartPolicy};
18094        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
18095        c.kind = CaixaKind::Supervisor;
18096        c.max_restarts = max_restarts;
18097        c.restart_window = restart_window.map(str::to_string);
18098        c.children = vec![ChildSpec {
18099            caixa: "worker".into(),
18100            versao: "^0.1".into(),
18101            restart: RestartPolicy::Permanent,
18102        }];
18103        c
18104    }
18105
18106    #[test]
18107    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
18108        // Value-shape pin: [`Caixa::max_restarts`] returns the
18109        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
18110        // from the typed slot's own storage, byte-equal across the
18111        // author-omitted `None` arm (the "defer to the
18112        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
18113        // `{intensity, 5, 60}` default" partition every
18114        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
18115        // and each of the representative fixtures in the accept-set —
18116        // `0` (the zero-floor arm the peer
18117        // [`crate::supervisor::SupervisorSpec::validate`]
18118        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
18119        // the post-composition altitude — the accessor must ship the
18120        // raw slot verbatim so struct-literal fixtures continue to
18121        // expose the zero at the accessor boundary), the OTP-canonical
18122        // `5` default (`{intensity, 5, 60}` worker-supervisor from
18123        // Learn You Some Erlang), `1000` (the
18124        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
18125        // upper-bound gate accepts on the boundary), `u32::MAX` (a
18126        // past-the-cap sentinel that the substrate-primitive accessor
18127        // must still ship verbatim). Second outer top-level
18128        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
18129        // pin — folds on the sibling
18130        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
18131        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
18132        // onto the sibling `Option<u32>` restart-budget-count arm.
18133        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
18134        for max_restarts in fixtures {
18135            let c = caixa_with_max_restarts(max_restarts);
18136            assert_eq!(
18137                c.max_restarts(),
18138                max_restarts,
18139                "Caixa::max_restarts must return :max-restarts verbatim \
18140                 (got {:?}, expected {max_restarts:?})",
18141                c.max_restarts(),
18142            );
18143            assert_eq!(
18144                c.max_restarts(),
18145                c.max_restarts,
18146                "Caixa::max_restarts accessor and self.max_restarts \
18147                 field access must byte-equal — a presence-bit or count \
18148                 drift would silently split the paired \
18149                 Caixa::declared_supervisor_slots presence-probe arm \
18150                 from the Caixa::supervisor_view unwrap_or(5) fold's \
18151                 composition input",
18152            );
18153        }
18154    }
18155
18156    #[test]
18157    fn max_restarts_projects_option_by_copy() {
18158        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
18159        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
18160        // borrow `&self` past the call (no lifetime on the return type),
18161        // and calling the accessor twice on the same [`Caixa`] must
18162        // yield equal values (idempotent, no side effects). Peer of the
18163        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
18164        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
18165        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
18166            let c = caixa_with_max_restarts(max_restarts);
18167            let first = c.max_restarts();
18168            let second = c.max_restarts();
18169            assert_eq!(
18170                first, second,
18171                "Caixa::max_restarts must be idempotent — two successive \
18172                 calls on the same &self must return the same Option<u32>",
18173            );
18174            assert_eq!(
18175                first, max_restarts,
18176                "Caixa::max_restarts must return :max-restarts verbatim \
18177                 by Copy — got {first:?}, expected {max_restarts:?}",
18178            );
18179        }
18180    }
18181
18182    #[test]
18183    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
18184        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18185        // `:max-restarts` presence-probe arm must key off
18186        // [`Caixa::max_restarts`], not the raw
18187        // `self.max_restarts.is_some()` field-probe. Structurally: every
18188        // `Caixa { max_restarts: Some(_), .. }` variant must push
18189        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
18190        // list (the presence bit is `Some` for every representative
18191        // count, so the M2 kind-coherence gate must surface the slot as
18192        // "declared"), and a `Caixa { max_restarts: None, .. }` must
18193        // NOT push the label. Peer of the sibling
18194        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
18195        // (ed04d3c) composition pin — same routing-through-accessor
18196        // discipline extended onto the sibling flat-spread `Option<u32>`
18197        // arm.
18198        for max_restarts in [0u32, 5, 1000, u32::MAX] {
18199            let c = caixa_with_max_restarts(Some(max_restarts));
18200            let slots = c.declared_supervisor_slots();
18201            assert!(
18202                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
18203                "declared_supervisor_slots must push \
18204                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
18205                 is Some({max_restarts}) — the accessor and the \
18206                 enumerator gate must route through the same \
18207                 substrate-primitive typed dispatch on the outer \
18208                 :max-restarts presence bit (got slots={slots:?})",
18209            );
18210        }
18211        let c = caixa_with_max_restarts(None);
18212        let slots = c.declared_supervisor_slots();
18213        assert!(
18214            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
18215            "declared_supervisor_slots must NOT push \
18216             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
18217             None — the author-omitted arm must route through the \
18218             accessor's None-return unchanged (got slots={slots:?})",
18219        );
18220    }
18221
18222    #[test]
18223    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
18224        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
18225        // [`SupervisorSpec`] construction arm must key off
18226        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
18227        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
18228        // every `:kind Supervisor` `Caixa` carrying an author-declared
18229        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
18230        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
18231        // carrying `None`, the composed [`SupervisorSpec`]'s
18232        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
18233        // of the sibling
18234        // `supervisor_view_estrategia_arm_routes_through_accessor`
18235        // (ed04d3c) composition pin.
18236        for max_restarts in [1u32, 5, 1000] {
18237            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
18238            let view = c.supervisor_view().expect(
18239                "supervisor_view must materialize a SupervisorSpec for a \
18240                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
18241            );
18242            assert_eq!(
18243                view.max_restarts(),
18244                max_restarts,
18245                "supervisor_view must carry the outer \
18246                 Caixa::max_restarts() Some arm onto the composed \
18247                 SupervisorSpec.max_restarts field verbatim (got {}, \
18248                 expected {max_restarts})",
18249                view.max_restarts(),
18250            );
18251        }
18252        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18253        let view = c.supervisor_view().expect(
18254            "supervisor_view must materialize a SupervisorSpec for a \
18255             :kind Supervisor Caixa carrying a None :max-restarts",
18256        );
18257        assert_eq!(
18258            view.max_restarts(),
18259            5,
18260            "supervisor_view must project the outer \
18261             Caixa::max_restarts() None arm onto the OTP-canonical \
18262             {{intensity, 5, 60}} default (5) through the flat-spread \
18263             unwrap_or(5) fold (got {})",
18264            view.max_restarts(),
18265        );
18266        assert!(
18267            c.max_restarts().is_none(),
18268            "Caixa::max_restarts() must remain None on the author-\
18269             omitted arm — the supervisor_view fold must not mutate \
18270             the outer flat-spread presence bit",
18271        );
18272    }
18273
18274    #[test]
18275    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
18276        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
18277        // `:estrategia` arm must degrade onto the substrate-canonical
18278        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
18279        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
18280        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
18281        // worker-supervisor default — rather than the transitively-
18282        // derived [`crate::supervisor::RestartStrategy::default`] route
18283        // the prior `.unwrap_or_default()` fold reached for. Prior to the
18284        // lift the composition site carried `.unwrap_or_default()` with
18285        // no compile-time link back to the shared OTP-canonical strategy
18286        // default that the paired [`crate::supervisor::Default for
18287        // RestartStrategy`] impl and the [`crate::supervisor::Default for
18288        // SupervisorSpec`] impl's struct-literal `estrategia` field both
18289        // (now) route through the same lifted constant — so a future
18290        // rebrand of the OTP-canonical strategy default (an OTP
18291        // `rest_for_one` widening once the substrate discovers startup-
18292        // order-coupled child cohorts as the more common worker-
18293        // supervisor shape, a per-cluster overlay the operator pins
18294        // through the MESH-COMPOSITION §III.2 supervision-canary
18295        // `:estrategia-overrides` roadmap slot) would have had to migrate
18296        // the paired `MaxIntensity` + `Period` halves through the lifted
18297        // constants and the `one_for_one` half through a
18298        // `RestartStrategy::default()` route in lockstep or a
18299        // `:kind Supervisor` caixa carrying an author-omitted
18300        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
18301        // whose `estrategia` disagreed with the paired
18302        // `SupervisorSpec::default()` view. Byte-parity against the
18303        // lifted constant closes the split. Peer of the sibling
18304        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
18305        // composition pin on the paired `MaxIntensity` half + the
18306        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
18307        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
18308        // pins on the sibling entry points onto the shared substrate
18309        // constant.
18310        use crate::CaixaKind;
18311        use crate::supervisor::{ChildSpec, RestartPolicy};
18312        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
18313        c.kind = CaixaKind::Supervisor;
18314        c.estrategia = None;
18315        c.children = vec![ChildSpec {
18316            caixa: "worker".into(),
18317            versao: "^0.1".into(),
18318            restart: RestartPolicy::Permanent,
18319        }];
18320        let view = c.supervisor_view().expect(
18321            "supervisor_view must materialize a SupervisorSpec for a \
18322             :kind Supervisor Caixa carrying a None :estrategia",
18323        );
18324        assert_eq!(
18325            view.estrategia(),
18326            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
18327            "supervisor_view must degrade the outer \
18328             Caixa::estrategia() None arm onto the lifted \
18329             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
18330             expected {:?})",
18331            view.estrategia(),
18332            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
18333        );
18334    }
18335
18336    #[test]
18337    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
18338        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
18339        // `:max-restarts` arm must degrade onto the substrate-canonical
18340        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
18341        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
18342        // `MaxIntensity` default — rather than a raw `5` literal. Prior
18343        // to the lift the composition site carried an inline
18344        // `.unwrap_or(5)` with no compile-time link back to the shared
18345        // OTP-canonical default that the serde-side
18346        // `#[serde(default = "default_max_restarts")]` wire-format arm
18347        // and the [`Default for crate::supervisor::SupervisorSpec`]
18348        // struct-literal default arm both key off — so a future rebrand
18349        // of the OTP-canonical default (Elixir's `Supervisor` `3`
18350        // default, a per-cluster overlay the operator pins through the
18351        // MESH-COMPOSITION §III.2 supervision-canary
18352        // `:supervisor :max-restarts-overrides` roadmap slot) would
18353        // have had to be threaded through both the serde-side helper
18354        // and this view-construction arm in lockstep or a `:kind
18355        // Supervisor` caixa carrying `:max-restarts ()` would silently
18356        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
18357        // with the same fixture's serde-side `SupervisorSpec` view (an
18358        // author-omitted slot round-tripping through
18359        // `SupervisorSpec::default()` to the lifted constant, then
18360        // splitting to a stale literal past `supervisor_view`).
18361        // Byte-parity against the lifted constant closes the split.
18362        // Peer of the sibling
18363        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
18364        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
18365        // composition pins that close the same routing on the two
18366        // sibling entry points onto the shared substrate constant.
18367        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18368        let view = c.supervisor_view().expect(
18369            "supervisor_view must materialize a SupervisorSpec for a \
18370             :kind Supervisor Caixa carrying a None :max-restarts",
18371        );
18372        assert_eq!(
18373            view.max_restarts(),
18374            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18375            "supervisor_view must degrade the outer \
18376             Caixa::max_restarts() None arm onto the lifted \
18377             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
18378             expected {})",
18379            view.max_restarts(),
18380            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18381        );
18382    }
18383
18384    #[test]
18385    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
18386        // Value-shape pin: [`Caixa::restart_window`] returns the
18387        // `:restart-window` typed `Option<String>` verbatim as an
18388        // `Option<&str>`, borrowed from the typed slot's own storage,
18389        // byte-equal across the author-omitted `None` arm and each of
18390        // the representative fixtures in the accept-set — the canonical
18391        // `"60s"` from `{intensity, 5, 60}`, the sibling
18392        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
18393        // / `"0s"`) the shared codec's positive-set sweep pin covers,
18394        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
18395        // seconds drift the sibling [`Self::validate_restart_window`]
18396        // gate refuses; the accessor must ship the raw slot verbatim
18397        // so struct-literal fixtures continue to expose the drift at
18398        // the accessor boundary). Third outer top-level [`Caixa`]
18399        // supervisor-tree flat-spread pin — extends the sub-family onto
18400        // the sibling `Option<&str>` raw-duration-string arm.
18401        for window in [
18402            None,
18403            Some("60s"),
18404            Some("5m"),
18405            Some("1h"),
18406            Some("500ms"),
18407            Some("1.5s"),
18408            Some(""),
18409        ] {
18410            let c = caixa_with_restart_window(window);
18411            assert_eq!(
18412                c.restart_window(),
18413                window,
18414                "Caixa::restart_window must return :restart-window \
18415                 verbatim as Option<&str> (got {:?}, expected {window:?})",
18416                c.restart_window(),
18417            );
18418            assert_eq!(
18419                c.restart_window(),
18420                c.restart_window.as_deref(),
18421                "Caixa::restart_window accessor and \
18422                 self.restart_window.as_deref() field access must \
18423                 byte-equal — a byte-level drift would silently split \
18424                 the paired Caixa::declared_supervisor_slots \
18425                 presence-probe arm from the \
18426                 Caixa::validate_restart_window shared-codec gate and \
18427                 the Caixa::supervisor_view soft-swallowing fold",
18428            );
18429        }
18430    }
18431
18432    #[test]
18433    fn restart_window_projects_slice_by_borrow() {
18434        // The by-borrow pin: [`Caixa::restart_window`] returns
18435        // `Option<&str>` by borrow — the returned string slice borrows
18436        // the underlying `Option<String>` storage of the `:restart-window`
18437        // slot and the accessor must not clone on every call. Peer of
18438        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
18439        // by-borrow pins on the universal-axis scalar family
18440        // (`licenca_projects_option_ref_by_borrow` /
18441        // `descricao_projects_option_ref_by_borrow` and siblings) —
18442        // extended onto the M2 supervisor-tree flat-spread
18443        // `Option<&str>` raw-duration-string axis.
18444        for window in [None, Some("60s"), Some("5m"), Some("")] {
18445            let c = caixa_with_restart_window(window);
18446            let first = c.restart_window();
18447            let second = c.restart_window();
18448            assert_eq!(
18449                first, second,
18450                "Caixa::restart_window must be idempotent — two \
18451                 successive calls on the same &self must return the \
18452                 same Option<&str>",
18453            );
18454            if let (Some(a), Some(b)) = (first, second) {
18455                assert_eq!(
18456                    a.as_ptr(),
18457                    b.as_ptr(),
18458                    "Caixa::restart_window must borrow the underlying \
18459                     String storage — two successive Some-arm calls must \
18460                     return slices with the same backing pointer (a fresh \
18461                     String clone would change the pointer on every call)",
18462                );
18463            }
18464            assert_eq!(
18465                first, window,
18466                "Caixa::restart_window must return :restart-window \
18467                 verbatim by borrow — got {first:?}, expected {window:?}",
18468            );
18469        }
18470    }
18471
18472    #[test]
18473    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
18474        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18475        // `:restart-window` presence-probe arm must key off
18476        // [`Caixa::restart_window`], not the raw
18477        // `self.restart_window.is_some()` field-probe. Structurally:
18478        // every `Caixa { restart_window: Some(_), .. }` must push
18479        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
18480        // list, and a `Caixa { restart_window: None, .. }` must NOT
18481        // push the label. Peer of the sibling
18482        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
18483        // routing pin.
18484        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
18485            let c = caixa_with_restart_window(Some(window));
18486            let slots = c.declared_supervisor_slots();
18487            assert!(
18488                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18489                "declared_supervisor_slots must push \
18490                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
18491                 `:restart-window` is Some({window:?}) — the accessor \
18492                 and the enumerator gate must route through the same \
18493                 substrate-primitive typed dispatch on the outer \
18494                 :restart-window presence bit (got slots={slots:?})",
18495            );
18496        }
18497        let c = caixa_with_restart_window(None);
18498        let slots = c.declared_supervisor_slots();
18499        assert!(
18500            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18501            "declared_supervisor_slots must NOT push \
18502             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
18503             is None — the author-omitted arm must route through the \
18504             accessor's None-return unchanged (got slots={slots:?})",
18505        );
18506    }
18507
18508    #[test]
18509    fn validate_restart_window_arm_routes_through_accessor() {
18510        // Composition pin: [`Caixa::validate_restart_window`]'s
18511        // shared-codec fold arm must key off [`Caixa::restart_window`],
18512        // not the raw `self.restart_window.as_deref()` field-projection.
18513        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
18514        // express no reset" canonical shape); (2) a canonical `Some`
18515        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
18516        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
18517        // .. })` carrying the offending raw string verbatim. The three
18518        // arms jointly pin that the validator's raw-string binding is
18519        // the accessor's return, not a peer projection — any future
18520        // silent detour that had the accessor collapse `Some("")` to
18521        // `None` would silently absorb the empty-after-trim refusal
18522        // case at the accessor boundary.
18523        caixa_with_restart_window(None)
18524            .validate_restart_window()
18525            .expect("None :restart-window must validate through the accessor");
18526        caixa_with_restart_window(Some("60s"))
18527            .validate_restart_window()
18528            .expect("canonical :restart-window \"60s\" must validate through the accessor");
18529        let err = caixa_with_restart_window(Some("1.5s"))
18530            .validate_restart_window()
18531            .expect_err("fractional-seconds :restart-window must fail through the accessor");
18532        assert!(
18533            matches!(
18534                err,
18535                ManifestError::RestartWindowMalformed { ref restart_window, .. }
18536                    if restart_window == "1.5s"
18537            ),
18538            "validator must carry the offending raw string verbatim \
18539             from the accessor's borrowed &str (got {err:?})",
18540        );
18541    }
18542
18543    #[test]
18544    fn supervisor_view_restart_window_arm_routes_through_accessor() {
18545        // Composition pin: [`Caixa::supervisor_view`]'s
18546        // per-`:restart-window` [`SupervisorSpec`] construction arm
18547        // must key off [`Caixa::restart_window`]'s soft-swallowing
18548        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
18549        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
18550        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
18551        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
18552        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
18553        // (the shared codec's canonical parse); (3) codec-rejected
18554        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
18555        // (the soft-swallow preserving the view's best-effort shape).
18556        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18557        let view = c.supervisor_view().expect("Supervisor kind has a view");
18558        assert_eq!(
18559            view.restart_window(),
18560            None,
18561            "supervisor_view must project outer None :restart-window \
18562             onto None on the composed SupervisorSpec (never-reset \
18563             sentinel) through the accessor's None-return unchanged",
18564        );
18565
18566        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
18567        let view = c.supervisor_view().expect("Supervisor kind has a view");
18568        assert_eq!(
18569            view.restart_window(),
18570            Some(std::time::Duration::from_secs(60)),
18571            "supervisor_view must fold outer Some(\"60s\") through the \
18572             shared duration_codec into Duration::from_secs(60) on the \
18573             composed SupervisorSpec (accessor's Some(&str) → codec \
18574             parse → Some(Duration))",
18575        );
18576
18577        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
18578        let view = c.supervisor_view().expect("Supervisor kind has a view");
18579        assert_eq!(
18580            view.restart_window(),
18581            None,
18582            "supervisor_view must soft-swallow the shared-codec parse \
18583             failure to None (the view's best-effort shape the sibling \
18584             manifest-level validate_restart_window surfaces as \
18585             RestartWindowMalformed); the accessor's raw-string return \
18586             is the single input every downstream consumer keys off",
18587        );
18588    }
18589
18590    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
18591
18592    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
18593        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18594        c.upgrade_from = upgrade_from;
18595        c
18596    }
18597
18598    #[test]
18599    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
18600        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
18601        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
18602        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
18603        // typed `Vec<UpgradeFromEntry>` verbatim as a
18604        // `&[UpgradeFromEntry]` slice-view over the same backing
18605        // buffer the raw `self.upgrade_from.as_slice()` field access
18606        // borrows from, element-equal across every representative
18607        // fixture in the accept-set — `[]` (the "no hot-upgrade path
18608        // declared" arm every `defcaixa` without an `:upgrade-from`
18609        // block carries; `#[serde(default)]` folds an omitted slot
18610        // onto `Vec::new()`), a canonical single-entry `Restart`
18611        // fixture (the shape most Servicos carry — a single prior
18612        // version with the fallback strategy), a canonical multi-
18613        // entry list carrying every typed instruction variant
18614        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
18615        // `Restart`), and a past-the-guard sentinel — a duplicate-
18616        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
18617        // ([`crate::upgrade::validate_upgrade_from`] rejects through
18618        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
18619        // ship the raw slot verbatim so struct-literal fixtures
18620        // continue to expose the duplicate at the accessor boundary).
18621        //
18622        // Pins against a future silent detour that returned an owned
18623        // `Vec<UpgradeFromEntry>` (which would type-check but silently
18624        // clone on every accessor call, breaking the zero-cost
18625        // projection every peer sibling slice accessor carries), a
18626        // `[dup, dup] → [dup]` dedup collapse (which would silently
18627        // absorb the `DuplicateFrom` refusal case at the accessor
18628        // boundary and the [`crate::StandardLayout::verify`] cross-
18629        // entry gate would silently accept a struct-literal `Caixa`
18630        // carrying the drift), a reference to an operator-resolved
18631        // overlay (the future per-cluster `:upgrade-overrides` slot
18632        // — its resolution must land at exactly this accessor body,
18633        // not silently divert the raw slot away from a second
18634        // consumer), or an axis-shuffled projection (a future detour
18635        // that reordered entries through the accessor would silently
18636        // split the paired [`crate::StandardLayout::verify`] per-
18637        // `:upgrade-from` shape gate's traversal input from the peer
18638        // [`crate::render::servico_m2_overlay`] emitter's projection
18639        // input, since the operator's hot-upgrade dispatch matches
18640        // per-`:from` and axis reordering would silently split the
18641        // per-entry script-path existence probe's iteration order
18642        // from the M2 overlay emitter's serialized-entry order).
18643        //
18644        // First outer top-level [`Caixa`] `&[Composite]`-return
18645        // slice accessor pin on the substrate primitive for M2 / M3
18646        // typed-slot vec-carry axes — opens the outer-`Caixa`
18647        // `&[Composite]` composite-slice projection pattern the
18648        // sibling `:children` [`crate::supervisor::ChildSpec`] /
18649        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
18650        // [`crate::aplicacao::WitContract`] future outer-composite-
18651        // slice pins fold on. Peer of the closed outer-`Caixa`
18652        // scalar `Option<&Composite>` composite-reference family the
18653        // sibling `limits` / `behavior` / `politicas` / `placement`
18654        // / `entrada` `..._returns_..._option_ref_verbatim_across_
18655        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
18656        // the "byte-equal, borrow-shared" outer-accessor discipline
18657        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
18658        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18659        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
18660            vec![],
18661            vec![UpgradeFromEntry {
18662                from: "0.0.1".into(),
18663                instructions: vec![UpgradeInstruction::Restart],
18664            }],
18665            vec![
18666                UpgradeFromEntry {
18667                    from: "0.0.1".into(),
18668                    instructions: vec![
18669                        UpgradeInstruction::LoadModule {
18670                            module: "demo".into(),
18671                        },
18672                        UpgradeInstruction::SoftPurge {
18673                            module: "demo".into(),
18674                        },
18675                    ],
18676                },
18677                UpgradeFromEntry {
18678                    from: "0.0.2".into(),
18679                    instructions: vec![
18680                        UpgradeInstruction::StateChange {
18681                            script: "servicos/upgrade.lisp".into(),
18682                        },
18683                        UpgradeInstruction::Purge {
18684                            module: "demo".into(),
18685                        },
18686                        UpgradeInstruction::Restart,
18687                    ],
18688                },
18689            ],
18690            vec![
18691                UpgradeFromEntry {
18692                    from: "0.1.0".into(),
18693                    instructions: vec![UpgradeInstruction::Restart],
18694                },
18695                UpgradeFromEntry {
18696                    from: "0.1.0".into(),
18697                    instructions: vec![UpgradeInstruction::Restart],
18698                },
18699            ],
18700        ];
18701        for upgrade_from in fixtures {
18702            let c = caixa_with_upgrade_from(upgrade_from.clone());
18703            assert_eq!(
18704                c.upgrade_from(),
18705                upgrade_from.as_slice(),
18706                "Caixa::upgrade_from must return :upgrade-from \
18707                 verbatim (got {:?}, expected {upgrade_from:?})",
18708                c.upgrade_from(),
18709            );
18710            assert_eq!(
18711                c.upgrade_from(),
18712                c.upgrade_from.as_slice(),
18713                "Caixa::upgrade_from must element-equal the raw \
18714                 `self.upgrade_from.as_slice()` field access across \
18715                 every value in the Vec<UpgradeFromEntry> accept-set",
18716            );
18717            assert_eq!(
18718                c.upgrade_from().is_empty(),
18719                c.upgrade_from.is_empty(),
18720                "Caixa::upgrade_from().is_empty() must byte-equal \
18721                 self.upgrade_from.is_empty() — a presence-bit drift \
18722                 would silently split the paired \
18723                 Caixa::declared_servico_slots M2 declared-slot \
18724                 enumerator's presence probe from the peer \
18725                 crate::render::servico_m2_overlay M2 overlay \
18726                 emitter's presence gate",
18727            );
18728        }
18729    }
18730
18731    #[test]
18732    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
18733        // Composition pin: [`Caixa::declared_servico_slots`]'s
18734        // `:upgrade-from` presence-probe arm must key off
18735        // [`Caixa::upgrade_from`], not the raw
18736        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
18737        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
18738        // instructions: vec![Restart] }], .. }` must push
18739        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
18740        // (the presence bit is non-empty, so the M2 kind-coherence
18741        // gate must surface the slot as "declared"), and a `Caixa {
18742        // upgrade_from: vec![], .. }` must NOT push the label (the
18743        // "author omitted the slot entirely" arm — the empty-slice
18744        // partition the serde-default folds onto). The pair jointly
18745        // pins the accessor + declared-slot enumerator composition:
18746        // any future silent detour that had the accessor collapse
18747        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
18748        // is_empty())` projection) would silently absorb the
18749        // "declared but degenerate" arm at the accessor boundary and
18750        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
18751        // coherence gate would silently accept a struct-literal
18752        // `Caixa` carrying the drift.
18753        //
18754        // Peer of the sibling
18755        // `declared_servico_slots_limits_arm_routes_through_accessor`
18756        // (b2bd9d7) and
18757        // `declared_servico_slots_behavior_arm_routes_through_accessor`
18758        // (35d8b52) composition pins on the sibling `:limits` /
18759        // `:behavior` outer-`Option<&Composite>` arms — same "the
18760        // enumerator gate must route through the substrate-primitive
18761        // typed dispatch" discipline extended onto the third M2
18762        // Servico-runtime slot axis, closing the enumerator's routing
18763        // invariant on every M2 arm.
18764        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18765        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18766            from: "0.0.1".into(),
18767            instructions: vec![UpgradeInstruction::Restart],
18768        }]);
18769        let slots = c.declared_servico_slots();
18770        assert!(
18771            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18772            "declared_servico_slots must push \
18773             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18774             non-empty — the accessor and the enumerator gate must \
18775             route through the same substrate-primitive typed \
18776             dispatch on the outer :upgrade-from presence bit (got \
18777             slots={slots:?})",
18778        );
18779        let c = caixa_with_upgrade_from(vec![]);
18780        let slots = c.declared_servico_slots();
18781        assert!(
18782            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18783            "declared_servico_slots must NOT push \
18784             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18785             empty — the author-omitted arm must route through the \
18786             accessor's empty-slice return unchanged (got \
18787             slots={slots:?})",
18788        );
18789    }
18790
18791    #[test]
18792    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
18793        // Composition pin: [`crate::render::servico_m2_overlay`]'s
18794        // per-`:upgrade-from` M2 overlay emit arm must key off
18795        // [`Caixa::upgrade_from`], not the raw
18796        // `!caixa.upgrade_from.is_empty()` presence gate + the
18797        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
18798        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
18799        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
18800        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
18801        // sequence in the overlay (the emitter fans onto the serde
18802        // slice-serialization), and a `Caixa { upgrade_from: vec![],
18803        // .. }` must omit the key entirely (the empty-slice
18804        // partition — the `!.is_empty()` outer gate elides the key
18805        // when the author omitted the slot). The pair jointly pins
18806        // the accessor + M2 overlay emitter composition: any future
18807        // silent detour that had the accessor return a fresh-cloned
18808        // `Vec<UpgradeFromEntry>` copy would silently break the
18809        // reference-identity pin the peer per-entry
18810        // `serde_yaml::to_value(caixa.upgrade_from())` projection
18811        // reads from — the projection would clone once per accessor
18812        // call instead of borrowing the storage buffer verbatim.
18813        //
18814        // Peer of the sibling
18815        // `servico_m2_overlay_limits_arm_routes_through_accessor`
18816        // (b2bd9d7) and
18817        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
18818        // (35d8b52) composition pins on the sibling `:limits` /
18819        // `:behavior` outer-`Option<&Composite>` arms — same "the
18820        // M2 overlay emitter must route through the substrate-
18821        // primitive typed dispatch" discipline extended onto the
18822        // third M2 Servico-runtime slot axis, closing the overlay
18823        // emitter's routing invariant on every M2 arm.
18824        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
18825        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18826        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18827            from: "0.0.1".into(),
18828            instructions: vec![UpgradeInstruction::Restart],
18829        }]);
18830        let overlay = servico_m2_overlay(&c).unwrap();
18831        assert!(
18832            overlay.contains_key(M2_KEY_UPGRADE_FROM),
18833            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
18834             `:upgrade-from` is non-empty — the accessor and the M2 \
18835             overlay emitter must route through the same substrate- \
18836             primitive typed dispatch on the outer :upgrade-from \
18837             slice (got overlay={overlay:?})",
18838        );
18839        let c = caixa_with_upgrade_from(vec![]);
18840        let overlay = servico_m2_overlay(&c).unwrap();
18841        assert!(
18842            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
18843            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
18844             `:upgrade-from` is empty — the empty-slice partition \
18845             must route through the accessor's empty-slice return \
18846             unchanged (got overlay={overlay:?})",
18847        );
18848    }
18849
18850    #[test]
18851    fn upgrade_from_projects_slice_by_borrow() {
18852        // The by-borrow pin: [`Caixa::upgrade_from`] returns
18853        // `&[UpgradeFromEntry]` by borrow — the returned slice
18854        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
18855        // the `:upgrade-from` slot and the accessor must not clone
18856        // the backing `Vec` on every call. Peer of the sibling
18857        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
18858        // (`autores_projects_slice_by_borrow` b5d813f,
18859        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18860        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18861        // `exe_projects_slice_by_borrow` 65d9527,
18862        // `servicos_projects_slice_by_borrow` 611f78b,
18863        // `deps_projects_slice_by_borrow` ad34b4e,
18864        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
18865        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
18866        // axes — extended here to the first outer-`Caixa`
18867        // composite-element `&[Composite]` axis: the accessor's
18868        // returned slice must borrow from `&self` (the returned
18869        // reference's lifetime is tied to `&self`), and calling the
18870        // accessor twice on the same [`Caixa`] must yield slices
18871        // that are pointer-equal (the underlying byte-buffer is the
18872        // storage `Vec`'s allocation, not a fresh copy) as well as
18873        // value-equal (idempotent, no side effects on `&self`).
18874        //
18875        // Pins against a future silent detour that returned an owned
18876        // `Vec<UpgradeFromEntry>` (which would type-check but
18877        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
18878        // return (which would leak the backing `Vec`'s
18879        // grow/push/reserve surface no downstream consumer reaches
18880        // for), or a one-arm-only accessor that returned a
18881        // saturating value on some sentinel input.
18882        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18883        for upgrade_from in [
18884            vec![],
18885            vec![UpgradeFromEntry {
18886                from: "0.0.1".into(),
18887                instructions: vec![UpgradeInstruction::Restart],
18888            }],
18889            vec![
18890                UpgradeFromEntry {
18891                    from: "0.0.1".into(),
18892                    instructions: vec![UpgradeInstruction::Restart],
18893                },
18894                UpgradeFromEntry {
18895                    from: "0.0.2".into(),
18896                    instructions: vec![UpgradeInstruction::SoftPurge {
18897                        module: "demo".into(),
18898                    }],
18899                },
18900            ],
18901        ] {
18902            let c = caixa_with_upgrade_from(upgrade_from.clone());
18903            let first = c.upgrade_from();
18904            let second = c.upgrade_from();
18905            assert_eq!(
18906                first, second,
18907                "Caixa::upgrade_from must be idempotent — two \
18908                 successive calls on the same &self must return the \
18909                 same &[UpgradeFromEntry]",
18910            );
18911            assert_eq!(
18912                first.as_ptr(),
18913                second.as_ptr(),
18914                "Caixa::upgrade_from must borrow the underlying \
18915                 Vec<UpgradeFromEntry> storage — two successive calls \
18916                 must return slices with the same backing pointer (a \
18917                 fresh Vec<UpgradeFromEntry> clone would change the \
18918                 pointer on every call)",
18919            );
18920            assert_eq!(
18921                first,
18922                upgrade_from.as_slice(),
18923                "Caixa::upgrade_from must return :upgrade-from \
18924                 verbatim by borrow — got {first:?}, expected \
18925                 {upgrade_from:?}",
18926            );
18927        }
18928    }
18929
18930    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
18931
18932    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
18933        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18934        c.children = children;
18935        c
18936    }
18937
18938    #[test]
18939    fn children_returns_children_slice_verbatim_across_permutations() {
18940        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
18941        // outer-composite `&[ChildSpec]`-return slice-shape pin:
18942        // [`Caixa::children`] must return the `:children` typed
18943        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
18944        // the same backing buffer the raw `self.children.as_slice()`
18945        // field access borrows from, element-equal across every
18946        // representative fixture in the accept-set — `[]` (the "no
18947        // static children declared" arm every non-`Supervisor`-kind
18948        // `defcaixa` carries by `#[serde(default)]` and every
18949        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
18950        // a canonical single-child `Permanent` fixture (the shape
18951        // most `OneForOne` supervisors carry — a single long-running
18952        // worker child), a canonical multi-child list carrying every
18953        // typed restart-policy variant (`Permanent` / `Transient` /
18954        // `Temporary`), and a past-the-guard sentinel — a duplicate
18955        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
18956        // ([`crate::SupervisorSpec::validate`] rejects through
18957        // `DuplicateChildNome { nome: "w" }` but the accessor must
18958        // ship the raw slot verbatim so struct-literal fixtures
18959        // continue to expose the duplicate at the accessor boundary).
18960        //
18961        // Pins against a future silent detour that returned an owned
18962        // `Vec<ChildSpec>` (which would type-check but silently clone
18963        // on every accessor call, breaking the zero-cost projection
18964        // every peer sibling slice accessor carries), a `[dup, dup] →
18965        // [dup]` dedup collapse (which would silently absorb the
18966        // `DuplicateChildNome` refusal case at the accessor boundary
18967        // and the [`crate::StandardLayout::verify`] cross-child gate
18968        // would silently accept a struct-literal `Caixa` carrying the
18969        // drift), a reference to an operator-resolved overlay (the
18970        // future per-cluster `:children-overrides` slot — its
18971        // resolution must land at exactly this accessor body, not
18972        // silently divert the raw slot away from a second consumer),
18973        // or an axis-shuffled projection (a future detour that
18974        // reordered children through the accessor would silently
18975        // split the paired [`crate::StandardLayout::verify`] per-
18976        // supervisor gate's traversal input from the peer
18977        // [`Self::supervisor_view`] fold-in path's clone-order input,
18978        // since the OTP `RestForOne` restart strategy dispatches on
18979        // declared child order and axis reordering would silently
18980        // split the operator's per-cluster restart-fan-out order
18981        // from the caixa.lisp source-order).
18982        //
18983        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
18984        // accessor pin on the substrate primitive for M2 / M3 typed-
18985        // slot vec-carry axes — folds on the outer-`Caixa`
18986        // `&[Composite]` composite-slice sub-family the sibling
18987        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18988        // (2a1f907) pin opened, peer at the outer altitude of the
18989        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
18990        // (bc92bce) accessor on the same OTP-supervisor static-child-
18991        // list axis.
18992        use crate::supervisor::{ChildSpec, RestartPolicy};
18993        let fixtures: Vec<Vec<ChildSpec>> = vec![
18994            vec![],
18995            vec![ChildSpec {
18996                caixa: "worker".into(),
18997                versao: "^0.1".into(),
18998                restart: RestartPolicy::Permanent,
18999            }],
19000            vec![
19001                ChildSpec {
19002                    caixa: "worker-a".into(),
19003                    versao: "^0.1".into(),
19004                    restart: RestartPolicy::Permanent,
19005                },
19006                ChildSpec {
19007                    caixa: "worker-b".into(),
19008                    versao: "^0.1".into(),
19009                    restart: RestartPolicy::Transient,
19010                },
19011                ChildSpec {
19012                    caixa: "worker-c".into(),
19013                    versao: "^0.1".into(),
19014                    restart: RestartPolicy::Temporary,
19015                },
19016            ],
19017            vec![
19018                ChildSpec {
19019                    caixa: "w".into(),
19020                    versao: "^0.1".into(),
19021                    restart: RestartPolicy::Permanent,
19022                },
19023                ChildSpec {
19024                    caixa: "w".into(),
19025                    versao: "^0.1".into(),
19026                    restart: RestartPolicy::Permanent,
19027                },
19028            ],
19029        ];
19030        for children in fixtures {
19031            let c = caixa_with_children(children.clone());
19032            assert_eq!(
19033                c.children(),
19034                children.as_slice(),
19035                "Caixa::children must return :children verbatim \
19036                 (got {:?}, expected {children:?})",
19037                c.children(),
19038            );
19039            assert_eq!(
19040                c.children(),
19041                c.children.as_slice(),
19042                "Caixa::children must element-equal the raw \
19043                 `self.children.as_slice()` field access across \
19044                 every value in the Vec<ChildSpec> accept-set",
19045            );
19046            assert_eq!(
19047                c.children().is_empty(),
19048                c.children.is_empty(),
19049                "Caixa::children().is_empty() must byte-equal \
19050                 self.children.is_empty() — a presence-bit drift \
19051                 would silently split the paired \
19052                 Caixa::declared_supervisor_slots supervisor-tree \
19053                 declared-slot enumerator's presence probe from the \
19054                 peer Caixa::supervisor_view typed-view composer's \
19055                 fold-in path",
19056            );
19057        }
19058    }
19059
19060    #[test]
19061    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
19062        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
19063        // `:children` presence-probe arm must key off
19064        // [`Caixa::children`], not the raw
19065        // `!self.children.is_empty()` field-probe. Structurally: a
19066        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
19067        // "^0.1", restart: Permanent }], .. }` must push
19068        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
19069        // (the presence bit is non-empty, so the supervisor-tree
19070        // kind-coherence gate must surface the slot as "declared"),
19071        // and a `Caixa { children: vec![], .. }` must NOT push the
19072        // label (the "author omitted the slot entirely" arm — the
19073        // empty-slice partition the serde-default folds onto). The
19074        // pair jointly pins the accessor + declared-slot enumerator
19075        // composition: any future silent detour that had the accessor
19076        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
19077        // "__reserved__")` projection) would silently absorb the
19078        // "declared but degenerate" arm at the accessor boundary and
19079        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
19080        // kind-coherence gate would silently accept a struct-literal
19081        // `Caixa` carrying the drift.
19082        //
19083        // Peer of the sibling
19084        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19085        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
19086        // same "the enumerator gate must route through the substrate-
19087        // primitive typed dispatch" discipline extended onto the
19088        // supervisor-tree `:children` composite-slice arm.
19089        use crate::supervisor::{ChildSpec, RestartPolicy};
19090        let c = caixa_with_children(vec![ChildSpec {
19091            caixa: "w".into(),
19092            versao: "^0.1".into(),
19093            restart: RestartPolicy::Permanent,
19094        }]);
19095        let slots = c.declared_supervisor_slots();
19096        assert!(
19097            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
19098            "declared_supervisor_slots must push \
19099             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
19100             non-empty — the accessor and the enumerator gate must \
19101             route through the same substrate-primitive typed \
19102             dispatch on the outer :children presence bit (got \
19103             slots={slots:?})",
19104        );
19105        let c = caixa_with_children(vec![]);
19106        let slots = c.declared_supervisor_slots();
19107        assert!(
19108            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
19109            "declared_supervisor_slots must NOT push \
19110             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
19111             empty — the author-omitted arm must route through the \
19112             accessor's empty-slice return unchanged (got \
19113             slots={slots:?})",
19114        );
19115    }
19116
19117    #[test]
19118    fn supervisor_view_children_arm_routes_through_accessor() {
19119        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
19120        // fold-in arm must key off [`Caixa::children`], not the raw
19121        // `self.children.clone()` field-clone. Structurally: a `Caixa {
19122        // kind: Supervisor, estrategia: Some(OneForOne), children:
19123        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
19124        // per-child list through the accessor into the typed
19125        // [`SupervisorSpec`] view's `children` field verbatim — every
19126        // entry the accessor surfaces must land in the view's
19127        // `children` slot in the same order. The pair jointly pins the
19128        // accessor + view-composer composition: any future silent
19129        // detour that had the accessor return a fresh-cloned
19130        // `Vec<ChildSpec>` copy would silently break the reference-
19131        // identity pin the peer `supervisor_view` fold-in path reads
19132        // from — the fold would clone once more per accessor call
19133        // instead of borrowing the storage buffer verbatim once.
19134        //
19135        // Peer of the sibling
19136        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
19137        // family) composition pin on the peer kind-gate arm — same
19138        // "the view composer must route through the substrate-
19139        // primitive typed dispatch" discipline extended onto the
19140        // per-`:children` fold-in arm, closing the supervisor-view
19141        // composer's routing invariant on the composite-slice input.
19142        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19143        let mut c = caixa_with_children(vec![
19144            ChildSpec {
19145                caixa: "worker-a".into(),
19146                versao: "^0.1".into(),
19147                restart: RestartPolicy::Permanent,
19148            },
19149            ChildSpec {
19150                caixa: "worker-b".into(),
19151                versao: "^0.1".into(),
19152                restart: RestartPolicy::Transient,
19153            },
19154        ]);
19155        c.kind = crate::CaixaKind::Supervisor;
19156        c.estrategia = Some(RestartStrategy::OneForOne);
19157        let view = c
19158            .supervisor_view()
19159            .expect("Supervisor kind must produce a supervisor_view");
19160        assert_eq!(
19161            view.children(),
19162            c.children(),
19163            "supervisor_view must fold Caixa::children verbatim into \
19164             SupervisorSpec::children — the accessor and the view \
19165             composer must route through the same substrate-primitive \
19166             typed dispatch on the outer :children slice (got view \
19167             children={:?}, expected {:?})",
19168            view.children(),
19169            c.children(),
19170        );
19171    }
19172
19173    #[test]
19174    fn children_projects_slice_by_borrow() {
19175        // The by-borrow pin: [`Caixa::children`] returns
19176        // `&[ChildSpec]` by borrow — the returned slice borrows the
19177        // underlying `Vec<ChildSpec>` storage of the `:children` slot
19178        // and the accessor must not clone the backing `Vec` on every
19179        // call. Peer of the sibling outer top-level [`Caixa`]
19180        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
19181        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
19182        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19183        // `exe_projects_slice_by_borrow` 65d9527,
19184        // `servicos_projects_slice_by_borrow` 611f78b,
19185        // `deps_projects_slice_by_borrow` ad34b4e,
19186        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19187        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
19188        // sibling outer top-level [`Caixa`] scalar-element and
19189        // composite-element `&[T]` axes — folds on the outer-`Caixa`
19190        // composite-element `&[Composite]` axis: the accessor's
19191        // returned slice must borrow from `&self` (the returned
19192        // reference's lifetime is tied to `&self`), and calling the
19193        // accessor twice on the same [`Caixa`] must yield slices
19194        // that are pointer-equal (the underlying byte-buffer is the
19195        // storage `Vec`'s allocation, not a fresh copy) as well as
19196        // value-equal (idempotent, no side effects on `&self`).
19197        //
19198        // Pins against a future silent detour that returned an owned
19199        // `Vec<ChildSpec>` (which would type-check but silently clone
19200        // on every call), a `&Vec<ChildSpec>` return (which would leak
19201        // the backing `Vec`'s grow/push/reserve surface no downstream
19202        // consumer reaches for), or a one-arm-only accessor that
19203        // returned a saturating value on some sentinel input.
19204        use crate::supervisor::{ChildSpec, RestartPolicy};
19205        for children in [
19206            vec![],
19207            vec![ChildSpec {
19208                caixa: "w".into(),
19209                versao: "^0.1".into(),
19210                restart: RestartPolicy::Permanent,
19211            }],
19212            vec![
19213                ChildSpec {
19214                    caixa: "worker-a".into(),
19215                    versao: "^0.1".into(),
19216                    restart: RestartPolicy::Permanent,
19217                },
19218                ChildSpec {
19219                    caixa: "worker-b".into(),
19220                    versao: "^0.1".into(),
19221                    restart: RestartPolicy::Transient,
19222                },
19223            ],
19224        ] {
19225            let c = caixa_with_children(children.clone());
19226            let first = c.children();
19227            let second = c.children();
19228            assert_eq!(
19229                first, second,
19230                "Caixa::children must be idempotent — two successive \
19231                 calls on the same &self must return the same \
19232                 &[ChildSpec]",
19233            );
19234            assert_eq!(
19235                first.as_ptr(),
19236                second.as_ptr(),
19237                "Caixa::children must borrow the underlying \
19238                 Vec<ChildSpec> storage — two successive calls must \
19239                 return slices with the same backing pointer (a fresh \
19240                 Vec<ChildSpec> clone would change the pointer on \
19241                 every call)",
19242            );
19243            assert_eq!(
19244                first,
19245                children.as_slice(),
19246                "Caixa::children must return :children verbatim by \
19247                 borrow — got {first:?}, expected {children:?}",
19248            );
19249        }
19250    }
19251
19252    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
19253
19254    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
19255        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19256        c.kind = CaixaKind::Aplicacao;
19257        c.membros = membros;
19258        c
19259    }
19260
19261    #[test]
19262    fn membros_returns_membros_slice_verbatim_across_permutations() {
19263        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
19264        // composite `&[Membro]`-return slice-shape pin:
19265        // [`Caixa::membros`] must return the `:membros` typed
19266        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
19267        // same backing buffer the raw `self.membros.as_slice()` field
19268        // access borrows from, element-equal across every
19269        // representative fixture in the accept-set — `[]` (the "no
19270        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
19271        // carries by `#[serde(default)]` and every partially-authored
19272        // Aplicacao carries before the
19273        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
19274        // canonical single-member fixture (the shape a minimal
19275        // Aplicacao carries — one Servico wrapping one contained
19276        // computation), a canonical multi-member list carrying three
19277        // distinct entries (the canonical checkout-shape Aplicacao —
19278        // cart / pricing / auth — every canonical example carries), and
19279        // a past-the-guard sentinel — a duplicate `:caixa`
19280        // `[("cart", ...), ("cart", ...)]` entry pair
19281        // ([`crate::AplicacaoSpec::validate`] rejects through
19282        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
19283        // the raw slot verbatim so struct-literal fixtures continue to
19284        // expose the duplicate at the accessor boundary).
19285        //
19286        // Pins against a future silent detour that returned an owned
19287        // `Vec<Membro>` (which would type-check but silently clone on
19288        // every accessor call, breaking the zero-cost projection every
19289        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
19290        // dedup collapse (which would silently absorb the
19291        // `DuplicateMembro` refusal case at the accessor boundary and
19292        // the [`crate::StandardLayout::verify`] cross-member gate would
19293        // silently accept a struct-literal `Caixa` carrying the drift),
19294        // a reference to an operator-resolved overlay (the future per-
19295        // cluster `:membros-overrides` slot — its resolution must land
19296        // at exactly this accessor body, not silently divert the raw
19297        // slot away from a second consumer), or an axis-shuffled
19298        // projection (a future detour that reordered members through
19299        // the accessor would silently split the paired
19300        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19301        // traversal input from the peer [`Self::aplicacao_view`] fold-
19302        // in path's clone-order input, since the canonical `:contratos`
19303        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
19304        // read the member set through the same slice).
19305        //
19306        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
19307        // accessor pin on the substrate primitive for M2 / M3 typed-
19308        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
19309        // arm of the `&[Composite]` composite-slice sub-family the
19310        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19311        // (2a1f907) and
19312        // `children_returns_children_slice_verbatim_across_permutations`
19313        // (c17b51e) pins opened, peer at the outer altitude of the
19314        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
19315        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
19316        // list axis.
19317        use crate::aplicacao::Membro;
19318        let fixtures: Vec<Vec<Membro>> = vec![
19319            vec![],
19320            vec![Membro {
19321                caixa: "cart".into(),
19322                versao: "^0.1".into(),
19323            }],
19324            vec![
19325                Membro {
19326                    caixa: "cart".into(),
19327                    versao: "^0.1".into(),
19328                },
19329                Membro {
19330                    caixa: "pricing".into(),
19331                    versao: "^0.2".into(),
19332                },
19333                Membro {
19334                    caixa: "auth".into(),
19335                    versao: "^1.0".into(),
19336                },
19337            ],
19338            vec![
19339                Membro {
19340                    caixa: "cart".into(),
19341                    versao: "^0.1".into(),
19342                },
19343                Membro {
19344                    caixa: "cart".into(),
19345                    versao: "^0.1".into(),
19346                },
19347            ],
19348        ];
19349        for membros in fixtures {
19350            let c = caixa_aplicacao_with_membros(membros.clone());
19351            assert_eq!(
19352                c.membros(),
19353                membros.as_slice(),
19354                "Caixa::membros must return :membros verbatim \
19355                 (got {:?}, expected {membros:?})",
19356                c.membros(),
19357            );
19358            assert_eq!(
19359                c.membros(),
19360                c.membros.as_slice(),
19361                "Caixa::membros must element-equal the raw \
19362                 `self.membros.as_slice()` field access across every \
19363                 value in the Vec<Membro> accept-set",
19364            );
19365            assert_eq!(
19366                c.membros().is_empty(),
19367                c.membros.is_empty(),
19368                "Caixa::membros().is_empty() must byte-equal \
19369                 self.membros.is_empty() — a presence-bit drift would \
19370                 silently split the paired Caixa::declared_mesh_slots \
19371                 mesh declared-slot enumerator's presence probe from \
19372                 the peer Caixa::aplicacao_view typed-view composer's \
19373                 fold-in path",
19374            );
19375        }
19376    }
19377
19378    #[test]
19379    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
19380        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
19381        // presence-probe arm must key off [`Caixa::membros`], not the
19382        // raw `!self.membros.is_empty()` field-probe. Structurally: a
19383        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
19384        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
19385        // declared-slot list (the presence bit is non-empty, so the
19386        // mesh kind-coherence gate must surface the slot as
19387        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
19388        // push the label (the "author omitted the slot entirely" arm
19389        // — the empty-slice partition the serde-default folds onto).
19390        // The pair jointly pins the accessor + declared-slot
19391        // enumerator composition: any future silent detour that had
19392        // the accessor collapse `[Membro { .. }]` to `[]` (a
19393        // `.filter(|m| m.nome() != "__reserved__")` projection) would
19394        // silently absorb the "declared but degenerate" arm at the
19395        // accessor boundary and the
19396        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19397        // coherence gate would silently accept a struct-literal
19398        // `Caixa` carrying the drift.
19399        //
19400        // Peer of the sibling
19401        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19402        // (2a1f907) and
19403        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19404        // (c17b51e) composition pins on the M2 `:upgrade-from` /
19405        // `:children` composite-slice arms — same "the enumerator gate
19406        // must route through the substrate-primitive typed dispatch"
19407        // discipline extended onto the M3 `:membros` composite-slice
19408        // arm, opening the M3 arm of the declared-slot enumerator's
19409        // routing invariant.
19410        use crate::aplicacao::Membro;
19411        let c = caixa_aplicacao_with_membros(vec![Membro {
19412            caixa: "cart".into(),
19413            versao: "^0.1".into(),
19414        }]);
19415        let slots = c.declared_mesh_slots();
19416        assert!(
19417            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19418            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
19419             `:membros` is non-empty — the accessor and the enumerator \
19420             gate must route through the same substrate-primitive \
19421             typed dispatch on the outer :membros presence bit (got \
19422             slots={slots:?})",
19423        );
19424        let c = caixa_aplicacao_with_membros(vec![]);
19425        let slots = c.declared_mesh_slots();
19426        assert!(
19427            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19428            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
19429             when `:membros` is empty — the author-omitted arm must \
19430             route through the accessor's empty-slice return unchanged \
19431             (got slots={slots:?})",
19432        );
19433    }
19434
19435    #[test]
19436    fn aplicacao_view_membros_arm_routes_through_accessor() {
19437        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
19438        // fold-in arm must key off [`Caixa::membros`], not the raw
19439        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
19440        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
19441        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
19442        // member list through the accessor into the typed
19443        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
19444        // every entry the accessor surfaces must land in the view's
19445        // `membros` slot in the same order. The pair jointly pins the
19446        // accessor + view-composer composition: any future silent
19447        // detour that had the accessor return a fresh-cloned
19448        // `Vec<Membro>` copy would silently break the reference-
19449        // identity pin the peer `aplicacao_view` fold-in path reads
19450        // from — the fold would clone once more per accessor call
19451        // instead of borrowing the storage buffer verbatim once.
19452        //
19453        // Peer of the sibling
19454        // `aplicacao_view_politicas_arm_folds_through_accessor`
19455        // (5d23d29) /
19456        // `aplicacao_view_placement_arm_folds_through_accessor`
19457        // (4fb8074) /
19458        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
19459        // composition pins on the M3 `:politicas` / `:placement` /
19460        // `:entrada` outer-`Option<&Composite>` arms — extended here to
19461        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
19462        // closing the aplicacao-view composer's routing invariant on
19463        // the composite-slice input.
19464        use crate::aplicacao::Membro;
19465        let c = caixa_aplicacao_with_membros(vec![
19466            Membro {
19467                caixa: "cart".into(),
19468                versao: "^0.1".into(),
19469            },
19470            Membro {
19471                caixa: "pricing".into(),
19472                versao: "^0.2".into(),
19473            },
19474        ]);
19475        let view = c
19476            .aplicacao_view()
19477            .expect("Aplicacao kind must produce an aplicacao_view");
19478        assert_eq!(
19479            view.membros(),
19480            c.membros(),
19481            "aplicacao_view must fold Caixa::membros verbatim into \
19482             AplicacaoSpec::membros — the accessor and the view \
19483             composer must route through the same substrate-primitive \
19484             typed dispatch on the outer :membros slice (got view \
19485             membros={:?}, expected {:?})",
19486            view.membros(),
19487            c.membros(),
19488        );
19489    }
19490
19491    #[test]
19492    fn membros_projects_slice_by_borrow() {
19493        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
19494        // borrow — the returned slice borrows the underlying
19495        // `Vec<Membro>` storage of the `:membros` slot and the
19496        // accessor must not clone the backing `Vec` on every call.
19497        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19498        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19499        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19500        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19501        // `exe_projects_slice_by_borrow` 65d9527,
19502        // `servicos_projects_slice_by_borrow` 611f78b,
19503        // `deps_projects_slice_by_borrow` ad34b4e,
19504        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19505        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19506        // `children_projects_slice_by_borrow` c17b51e) on the sibling
19507        // outer top-level [`Caixa`] scalar-element and composite-
19508        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
19509        // slot composite-element `&[Composite]` axis: the accessor's
19510        // returned slice must borrow from `&self` (the returned
19511        // reference's lifetime is tied to `&self`), and calling the
19512        // accessor twice on the same [`Caixa`] must yield slices that
19513        // are pointer-equal (the underlying byte-buffer is the storage
19514        // `Vec`'s allocation, not a fresh copy) as well as value-equal
19515        // (idempotent, no side effects on `&self`).
19516        //
19517        // Pins against a future silent detour that returned an owned
19518        // `Vec<Membro>` (which would type-check but silently clone on
19519        // every call), a `&Vec<Membro>` return (which would leak the
19520        // backing `Vec`'s grow/push/reserve surface no downstream
19521        // consumer reaches for), or a one-arm-only accessor that
19522        // returned a saturating value on some sentinel input.
19523        use crate::aplicacao::Membro;
19524        for membros in [
19525            vec![],
19526            vec![Membro {
19527                caixa: "cart".into(),
19528                versao: "^0.1".into(),
19529            }],
19530            vec![
19531                Membro {
19532                    caixa: "cart".into(),
19533                    versao: "^0.1".into(),
19534                },
19535                Membro {
19536                    caixa: "pricing".into(),
19537                    versao: "^0.2".into(),
19538                },
19539            ],
19540        ] {
19541            let c = caixa_aplicacao_with_membros(membros.clone());
19542            let first = c.membros();
19543            let second = c.membros();
19544            assert_eq!(
19545                first, second,
19546                "Caixa::membros must be idempotent — two successive \
19547                 calls on the same &self must return the same &[Membro]",
19548            );
19549            assert_eq!(
19550                first.as_ptr(),
19551                second.as_ptr(),
19552                "Caixa::membros must borrow the underlying Vec<Membro> \
19553                 storage — two successive calls must return slices with \
19554                 the same backing pointer (a fresh Vec<Membro> clone \
19555                 would change the pointer on every call)",
19556            );
19557            assert_eq!(
19558                first,
19559                membros.as_slice(),
19560                "Caixa::membros must return :membros verbatim by borrow \
19561                 — got {first:?}, expected {membros:?}",
19562            );
19563        }
19564    }
19565
19566    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
19567
19568    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
19569        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19570        c.kind = CaixaKind::Aplicacao;
19571        c.contratos = contratos;
19572        c
19573    }
19574
19575    fn contrato_http_for_test(
19576        de: &str,
19577        para: &str,
19578        endpoint: &str,
19579    ) -> crate::aplicacao::WitContract {
19580        crate::aplicacao::WitContract {
19581            de: de.into(),
19582            para: para.into(),
19583            wit: "wasi:http/proxy".into(),
19584            endpoint: Some(endpoint.into()),
19585            subject: None,
19586            slot: None,
19587        }
19588    }
19589
19590    #[test]
19591    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
19592        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
19593        // composite `&[WitContract]`-return slice-shape pin:
19594        // [`Caixa::contratos`] must return the `:contratos` typed
19595        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
19596        // over the same backing buffer the raw
19597        // `self.contratos.as_slice()` field access borrows from,
19598        // element-equal across every representative fixture in the
19599        // accept-set — `[]` (the "no contracts declared" arm every
19600        // non-`Aplicacao`-kind `defcaixa` carries by
19601        // `#[serde(default)]` and every leaf-Aplicacao with a single
19602        // member carries), a canonical single-edge fixture (the
19603        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
19604        // edge), and a canonical multi-edge fixture with three distinct
19605        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
19606        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
19607        //
19608        // Pins against a future silent detour that returned an owned
19609        // `Vec<WitContract>` (which would type-check but silently clone
19610        // on every accessor call, breaking the zero-cost projection
19611        // every peer sibling slice accessor carries), an axis-shuffled
19612        // projection (a future detour that reordered edges through the
19613        // accessor would silently split the paired
19614        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19615        // traversal input from the peer [`Self::aplicacao_view`] fold-
19616        // in path's clone-order input, since every canonical
19617        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
19618        // seed dispatch reads the edge set through the same slice),
19619        // or a reference to an operator-resolved overlay (the future
19620        // per-cluster `:contratos-overrides` slot — its resolution
19621        // must land at exactly this accessor body, not silently divert
19622        // the raw slot away from a second consumer).
19623        //
19624        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
19625        // accessor pin on the substrate primitive for M2 / M3 typed-
19626        // slot vec-carry axes — closes the outer-`Caixa`
19627        // `&[Composite]` composite-slice sub-family the sibling M2
19628        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19629        // (2a1f907) and
19630        // `children_returns_children_slice_verbatim_across_permutations`
19631        // (c17b51e) pins opened and the M3
19632        // `membros_returns_membros_slice_verbatim_across_permutations`
19633        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
19634        // slot arm of the composite-slice sub-family. Peer at the outer
19635        // altitude of the closed inner-
19636        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
19637        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
19638        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
19639            vec![],
19640            vec![contrato_http_for_test("cart", "catalog", "/items")],
19641            vec![
19642                contrato_http_for_test("cart", "catalog", "/items"),
19643                contrato_http_for_test("cart", "pricing", "/price"),
19644                contrato_http_for_test("cart", "auth", "/whoami"),
19645            ],
19646        ];
19647        for contratos in fixtures {
19648            let c = caixa_aplicacao_with_contratos(contratos.clone());
19649            assert_eq!(
19650                c.contratos(),
19651                contratos.as_slice(),
19652                "Caixa::contratos must return :contratos verbatim \
19653                 (got {:?}, expected {contratos:?})",
19654                c.contratos(),
19655            );
19656            assert_eq!(
19657                c.contratos(),
19658                c.contratos.as_slice(),
19659                "Caixa::contratos must element-equal the raw \
19660                 `self.contratos.as_slice()` field access across every \
19661                 value in the Vec<WitContract> accept-set",
19662            );
19663            assert_eq!(
19664                c.contratos().is_empty(),
19665                c.contratos.is_empty(),
19666                "Caixa::contratos().is_empty() must byte-equal \
19667                 self.contratos.is_empty() — a presence-bit drift would \
19668                 silently split the paired Caixa::declared_mesh_slots \
19669                 mesh declared-slot enumerator's presence probe from \
19670                 the peer Caixa::aplicacao_view typed-view composer's \
19671                 fold-in path",
19672            );
19673        }
19674    }
19675
19676    #[test]
19677    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
19678        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
19679        // presence-probe arm must key off [`Caixa::contratos`], not the
19680        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
19681        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
19682        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
19683        // presence bit is non-empty, so the mesh kind-coherence gate
19684        // must surface the slot as "declared"), and a `Caixa {
19685        // contratos: vec![], .. }` must NOT push the label (the "author
19686        // omitted the slot entirely" arm — the empty-slice partition
19687        // the serde-default folds onto). The pair jointly pins the
19688        // accessor + declared-slot enumerator composition: any future
19689        // silent detour that had the accessor collapse
19690        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
19691        // "__reserved__")` projection) would silently absorb the
19692        // "declared but degenerate" arm at the accessor boundary and
19693        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19694        // coherence gate would silently accept a struct-literal
19695        // `Caixa` carrying the drift.
19696        //
19697        // Peer of the sibling
19698        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19699        // (2a1f907),
19700        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19701        // (c17b51e), and
19702        // `declared_mesh_slots_membros_arm_routes_through_accessor`
19703        // (0f26987) composition pins on the M2 `:upgrade-from` /
19704        // `:children` / M3 `:membros` composite-slice arms — same "the
19705        // enumerator gate must route through the substrate-primitive
19706        // typed dispatch" discipline extended onto the M3 `:contratos`
19707        // composite-slice arm, closing the M3 mesh-slot arm of the
19708        // declared-slot enumerator's routing invariant on the
19709        // composite-slice inputs.
19710        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
19711            "cart", "catalog", "/items",
19712        )]);
19713        let slots = c.declared_mesh_slots();
19714        assert!(
19715            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19716            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
19717             `:contratos` is non-empty — the accessor and the enumerator \
19718             gate must route through the same substrate-primitive \
19719             typed dispatch on the outer :contratos presence bit (got \
19720             slots={slots:?})",
19721        );
19722        let c = caixa_aplicacao_with_contratos(vec![]);
19723        let slots = c.declared_mesh_slots();
19724        assert!(
19725            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19726            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
19727             when `:contratos` is empty — the author-omitted arm must \
19728             route through the accessor's empty-slice return unchanged \
19729             (got slots={slots:?})",
19730        );
19731    }
19732
19733    #[test]
19734    fn aplicacao_view_contratos_arm_routes_through_accessor() {
19735        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
19736        // fold-in arm must key off [`Caixa::contratos`], not the raw
19737        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
19738        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
19739        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
19740        // per-edge list through the accessor into the typed
19741        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
19742        // every entry the accessor surfaces must land in the view's
19743        // `contratos` slot in the same order. The pair jointly pins
19744        // the accessor + view-composer composition: a future silent
19745        // detour that had the accessor shuffle or drop an edge would
19746        // silently split the paired declared-slot enumerator's
19747        // presence bit from the typed-view composer's edge-list, a
19748        // two-consumer split at the enumerator and the view composer
19749        // far from the source `caixa.lisp`.
19750        //
19751        // Peer of the sibling
19752        // `aplicacao_view_membros_arm_routes_through_accessor`
19753        // (0f26987) composition pin on the M3 `:membros` outer-
19754        // `&[Composite]` composite-slice arm, closing the aplicacao-
19755        // view composer's routing invariant on the composite-slice
19756        // inputs at the outer altitude.
19757        let c = caixa_aplicacao_with_contratos(vec![
19758            contrato_http_for_test("cart", "catalog", "/items"),
19759            contrato_http_for_test("cart", "pricing", "/price"),
19760        ]);
19761        let view = c
19762            .aplicacao_view()
19763            .expect("Aplicacao kind must produce an aplicacao_view");
19764        assert_eq!(
19765            view.contratos(),
19766            c.contratos(),
19767            "aplicacao_view must fold Caixa::contratos verbatim into \
19768             AplicacaoSpec::contratos — the accessor and the view \
19769             composer must route through the same substrate-primitive \
19770             typed dispatch on the outer :contratos slice (got view \
19771             contratos={:?}, expected {:?})",
19772            view.contratos(),
19773            c.contratos(),
19774        );
19775    }
19776
19777    #[test]
19778    fn contratos_projects_slice_by_borrow() {
19779        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
19780        // by borrow — the returned slice borrows the underlying
19781        // `Vec<WitContract>` storage of the `:contratos` slot and the
19782        // accessor must not clone the backing `Vec` on every call.
19783        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19784        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19785        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19786        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19787        // `exe_projects_slice_by_borrow` 65d9527,
19788        // `servicos_projects_slice_by_borrow` 611f78b,
19789        // `deps_projects_slice_by_borrow` ad34b4e,
19790        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19791        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19792        // `children_projects_slice_by_borrow` c17b51e,
19793        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
19794        // outer top-level [`Caixa`] scalar-element and composite-
19795        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
19796        // composite-element `&[Composite]` axis on the by-borrow pin:
19797        // the accessor's returned slice must borrow from `&self` (the
19798        // returned reference's lifetime is tied to `&self`), and
19799        // calling the accessor twice on the same [`Caixa`] must yield
19800        // slices that are pointer-equal (the underlying byte-buffer is
19801        // the storage `Vec`'s allocation, not a fresh copy) as well as
19802        // value-equal (idempotent, no side effects on `&self`).
19803        //
19804        // Pins against a future silent detour that returned an owned
19805        // `Vec<WitContract>` (which would type-check but silently clone
19806        // on every call), a `&Vec<WitContract>` return (which would
19807        // leak the backing `Vec`'s grow/push/reserve surface no
19808        // downstream consumer reaches for), or a one-arm-only accessor
19809        // that returned a saturating value on some sentinel input.
19810        for contratos in [
19811            vec![],
19812            vec![contrato_http_for_test("cart", "catalog", "/items")],
19813            vec![
19814                contrato_http_for_test("cart", "catalog", "/items"),
19815                contrato_http_for_test("cart", "pricing", "/price"),
19816            ],
19817        ] {
19818            let c = caixa_aplicacao_with_contratos(contratos.clone());
19819            let first = c.contratos();
19820            let second = c.contratos();
19821            assert_eq!(
19822                first, second,
19823                "Caixa::contratos must be idempotent — two successive \
19824                 calls on the same &self must return the same \
19825                 &[WitContract]",
19826            );
19827            assert_eq!(
19828                first.as_ptr(),
19829                second.as_ptr(),
19830                "Caixa::contratos must borrow the underlying \
19831                 Vec<WitContract> storage — two successive calls must \
19832                 return slices with the same backing pointer (a fresh \
19833                 Vec<WitContract> clone would change the pointer on \
19834                 every call)",
19835            );
19836            assert_eq!(
19837                first,
19838                contratos.as_slice(),
19839                "Caixa::contratos must return :contratos verbatim by \
19840                 borrow — got {first:?}, expected {contratos:?}",
19841            );
19842        }
19843    }
19844
19845    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
19846
19847    #[test]
19848    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
19849        // Load-bearing invariant: every multi-word top-level [`Caixa`]
19850        // serde-derived JSON key routes through a lifted `&'static str`
19851        // const. The Rust field names are `snake_case`
19852        // (`deps_dev` / `upgrade_from` / `max_restarts` /
19853        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
19854        // "camelCase")]` derive attribute maps each to the camelCase
19855        // byte-string the [`Caixa::to_lisp`] round-trip's
19856        // `serde_json::to_value(self)` step lands under before
19857        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
19858        // to the kebab-case `:deps-dev` / `:upgrade-from` /
19859        // `:max-restarts` / `:restart-window` author surface. Serialize
19860        // a fully-populated [`Caixa`] and pin that each canonical
19861        // byte-sequence appears verbatim in the JSON — a future
19862        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
19863        // verbatim-field-name flip at the derive attribute (any of
19864        // which would silently break every [`Caixa::to_lisp`]
19865        // round-trip and the future M4 operator-side manifest ingest's
19866        // `Value::get(<key>)` navigation) surfaces here as a build-time
19867        // test failure at `manifest.rs`, not as an apply-time
19868        // `.get(<stale-canonical-const>)` returning `None` far from the
19869        // derive-attr drift's commit. Same discipline the sibling
19870        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
19871        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
19872        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
19873        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
19874        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
19875        // [`UpgradeFromEntry`] per-entry axes — extended here to the
19876        // enclosing M0 [`Caixa`] top-level axis so the last of the four
19877        // multi-word top-level [`Caixa`] serde-derived JSON keys
19878        // (`depsDev`) joins the substrate's "one canonical byte-string
19879        // per typed serialized-key axis" discipline.
19880        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19881        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
19882        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19883        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
19884        c.upgrade_from = vec![UpgradeFromEntry {
19885            from: "0.0.1".into(),
19886            instructions: vec![UpgradeInstruction::Restart],
19887        }];
19888        c.estrategia = Some(RestartStrategy::OneForOne);
19889        c.max_restarts = Some(3);
19890        c.restart_window = Some("60s".into());
19891        c.children = vec![ChildSpec {
19892            caixa: "child".into(),
19893            versao: "^0.1".into(),
19894            restart: RestartPolicy::Permanent,
19895        }];
19896        let json = serde_json::to_string(&c).unwrap();
19897        for key in [
19898            crate::render::CAIXA_KEY_DEPS_DEV,
19899            crate::render::M2_KEY_UPGRADE_FROM,
19900            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19901            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19902        ] {
19903            let quoted = format!("\"{key}\"");
19904            assert!(
19905                json.contains(&quoted),
19906                "serialized Caixa must carry the lifted top-level \
19907                 multi-word byte-sequence {quoted} verbatim in the JSON \
19908                 emission (got: {json})",
19909            );
19910        }
19911    }
19912
19913    #[test]
19914    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
19915        // Cross-axis drift-detection pin: a future collapse of the four
19916        // canonical [`Caixa`] top-level multi-word byte-strings onto the
19917        // same value (e.g. an accidental copy-paste flip of
19918        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
19919        // `"upgradeFrom"`) would silently reroute every downstream
19920        // `Value::get(<key>)` probe on one axis onto the sibling axis's
19921        // top-level entry and pass every propagation-probe test that
19922        // expected only the stale axis's value. Peer of the sibling
19923        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
19924        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
19925        let all = [
19926            crate::render::CAIXA_KEY_DEPS_DEV,
19927            crate::render::M2_KEY_UPGRADE_FROM,
19928            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19929            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19930        ];
19931        for (i, a) in all.iter().enumerate() {
19932            for b in all.iter().skip(i + 1) {
19933                assert_ne!(
19934                    a, b,
19935                    "Caixa top-level multi-word key consts must be \
19936                     pairwise-distinct canonical byte-sequences — got \
19937                     `{a}` == `{b}`",
19938                );
19939            }
19940        }
19941    }
19942
19943    #[test]
19944    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
19945        // Shape-pin: every [`Caixa`] top-level multi-word key const must
19946        // be a lowerCamelCase byte-sequence (no `snake_case`
19947        // underscores, no `kebab-case` hyphens, no leading colon, no
19948        // `PascalCase` leading capital, no whitespace / dots) — the
19949        // canonical shape the `#[serde(rename_all = "camelCase")]`
19950        // derive produces on [`Caixa`]. A future flip to a
19951        // non-camelCase attribute at the derive surfaces both here
19952        // (this test fails on the stale-constant shape) and at
19953        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19954        // (that test fails on the mismatch between const and derive).
19955        // Peer with `membro_key_consts_are_lower_camel_case_shape`
19956        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
19957        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
19958        for key in [
19959            crate::render::CAIXA_KEY_DEPS_DEV,
19960            crate::render::M2_KEY_UPGRADE_FROM,
19961            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19962            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19963        ] {
19964            assert!(
19965                !key.is_empty(),
19966                "Caixa top-level multi-word key const must be non-empty \
19967                 (got {key:?})"
19968            );
19969            let first = key.chars().next().unwrap();
19970            assert!(
19971                first.is_ascii_lowercase(),
19972                "Caixa top-level multi-word key const must lead with an \
19973                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
19974            );
19975            assert!(
19976                key.chars().all(|c| c.is_ascii_alphanumeric()),
19977                "Caixa top-level multi-word key const must be \
19978                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
19979                 whitespace (got {key:?})",
19980            );
19981        }
19982    }
19983
19984    #[test]
19985    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
19986        // Scalar-value pin: the byte-string the
19987        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
19988        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
19989        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
19990        // → `depsTest` matching a hypothetical per-test-target
19991        // vocabulary flip) lands as an edit to exactly one const AND
19992        // one derive attribute — the sibling
19993        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19994        // pin already ties the const to the derive attribute, so a
19995        // rebrand that touches only one side of the pair fails at
19996        // caixa-core build time. Same "scalar-value pin per const"
19997        // discipline the sibling
19998        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
19999        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
20000        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
20001        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
20002    }
20003
20004    #[test]
20005    fn caixa_key_deps_pins_canonical_byte_string() {
20006        // Scalar-value pin: the byte-string the
20007        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
20008        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
20009        // on the two-list dep-graph serialized-key axis — the sibling
20010        // pin covers the multi-word `deps_dev → depsDev` camelCase
20011        // arm, this pin covers the single-word `deps → deps` no-op arm
20012        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
20013        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
20014        // axis and the emitted JSON key equals the source-side field
20015        // name byte-for-byte). A future [`crate::Caixa::deps`] field
20016        // rename (`deps` → `dependencies` matching Cargo's verbatim
20017        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
20018        // hypothetical per-runtime-target vocabulary flip) OR an added
20019        // `#[serde(rename = "…")]` explicit override lands as an edit
20020        // to exactly one const AND one derive-attr / field name — the
20021        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
20022        // pin ties the const to the emitted JSON key, so a rebrand
20023        // that touches only one side of the pair fails at caixa-core
20024        // build time.
20025        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
20026    }
20027
20028    #[test]
20029    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
20030        // Load-bearing invariant on the single-word `deps` top-level
20031        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
20032        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
20033        // `serde_json::to_value(self)` step emits. Serialize a
20034        // populated [`Caixa`] whose `:deps` slot carries at least one
20035        // entry (the `#[serde(default)]` attribute on the field emits
20036        // an empty `[]` even without members, but a non-empty vec
20037        // additionally covers the codec's per-`Dep`-entry emission
20038        // path) and pin that `"deps"` appears verbatim in the JSON
20039        // emission — a future accidental `rename_all = "snake_case"` /
20040        // `"kebab-case"` flip at the derive attribute (or an added
20041        // `#[serde(rename = "…")]` explicit override on the field, or
20042        // a Rust field rename) would break every [`Caixa::to_lisp`]
20043        // round-trip and the future M4 operator-side manifest ingest's
20044        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
20045        // build-time test failure at `manifest.rs`, not as an
20046        // apply-time `.get(<stale-canonical-const>)` returning `None`
20047        // far from the drift's commit. Peer of the sibling
20048        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
20049        // multi-word pin on the same M0 [`Caixa`] top-level
20050        // serialized-key axis, extended here to the single-word arm
20051        // the multi-word test's `rename_all = "camelCase"` sweep can't
20052        // reach (single-word `deps → deps` is a no-op the multi-word
20053        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
20054        // `\"restartWindow\"` byte-scan can never observe).
20055        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20056        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
20057        let json = serde_json::to_string(&c).unwrap();
20058        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
20059        assert!(
20060            json.contains(&quoted),
20061            "serialized Caixa must carry the lifted top-level `deps` \
20062             byte-sequence {quoted} verbatim in the JSON emission (got: \
20063             {json})",
20064        );
20065    }
20066
20067    #[test]
20068    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
20069        // Cross-axis drift-detection pin on the two-list dep-graph
20070        // renderer-side wire-key axis: a future collapse of the
20071        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
20072        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
20073        // same value (e.g. an accidental copy-paste flip of
20074        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
20075        // reroute every downstream `Value::get(<key>)` probe on one
20076        // axis onto the sibling axis's dep-list and pass every
20077        // propagation-probe test that expected only the stale axis's
20078        // value — a dev-only dep would land in the runtime closure at
20079        // publish time, or a runtime dep would be excluded from the
20080        // published lacre. Peer of the sibling four-way distinct pin
20081        // on the top-level multi-word tetrad
20082        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
20083        // and the two-way pin on the sibling
20084        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
20085        // author-facing arm (4da6fba's test), extended here to the
20086        // renderer-side wire-key arm of the same two-list dep-graph
20087        // axis so both halves of the "one canonical byte-string per
20088        // typed axis per (author, wire)" grid carry the same
20089        // distinct-ness discipline.
20090        assert_ne!(
20091            crate::render::CAIXA_KEY_DEPS,
20092            crate::render::CAIXA_KEY_DEPS_DEV,
20093            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
20094             canonical byte-sequences on the two-list dep-graph \
20095             renderer-side wire-key axis"
20096        );
20097    }
20098
20099    // ── DepList / Caixa::push_dep pin ────────────────────────────────
20100    //
20101    // The compounding pin: the two-arm closed-set typed enum
20102    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
20103    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
20104    // consumer of the top-level manifest's dep-mutation surface reads
20105    // through, and the typed dispatch [`Caixa::push_dep`] on the
20106    // substrate primitive folds the "select list → check within-list
20107    // dup → push" cascade onto one method call. Prior to this landing
20108    // the two axes lived across two `&'static str` constants
20109    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
20110    // set type carrying the pair; the `feira add` mutation site's
20111    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
20112    // caixa.deps }` dispatch expressed no compile-time link back to
20113    // the substrate primitive, and a future third dep-list axis would
20114    // have silently split at every open-coded mutation site.
20115
20116    #[test]
20117    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
20118        // Every arm returns the same `&'static str` the substrate's
20119        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
20120        // constants carry. A future rebrand on either constant reaches
20121        // the enum through one edit; a regression to inline literals
20122        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
20123        // quotes from the wire-format constants every consumer routes
20124        // through and this pin flags it at build time.
20125        assert_eq!(
20126            crate::dep::DepList::Prod.as_str(),
20127            crate::render::DEP_AUTHOR_KEY_DEPS
20128        );
20129        assert_eq!(
20130            crate::dep::DepList::Dev.as_str(),
20131            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20132        );
20133    }
20134
20135    #[test]
20136    fn dep_list_display_routes_through_as_str() {
20137        // Same as-str-through-Display convergence discipline the
20138        // sibling closed-set typed enums carry — a `format!("{list}")`
20139        // call must land byte-for-byte on the accessor's return so a
20140        // future consumer that formats the enum for a diagnostic line
20141        // reaches the same wire-format constant the wire-format
20142        // producers do.
20143        assert_eq!(
20144            format!("{}", crate::dep::DepList::Prod),
20145            crate::dep::DepList::Prod.as_str()
20146        );
20147        assert_eq!(
20148            format!("{}", crate::dep::DepList::Dev),
20149            crate::dep::DepList::Dev.as_str()
20150        );
20151    }
20152
20153    #[test]
20154    fn dep_list_all_enumerates_every_variant_once() {
20155        // Exhaustive-iteration pin — every arm appears exactly once in
20156        // `ALL`, matching the closed set the compiler enforces on the
20157        // sibling `match self` arms. A future variant addition that
20158        // extends only one method's match without extending `ALL`
20159        // would silently drop the new arm from every consumer that
20160        // iterates the slice.
20161        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
20162        assert!(variants.contains(&crate::dep::DepList::Prod));
20163        assert!(variants.contains(&crate::dep::DepList::Dev));
20164        assert_eq!(variants.len(), 2);
20165    }
20166
20167    #[test]
20168    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
20169        // Reverse projection on the two-list dep-graph axis: the
20170        // author-surface wire tag the sibling `as_str` emitter walks
20171        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
20172        // `Some(DepList::Prod)`. A regression that hand-rolled the
20173        // per-arm match without routing through the lifted
20174        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
20175        // future wire-tag rebrand and this pin flags it at build time.
20176        assert_eq!(
20177            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
20178            Some(crate::dep::DepList::Prod)
20179        );
20180    }
20181
20182    #[test]
20183    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
20184        // Peer of the `Prod`-arm pin on the dev-only axis: the
20185        // author-surface wire tag the sibling `as_str` emitter walks
20186        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
20187        // back to `Some(DepList::Dev)`. Same drift-detection posture
20188        // as the peer arm — the sibling method `match` arms are
20189        // compiler-checked exhaustive so a future variant addition
20190        // trips at build time.
20191        assert_eq!(
20192            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
20193            Some(crate::dep::DepList::Dev)
20194        );
20195    }
20196
20197    #[test]
20198    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
20199        // Every input outside the closed-set arm-string set the
20200        // sibling `as_str` emitter walks lands on the terminal `None`
20201        // fallback — no silent-accept surface. Sweeps a set of
20202        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
20203        // rebrand candidates, foreign wire tags, empty string) so a
20204        // future variant addition that widened one wire form without
20205        // extending the emitter's arm-set would trip the sibling
20206        // round-trip pin below rather than silently accepting the new
20207        // form here.
20208        for candidate in [
20209            "",
20210            "deps",
20211            "deps-dev",
20212            ":deps ",
20213            ":Deps",
20214            ":DEPS",
20215            ":build-dep",
20216            ":tool-dep",
20217            "prod",
20218            "dev",
20219        ] {
20220            assert_eq!(
20221                crate::dep::DepList::from_wire(candidate),
20222                None,
20223                "from_wire({candidate:?}) must return None; every input outside \
20224                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
20225                 the sibling as_str emitter walks lands on the terminal fallback",
20226            );
20227        }
20228    }
20229
20230    #[test]
20231    fn dep_list_round_trips_through_as_str_and_from_wire() {
20232        // Load-bearing round-trip pin: every arm the `ALL` iteration
20233        // exposes survives the `as_str` → `from_wire` composition
20234        // byte-for-byte. Same discipline the sibling closed-set enums
20235        // carry — `CaixaKind` /
20236        // `RestartStrategy` / `RestartPolicy` /
20237        // `PlacementStrategy` — extended onto the two-list dep-graph
20238        // axis. A future variant addition that extends `ALL` +
20239        // `as_str` without extending `from_wire` (or vice versa)
20240        // trips at build time on this iteration because the compiler
20241        // enforces exhaustiveness on the sibling `match self` arms.
20242        for &list in crate::dep::DepList::ALL {
20243            assert_eq!(
20244                crate::dep::DepList::from_wire(list.as_str()),
20245                Some(list),
20246                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
20247                 a silent split between the forward emitter and the reverse parser \
20248                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
20249            );
20250        }
20251    }
20252
20253    #[test]
20254    fn push_dep_routes_to_deps_slot_on_prod_arm() {
20255        // The `Prod` arm dispatches to the runtime-closure `:deps`
20256        // slot every downstream lacre-pipeline consumer resolves at
20257        // build time. A future arm that regressed to inline `&mut
20258        // self.deps_dev` on the `Prod` path would silently reroute
20259        // every runtime dep into the dev-only closure at publish time
20260        // — this pin refuses that regression.
20261        let src = Caixa::template("host");
20262        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20263        let before_deps = caixa.deps().len();
20264        let before_deps_dev = caixa.deps_dev().len();
20265        let dep = Dep {
20266            nome: "caixa-teia".to_string(),
20267            versao: "^0.1".to_string(),
20268            fonte: None,
20269            opcional: false,
20270            caracteristicas: Vec::new(),
20271        };
20272        caixa
20273            .push_dep(crate::dep::DepList::Prod, dep)
20274            .expect("first push into :deps succeeds");
20275        assert_eq!(caixa.deps().len(), before_deps + 1);
20276        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
20277        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
20278    }
20279
20280    #[test]
20281    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
20282        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
20283        // must dispatch to the dev-only-closure `:deps-dev` slot every
20284        // downstream test-facing artifact resolver reads. A future
20285        // regression that inverted the two arms would silently route
20286        // every dev-only dep into the runtime closure at publish time
20287        // and this pin catches it before the drift ships.
20288        let src = Caixa::template("host");
20289        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20290        let dep = Dep {
20291            nome: "tatara-check".to_string(),
20292            versao: "*".to_string(),
20293            fonte: None,
20294            opcional: false,
20295            caracteristicas: Vec::new(),
20296        };
20297        caixa
20298            .push_dep(crate::dep::DepList::Dev, dep)
20299            .expect("first push into :deps-dev succeeds");
20300        assert!(caixa.deps().is_empty());
20301        assert_eq!(caixa.deps_dev().len(), 1);
20302        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
20303    }
20304
20305    #[test]
20306    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
20307        // Within-list dup check routes through the canonical
20308        // [`DepError::DuplicateNome`] carrier — the substrate's typed
20309        // diagnostic for the same axis [`Caixa::validate_deps`]'s
20310        // parse-time [`crate::render::insert_first_seen`] walk raises
20311        // on. Prior to the lift the mutation site's inline
20312        // `bail!("dep '{}' already declared", …)` string-diagnostic
20313        // path expressed no through-line back to the typed error;
20314        // routing every dep-list refusal through one carrier means an
20315        // author reading a `feira add` refusal and a `feira build`
20316        // refusal reaches for the same corrective surface without
20317        // switching diagnostic idioms.
20318        let src = Caixa::template("host");
20319        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20320        let dep = Dep {
20321            nome: "caixa-teia".to_string(),
20322            versao: "^0.1".to_string(),
20323            fonte: None,
20324            opcional: false,
20325            caracteristicas: Vec::new(),
20326        };
20327        caixa
20328            .push_dep(crate::dep::DepList::Prod, dep.clone())
20329            .expect("first push succeeds");
20330        let dup = Dep {
20331            nome: "caixa-teia".to_string(),
20332            versao: "^0.2".to_string(),
20333            fonte: None,
20334            opcional: false,
20335            caracteristicas: Vec::new(),
20336        };
20337        let err = caixa
20338            .push_dep(crate::dep::DepList::Prod, dup)
20339            .expect_err("second push with same :nome refuses");
20340        assert_eq!(
20341            err,
20342            DepError::DuplicateNome {
20343                nome: "caixa-teia".to_string(),
20344                list: crate::render::DEP_AUTHOR_KEY_DEPS,
20345            }
20346        );
20347        // The refused mutation must not corrupt the target list —
20348        // exactly one entry lives past the refusal, matching the
20349        // canonical single-source-of-truth invariant `Caixa::deps()`
20350        // carries.
20351        assert_eq!(caixa.deps().len(), 1);
20352    }
20353
20354    #[test]
20355    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
20356        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
20357        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
20358        // `list` payload so a future author reading the refusal grep's
20359        // for the correct `:deps-dev` block in their `caixa.lisp`,
20360        // not the sibling `:deps` block the runtime closure resolves.
20361        let src = Caixa::template("host");
20362        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20363        let dep = Dep {
20364            nome: "tatara-check".to_string(),
20365            versao: "*".to_string(),
20366            fonte: None,
20367            opcional: false,
20368            caracteristicas: Vec::new(),
20369        };
20370        caixa
20371            .push_dep(crate::dep::DepList::Dev, dep.clone())
20372            .expect("first push succeeds");
20373        let err = caixa
20374            .push_dep(crate::dep::DepList::Dev, dep)
20375            .expect_err("second push with same :nome refuses");
20376        assert!(matches!(
20377            err,
20378            DepError::DuplicateNome {
20379                ref nome,
20380                list,
20381            } if nome == "tatara-check"
20382                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20383        ));
20384    }
20385
20386    #[test]
20387    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
20388        // The within-list dup check is scoped to the target arm — a
20389        // caixa may legitimately carry the same `:nome` under both
20390        // `:deps` and `:deps-dev` (though the substrate's peer
20391        // [`crate::Caixa::validate_deps`] walk still refuses the
20392        // shape at parse time; the mutation-site refusal is scoped to
20393        // the mutation-site's list to match the peer parse-time
20394        // per-list [`crate::render::insert_first_seen`] discipline).
20395        // The two arms hold independent seen-sets.
20396        let src = Caixa::template("host");
20397        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20398        let dep_prod = Dep {
20399            nome: "shared".to_string(),
20400            versao: "^0.1".to_string(),
20401            fonte: None,
20402            opcional: false,
20403            caracteristicas: Vec::new(),
20404        };
20405        let dep_dev = Dep {
20406            nome: "shared".to_string(),
20407            versao: "*".to_string(),
20408            fonte: None,
20409            opcional: false,
20410            caracteristicas: Vec::new(),
20411        };
20412        caixa
20413            .push_dep(crate::dep::DepList::Prod, dep_prod)
20414            .expect("push into :deps succeeds");
20415        caixa
20416            .push_dep(crate::dep::DepList::Dev, dep_dev)
20417            .expect("push same :nome into :deps-dev succeeds");
20418        assert_eq!(caixa.deps().len(), 1);
20419        assert_eq!(caixa.deps_dev().len(), 1);
20420    }
20421
20422    #[test]
20423    fn deps_of_prod_returns_the_deps_slot_verbatim() {
20424        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
20425        // accessor must project onto the runtime-closure `:deps` slot —
20426        // element-equal and length-equal to the sibling per-slot
20427        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
20428        // A future arm that regressed to `self.deps_dev()` on the `Prod`
20429        // path would silently reroute every downstream typed-dispatch
20430        // walker (the [`Caixa::validate_deps`] per-list
20431        // [`crate::render::insert_first_seen`] dedup walk, any future
20432        // per-axis-parametrised consumer) into the sibling dev-only
20433        // closure and this pin refuses that regression.
20434        let src = Caixa::template("host");
20435        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20436        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20437        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
20438        let dep = Dep {
20439            nome: "caixa-teia".to_string(),
20440            versao: "^0.1".to_string(),
20441            fonte: None,
20442            opcional: false,
20443            caracteristicas: Vec::new(),
20444        };
20445        caixa
20446            .push_dep(crate::dep::DepList::Prod, dep.clone())
20447            .expect("push into :deps succeeds");
20448        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20449        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
20450        assert_eq!(
20451            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
20452            "caixa-teia"
20453        );
20454    }
20455
20456    #[test]
20457    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
20458        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
20459        // [`Caixa::deps_of`] must project onto the dev-only-closure
20460        // `:deps-dev` slot, element-equal and length-equal to the
20461        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
20462        // future regression that inverted the two arms would silently
20463        // route every dev-list walker onto the runtime closure and this
20464        // pin catches it before the drift ships.
20465        let src = Caixa::template("host");
20466        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20467        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20468        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
20469        let dep = Dep {
20470            nome: "tatara-check".to_string(),
20471            versao: "*".to_string(),
20472            fonte: None,
20473            opcional: false,
20474            caracteristicas: Vec::new(),
20475        };
20476        caixa
20477            .push_dep(crate::dep::DepList::Dev, dep)
20478            .expect("push into :deps-dev succeeds");
20479        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20480        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
20481        assert_eq!(
20482            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
20483            "tatara-check"
20484        );
20485    }
20486
20487    #[test]
20488    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
20489        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
20490        // [`Caixa::deps_of`] must land on the same two-slot partition the
20491        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
20492        // expose — the canonical dispatch a future per-axis-parametrised
20493        // walker (a future `feira app graph` per-list dep summary, a
20494        // future M4 per-cluster dev-closure-audit overlay the CR
20495        // materializer resolves per-CR) reads through. Prior to the
20496        // lift the two-block iteration lived open-coded at every walker,
20497        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
20498        // §I) would have had to grow a third block at every consumer.
20499        // A regression that dropped the `Dev` arm from `ALL` would flip
20500        // the collected pairs to `[(":deps", &[])]` alone and this pin
20501        // refuses that shape.
20502        let src = Caixa::template("host");
20503        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20504        let prod_dep = Dep {
20505            nome: "caixa-teia".to_string(),
20506            versao: "^0.1".to_string(),
20507            fonte: None,
20508            opcional: false,
20509            caracteristicas: Vec::new(),
20510        };
20511        let dev_dep = Dep {
20512            nome: "tatara-check".to_string(),
20513            versao: "*".to_string(),
20514            fonte: None,
20515            opcional: false,
20516            caracteristicas: Vec::new(),
20517        };
20518        caixa
20519            .push_dep(crate::dep::DepList::Prod, prod_dep)
20520            .expect("push into :deps succeeds");
20521        caixa
20522            .push_dep(crate::dep::DepList::Dev, dev_dep)
20523            .expect("push into :deps-dev succeeds");
20524        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
20525            .iter()
20526            .map(|&list| {
20527                let slice = caixa.deps_of(list);
20528                (list.as_str(), slice.len(), slice[0].nome())
20529            })
20530            .collect();
20531        assert_eq!(
20532            collected,
20533            vec![
20534                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
20535                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
20536            ]
20537        );
20538    }
20539
20540    #[test]
20541    fn caixa_deps_of_is_const_fn() {
20542        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
20543        // `const`-eval-surface posture. The typed-dispatch read
20544        // accessor forwards through the sibling `pub const fn`
20545        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
20546        // accessors on the two [`crate::dep::DepList`] enum arms —
20547        // every operator in the body is already `const`-callable
20548        // (`DepList` is a plain `#[derive(Copy)]` closed-set
20549        // discriminator so the `match` arms are const-evaluable, and
20550        // each arm dispatches through the sibling `pub const fn`
20551        // slice accessor). Any future accidental downgrade to
20552        // non-`const` fails the `deps_of_via_const_fn` wrapper below
20553        // at caixa-core build time with E0015 (`cannot call non-const
20554        // method`), strictly stronger than a runtime `assert!` and
20555        // side-stepping the destructor-in-const restriction the
20556        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
20557        // rule out on the direct-`const _: () = assert!(...)`
20558        // residence.
20559        //
20560        // Peer of the sibling outer-`Caixa` accessor family pins
20561        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
20562        // on the `&[String]` universal-axis surface,
20563        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
20564        // on the outer `&[T]` composite-slice surface,
20565        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
20566        // on the outer `Option<&Composite>` surface) — this pin
20567        // extends the `const`-eval-surface discipline onto the outer-
20568        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
20569        // dep-list axis, closing the outer-`Caixa` accessor family's
20570        // last unlifted `pub fn` on the read side.
20571        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
20572            c.deps_of(list)
20573        }
20574        let src = Caixa::template("host");
20575        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20576        // Empty-list arm: both `Prod` and `Dev` degenerate to the
20577        // empty slice with no silent `None` collapse — the
20578        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
20579        // that omits the slot lands on.
20580        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
20581        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
20582        assert_eq!(
20583            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20584            caixa.deps()
20585        );
20586        assert_eq!(
20587            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20588            caixa.deps_dev()
20589        );
20590        // Populated arms: each list carries its own entry, and the
20591        // wrapper / direct dispatches agree byte-for-byte on the
20592        // slice-view under both non-empty arms.
20593        let prod_dep = Dep {
20594            nome: "caixa-teia".to_string(),
20595            versao: "^0.1".to_string(),
20596            fonte: None,
20597            opcional: false,
20598            caracteristicas: Vec::new(),
20599        };
20600        let dev_dep = Dep {
20601            nome: "tatara-check".to_string(),
20602            versao: "*".to_string(),
20603            fonte: None,
20604            opcional: false,
20605            caracteristicas: Vec::new(),
20606        };
20607        caixa
20608            .push_dep(crate::dep::DepList::Prod, prod_dep)
20609            .expect("push into :deps succeeds");
20610        caixa
20611            .push_dep(crate::dep::DepList::Dev, dev_dep)
20612            .expect("push into :deps-dev succeeds");
20613        assert_eq!(
20614            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20615            caixa.deps()
20616        );
20617        assert_eq!(
20618            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20619            caixa.deps_dev()
20620        );
20621        assert_eq!(
20622            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
20623            "caixa-teia"
20624        );
20625        assert_eq!(
20626            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
20627            "tatara-check"
20628        );
20629    }
20630
20631    #[test]
20632    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
20633        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
20634        // must route its per-list [`crate::render::insert_first_seen`]
20635        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
20636        // rather than the pre-lift open-coded two-block iteration over
20637        // `self.deps()` + `self.deps_dev()`. A regression that dropped
20638        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
20639        // stop refusing within-list dups on the sibling arm; a
20640        // regression that flipped the arm-to-list-key mapping
20641        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
20642        // diagnostic surface. Both drifts surface here through a paired
20643        // duplicate-name refusal per arm plus an offending-list-key
20644        // check on the emitted [`DepError::DuplicateNome`] carrier.
20645        for &list in crate::dep::DepList::ALL {
20646            let src = Caixa::template("host");
20647            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20648            let dup = Dep {
20649                nome: "twin".to_string(),
20650                versao: "^0.1".to_string(),
20651                fonte: None,
20652                opcional: false,
20653                caracteristicas: Vec::new(),
20654            };
20655            match list {
20656                crate::dep::DepList::Prod => {
20657                    caixa.deps.push(dup.clone());
20658                    caixa.deps.push(dup);
20659                }
20660                crate::dep::DepList::Dev => {
20661                    caixa.deps_dev.push(dup.clone());
20662                    caixa.deps_dev.push(dup);
20663                }
20664            }
20665            let err = caixa
20666                .validate_deps()
20667                .expect_err("within-list duplicate :nome must refuse");
20668            assert_eq!(
20669                err,
20670                DepError::DuplicateNome {
20671                    nome: "twin".to_string(),
20672                    list: list.as_str(),
20673                },
20674                "validate_deps on {list} arm must emit \
20675                 DepError::DuplicateNome carrying the arm's own \
20676                 as_str() diagnostic — the arm-to-list-key mapping \
20677                 flowed through DepList::ALL + Caixa::deps_of"
20678            );
20679        }
20680    }
20681
20682    #[test]
20683    fn caixa_licenca_default_pins_canonical_mit_byte() {
20684        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
20685        // canonical SPDX-`"MIT"` byte today, the same license expression
20686        // every peer substrate-side consumer of the author-omitted
20687        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
20688        // `caixa-helm/src/lib.rs`, the future M4
20689        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
20690        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
20691        // crate's [`Caixa::validate_licenca`] docstring roadmap already
20692        // names as the second consumer) fills into its per-consumer
20693        // README/annotation emit site. Pin the literal here (peer with the
20694        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
20695        // [`crate::version::DEFAULT_GIT_REMOTE`] /
20696        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
20697        // on the sibling lifted-constant surfaces) so a future
20698        // substrate-side license-fallback rebrand surfaces here as a
20699        // coordinated edit-point: the sibling caixa-helm
20700        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
20701        // pinning test already pins the equality at the renderer-emit
20702        // axis; this pin closes the second coordinate of the pair by
20703        // anchoring the lifted constant's current byte to the canonical
20704        // CAIXA-SDLC §I license scaffold's documented shape.
20705        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
20706    }
20707
20708    // ── Caixa::validate_upgrade_from — compound per-Caixa entry gate on ──
20709    // ── the M2 `:upgrade-from` slot: folds the three top-level        ──
20710    // ── `crate::upgrade` validators (per-entry + cross-entry           ──
20711    // ── duplicate-`:from`, cross-slot `:from < :versao` precedence,   ──
20712    // ── cross-slot `:state-change` ↔ `:on-state-change` composition)  ──
20713    // ── onto one substrate primitive. Byte-for-byte equivalent to the ──
20714    // ── pre-fold three-block cascade at                               ──
20715    // ── `crate::layout::StandardLayout::verify` under the same        ──
20716    // ── canonical dispatch order.                                     ──
20717
20718    #[test]
20719    fn validate_upgrade_from_folds_per_entry_arm_matches_gate() {
20720        // Fail-before-pass-after per-arm equivalence pin on the
20721        // per-entry + cross-entry axis: a fixture whose `:upgrade-from`
20722        // carries a per-entry-invalid `:from` (git-tag shape `"v0.1.0"`,
20723        // which `semver::Version::parse` rejects) surfaces the same
20724        // [`crate::UpgradeError`] through the compound gate
20725        // [`Caixa::validate_upgrade_from`] and the standalone per-entry
20726        // gate [`crate::upgrade::validate_upgrade_from`] on the same
20727        // [`Caixa::upgrade_from`] slice. Pins the fold — a silent
20728        // regression that de-folded the per-entry arm would surface here
20729        // as a mismatch between the two dispatches. Sibling in shape to
20730        // the peer per-slot-≡-standalone equivalence pins the
20731        // [`crate::AplicacaoSpec::validate_contratos`] /
20732        // [`crate::MeshPolicy::validate`] /
20733        // [`crate::SupervisorSpec::validate_children`] compound gates
20734        // each carry on their axes.
20735        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20736        c.upgrade_from = vec![crate::UpgradeFromEntry {
20737            from: "v0.1.0".into(),
20738            instructions: vec![crate::UpgradeInstruction::Restart],
20739        }];
20740        let via_method = c.validate_upgrade_from().unwrap_err();
20741        let via_standalone = crate::upgrade::validate_upgrade_from(c.upgrade_from()).unwrap_err();
20742        assert_eq!(
20743            via_method, via_standalone,
20744            "Caixa::validate_upgrade_from must surface the per-entry \
20745             axis's diagnostic byte-equal to the standalone \
20746             `crate::upgrade::validate_upgrade_from` on the same \
20747             upgrade_from() slice"
20748        );
20749        assert!(
20750            matches!(
20751                via_method,
20752                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.1.0"
20753            ),
20754            "expected FromInvalid on the git-tag-shape `:from`, got {via_method:?}"
20755        );
20756    }
20757
20758    #[test]
20759    fn validate_upgrade_from_folds_versao_arm_matches_gate() {
20760        // Per-arm equivalence pin on the cross-slot `:from ↔ :versao`
20761        // precedence axis: a fixture with a well-formed `:from` (so the
20762        // per-entry arm passes) whose parsed semver is >= the caixa's
20763        // `:versao` under SemVer-2 precedence surfaces the same
20764        // [`crate::UpgradeError::FromNotBeforeVersao`] through both the
20765        // compound gate and the standalone
20766        // [`crate::upgrade::validate_upgrade_from_against_versao`] gate
20767        // keyed off the same `(upgrade_from, versao)` pair. Pins the
20768        // fold's second arm — reaching this arm through the compound
20769        // gate requires the per-entry arm to pass first, which itself
20770        // pins the per-arm cross-arm ordering.
20771        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20772        c.versao = "0.1.0".into();
20773        c.upgrade_from = vec![crate::UpgradeFromEntry {
20774            from: "0.2.0".into(),
20775            instructions: vec![crate::UpgradeInstruction::Restart],
20776        }];
20777        let via_method = c.validate_upgrade_from().unwrap_err();
20778        let via_standalone =
20779            crate::upgrade::validate_upgrade_from_against_versao(c.upgrade_from(), c.versao())
20780                .unwrap_err();
20781        assert_eq!(
20782            via_method, via_standalone,
20783            "Caixa::validate_upgrade_from must surface the \
20784             `:from >= :versao` diagnostic byte-equal to the standalone \
20785             `crate::upgrade::validate_upgrade_from_against_versao` on \
20786             the same (upgrade_from, versao) pair"
20787        );
20788        assert!(
20789            matches!(
20790                via_method,
20791                crate::UpgradeError::FromNotBeforeVersao { ref from, ref versao }
20792                    if from == "0.2.0" && versao == "0.1.0"
20793            ),
20794            "expected FromNotBeforeVersao carrying the offending pair, got {via_method:?}"
20795        );
20796    }
20797
20798    #[test]
20799    fn validate_upgrade_from_folds_behavior_arm_matches_gate() {
20800        // Per-arm equivalence pin on the cross-slot `:state-change ↔
20801        // :on-state-change` composition axis: a fixture with a
20802        // well-formed `:from` strictly less than `:versao` (so the
20803        // per-entry and versao arms both pass) whose `:instructions`
20804        // list carries a `(:state-change …)` instruction with no
20805        // `:behavior :on-state-change` callback declared surfaces the
20806        // same [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
20807        // through both the compound gate and the standalone
20808        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
20809        // gate keyed off the same `(upgrade_from, behavior)` pair.
20810        // Reaching this arm through the compound gate requires both
20811        // prior arms to pass first — the ordering pin below pins the
20812        // per-arm dispatch order explicitly.
20813        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20814        c.versao = "0.2.0".into();
20815        c.behavior = None;
20816        c.upgrade_from = vec![crate::UpgradeFromEntry {
20817            from: "0.1.0".into(),
20818            instructions: vec![
20819                crate::UpgradeInstruction::LoadModule {
20820                    module: "demo".into(),
20821                },
20822                crate::UpgradeInstruction::StateChange {
20823                    script: std::path::PathBuf::from("lib/m.lisp"),
20824                },
20825                crate::UpgradeInstruction::SoftPurge {
20826                    module: "demo-old".into(),
20827                },
20828            ],
20829        }];
20830        let via_method = c.validate_upgrade_from().unwrap_err();
20831        let via_standalone =
20832            crate::upgrade::validate_upgrade_from_against_behavior(c.upgrade_from(), c.behavior())
20833                .unwrap_err();
20834        assert_eq!(
20835            via_method, via_standalone,
20836            "Caixa::validate_upgrade_from must surface the \
20837             `:state-change` ↔ `:on-state-change` composition \
20838             diagnostic byte-equal to the standalone \
20839             `crate::upgrade::validate_upgrade_from_against_behavior` \
20840             on the same (upgrade_from, behavior) pair"
20841        );
20842        assert!(
20843            matches!(
20844                via_method,
20845                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback {
20846                    ref from,
20847                    ref script,
20848                } if from == "0.1.0" && script == &std::path::PathBuf::from("lib/m.lisp")
20849            ),
20850            "expected StateChangeWithoutOnStateChangeCallback carrying \
20851             the offending (from, script) pair, got {via_method:?}"
20852        );
20853    }
20854
20855    #[test]
20856    fn validate_upgrade_from_per_entry_arm_fires_before_versao_arm() {
20857        // Cross-arm ordering pin between the first two arms of the
20858        // fold: a fixture carrying BOTH a per-entry-invalid `:from`
20859        // (`"v0.0.5"` — git-tag shape rejected by
20860        // [`crate::upgrade::validate_upgrade_from`]) AND a would-be
20861        // versao-precedence violation on a second entry (`"0.2.0" >=
20862        // :versao "0.1.0"`) surfaces the per-entry diagnostic first
20863        // through the compound gate. Sanity assertion: the second
20864        // entry alone under the same `:versao` trips the versao arm
20865        // on its own via the standalone
20866        // [`crate::upgrade::validate_upgrade_from_against_versao`], so
20867        // the per-entry-first surfacing is a real ordering property,
20868        // not a case where the versao arm silently accepts the
20869        // fixture. Pins the pre-fold layout wire-up's canonical
20870        // dispatch order (per-entry → versao → behavior) as a
20871        // property of the substrate primitive rather than a
20872        // convention of the layout call site.
20873        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20874        c.versao = "0.1.0".into();
20875        c.upgrade_from = vec![
20876            crate::UpgradeFromEntry {
20877                from: "v0.0.5".into(),
20878                instructions: vec![crate::UpgradeInstruction::Restart],
20879            },
20880            crate::UpgradeFromEntry {
20881                from: "0.2.0".into(),
20882                instructions: vec![crate::UpgradeInstruction::Restart],
20883            },
20884        ];
20885        let err = c.validate_upgrade_from().unwrap_err();
20886        assert!(
20887            matches!(
20888                err,
20889                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.0.5"
20890            ),
20891            "per-entry arm must fire before versao arm — expected \
20892             FromInvalid on `v0.0.5`, got {err:?}"
20893        );
20894        // Sanity: the versao-violating second entry alone under the
20895        // same `:versao` trips the versao arm on its own — proves the
20896        // per-entry-first surfacing above is a real ordering property.
20897        let sanity = crate::upgrade::validate_upgrade_from_against_versao(
20898            &[crate::UpgradeFromEntry {
20899                from: "0.2.0".into(),
20900                instructions: vec![crate::UpgradeInstruction::Restart],
20901            }],
20902            "0.1.0",
20903        )
20904        .unwrap_err();
20905        assert!(
20906            matches!(sanity, crate::UpgradeError::FromNotBeforeVersao { .. }),
20907            "sanity: the versao-violating fixture alone must trip the \
20908             versao arm — got {sanity:?}"
20909        );
20910    }
20911
20912    #[test]
20913    fn validate_upgrade_from_versao_arm_fires_before_behavior_arm() {
20914        // Cross-arm ordering pin between the second and third arms of
20915        // the fold: a fixture carrying BOTH a versao-precedence
20916        // violation (`:from "0.2.0" >= :versao "0.1.0"`) AND a
20917        // would-be missing-callback violation (a `(:state-change …)`
20918        // instruction with no `:behavior :on-state-change`) surfaces
20919        // the versao diagnostic first through the compound gate.
20920        // Sanity assertion: the missing-callback fixture alone (with
20921        // the versao-precedence violation removed by bumping
20922        // `:versao` past `:from`) trips the behavior arm on its own
20923        // via the standalone
20924        // [`crate::upgrade::validate_upgrade_from_against_behavior`],
20925        // so the versao-first surfacing is a real ordering property,
20926        // not a case where the behavior arm silently accepts the
20927        // fixture.
20928        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20929        c.versao = "0.1.0".into();
20930        c.behavior = None;
20931        c.upgrade_from = vec![crate::UpgradeFromEntry {
20932            from: "0.2.0".into(),
20933            instructions: vec![
20934                crate::UpgradeInstruction::LoadModule {
20935                    module: "demo".into(),
20936                },
20937                crate::UpgradeInstruction::StateChange {
20938                    script: std::path::PathBuf::from("lib/m.lisp"),
20939                },
20940            ],
20941        }];
20942        let err = c.validate_upgrade_from().unwrap_err();
20943        assert!(
20944            matches!(
20945                err,
20946                crate::UpgradeError::FromNotBeforeVersao { ref from, .. } if from == "0.2.0"
20947            ),
20948            "versao arm must fire before behavior arm — expected \
20949             FromNotBeforeVersao on `0.2.0`, got {err:?}"
20950        );
20951        // Sanity: the same instructions under a `:versao` that
20952        // accepts the `:from` (so the versao arm passes) trips the
20953        // behavior arm — proves the versao-first surfacing above is a
20954        // real ordering property.
20955        let sanity = crate::upgrade::validate_upgrade_from_against_behavior(
20956            &[crate::UpgradeFromEntry {
20957                from: "0.2.0".into(),
20958                instructions: vec![
20959                    crate::UpgradeInstruction::LoadModule {
20960                        module: "demo".into(),
20961                    },
20962                    crate::UpgradeInstruction::StateChange {
20963                        script: std::path::PathBuf::from("lib/m.lisp"),
20964                    },
20965                ],
20966            }],
20967            None,
20968        )
20969        .unwrap_err();
20970        assert!(
20971            matches!(
20972                sanity,
20973                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback { .. }
20974            ),
20975            "sanity: the missing-callback fixture alone must trip the \
20976             behavior arm — got {sanity:?}"
20977        );
20978    }
20979
20980    #[test]
20981    fn validate_upgrade_from_accepts_clean_fixture() {
20982        // Positive control: a well-formed `:upgrade-from` (single entry
20983        // with `:from` strictly less than `:versao`, no
20984        // `:state-change` instruction so the behavior arm is vacuous)
20985        // passes the compound gate cleanly. A future tightening of any
20986        // one arm's accepted set surfaces here as a test failure
20987        // first. Mirrors the peer `validate_versao_accepts_canonical_forms`
20988        // positive-control posture on the sibling per-Caixa gate.
20989        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20990        c.versao = "0.2.0".into();
20991        c.upgrade_from = vec![crate::UpgradeFromEntry {
20992            from: "0.1.0".into(),
20993            instructions: vec![crate::UpgradeInstruction::Restart],
20994        }];
20995        c.validate_upgrade_from()
20996            .expect("clean fixture must pass the compound `:upgrade-from` gate");
20997    }
20998
20999    #[test]
21000    fn validate_upgrade_from_accepts_empty_upgrade_from() {
21001        // Positive control on the empty-list arm: a caixa without any
21002        // `:upgrade-from` block (the default `Vec::new()`
21003        // `#[serde(default)]` folds an omitted slot onto) passes the
21004        // compound gate cleanly regardless of `:versao` or `:behavior`
21005        // — each of the three standalone validators is vacuous on the
21006        // empty entry list. Pins the identity element of the fold on
21007        // the empty-slot side.
21008        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21009        assert!(
21010            c.upgrade_from().is_empty(),
21011            "template caixa must carry an empty :upgrade-from — got {:?}",
21012            c.upgrade_from()
21013        );
21014        c.validate_upgrade_from()
21015            .expect("empty :upgrade-from must pass the compound gate cleanly");
21016    }
21017
21018    // ── Caixa::validate_limits — compound per-Caixa entry gate on   ──
21019    // ── the M2 `:limits` slot: folds the                            ──
21020    // ── [`crate::LimitsSpec::validate`] four-axis cascade on the    ──
21021    // ── present-slot arm and the `Option::None` identity element on ──
21022    // ── the absent-slot arm onto one substrate primitive.           ──
21023    // ── Byte-for-byte equivalent to the pre-fold                    ──
21024    // ── `if let Some(l) = caixa.limits() { l.validate() }`          ──
21025    // ── unwrap-and-dispatch pattern at                              ──
21026    // ── `crate::layout::StandardLayout::verify` (`layout.rs`).      ──
21027
21028    #[test]
21029    fn validate_limits_folds_arm_matches_gate() {
21030        // Fail-before-pass-after per-arm equivalence pin on the
21031        // present-slot arm: a fixture whose `:limits` carries a
21032        // zero-floor-violating `:fuel` (`Some(0)`, which
21033        // [`crate::LimitsSpec::validate`] rejects through
21034        // [`crate::LimitsError::FuelZero`]) surfaces the same
21035        // [`crate::LimitsError`] byte-equal through both the compound
21036        // gate [`Caixa::validate_limits`] and the standalone
21037        // [`crate::LimitsSpec::validate`] gate on the same `LimitsSpec`
21038        // value. Pins the fold — a silent regression that de-folded
21039        // the present-slot arm would surface here as a mismatch
21040        // between the two dispatches. Sibling in shape to the peer
21041        // per-arm equivalence pins the
21042        // [`crate::AplicacaoSpec::validate_contratos`] /
21043        // [`crate::MeshPolicy::validate`] /
21044        // [`crate::SupervisorSpec::validate_children`] /
21045        // [`Caixa::validate_upgrade_from`] compound gates each carry
21046        // on their axes.
21047        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21048        let l = crate::LimitsSpec {
21049            memory: None,
21050            fuel: Some(0),
21051            wall_clock: None,
21052            cpu: None,
21053        };
21054        c.limits = Some(l);
21055        let via_method = c.validate_limits().unwrap_err();
21056        let via_standalone = l.validate().unwrap_err();
21057        assert_eq!(
21058            via_method, via_standalone,
21059            "Caixa::validate_limits must surface the present-slot \
21060             arm's diagnostic byte-equal to the standalone \
21061             `LimitsSpec::validate` on the same `LimitsSpec` value"
21062        );
21063        assert!(
21064            matches!(via_method, crate::LimitsError::FuelZero),
21065            "expected FuelZero on the zero-floor-violating `:fuel`, \
21066             got {via_method:?}"
21067        );
21068    }
21069
21070    #[test]
21071    fn validate_limits_accepts_none() {
21072        // Positive control on the absent-slot arm (the fold's identity
21073        // element): a caixa without any `:limits` block (the
21074        // canonical "no bound declared — engine-default applies"
21075        // author shape [`crate::LimitsSpec::is_empty`]'s per-axis
21076        // `None` cascade reads, and the shape the [`Caixa::template`]
21077        // scaffold emits by construction) passes the compound gate
21078        // cleanly, regardless of any per-axis defect a subsequent
21079        // `Some(_)` binding would surface. Pins the identity element
21080        // of the fold on the absent-slot side, matching the peer
21081        // `validate_upgrade_from_accepts_empty_upgrade_from` positive-
21082        // control posture on the sibling M2 slot.
21083        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21084        assert!(
21085            c.limits().is_none(),
21086            "template caixa must carry an absent :limits — got {:?}",
21087            c.limits()
21088        );
21089        c.validate_limits()
21090            .expect("absent :limits must pass the compound gate cleanly");
21091    }
21092
21093    #[test]
21094    fn validate_limits_accepts_clean_fixture() {
21095        // Positive control on the present-slot arm: a caixa whose
21096        // `:limits` is `Some(LimitsSpec::default())` (all four axes
21097        // `None` — every axis absent under the outer `Some(_)`
21098        // binding, so every present-slot arm on
21099        // [`crate::LimitsSpec::validate`] is vacuous) passes the
21100        // compound gate cleanly. A future tightening of any one axis
21101        // that surfaces a diagnostic on the all-`None` `LimitsSpec`
21102        // would land here as a test failure first. Pins the
21103        // present-slot arm's accept-shape on the canonical
21104        // "declared-but-empty" author fixture the
21105        // `limits_round_trip_via_json` peer already round-trips
21106        // (`caixa-core/src/manifest.rs:6971`).
21107        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21108        c.limits = Some(crate::LimitsSpec::default());
21109        c.validate_limits()
21110            .expect("Some(LimitsSpec::default()) must pass the compound gate cleanly");
21111    }
21112
21113    // ── Caixa::validate_behavior — compound per-Caixa entry gate on ──
21114    // ── the M2 `:behavior` slot's pure value-shape surface: folds   ──
21115    // ── the [`crate::BehaviorSpec::validate`] six-slot cascade on   ──
21116    // ── the present-slot arm and the `Option::None` identity        ──
21117    // ── element on the absent-slot arm onto one substrate primitive.──
21118    // ── Byte-for-byte equivalent to the pre-fold                    ──
21119    // ── `if let Some(b) = caixa.behavior() { b.validate() }`        ──
21120    // ── unwrap-and-dispatch pattern at                              ──
21121    // ── `crate::layout::StandardLayout::verify` (`layout.rs`). The  ──
21122    // ── on-disk callback-path existence walk stays open-coded at    ──
21123    // ── the layout altitude because it needs the                    ──
21124    // ── [`crate::layout::LayoutInvariants::exists`] filesystem       ──
21125    // ── oracle the pure typed-shape surface has no reference to —   ──
21126    // ── mirror of the peer M2 `:upgrade-from` per-instruction       ──
21127    // ── script-path existence probe that stayed at the layout       ──
21128    // ── altitude after the [`Caixa::validate_upgrade_from`] lift    ──
21129    // ── (d6801df) for the same reason.                              ──
21130
21131    #[test]
21132    fn validate_behavior_folds_arm_matches_gate() {
21133        // Fail-before-pass-after per-arm equivalence pin on the
21134        // present-slot arm: a fixture whose `:behavior` carries an
21135        // absolute-path `:on-init` (`"/etc/passwd"`, which
21136        // [`crate::BehaviorSpec::validate`] rejects through
21137        // [`crate::BehaviorError::AbsolutePath`]) surfaces the same
21138        // [`crate::BehaviorError`] byte-equal through both the
21139        // compound gate [`Caixa::validate_behavior`] and the standalone
21140        // [`crate::BehaviorSpec::validate`] gate on the same
21141        // `BehaviorSpec` value. Pins the fold — a silent regression
21142        // that de-folded the present-slot arm would surface here as a
21143        // mismatch between the two dispatches. Sibling in shape to the
21144        // peer per-arm equivalence pins the
21145        // [`Caixa::validate_limits`] (baa4688),
21146        // [`Caixa::validate_upgrade_from`] (d6801df),
21147        // [`crate::MeshPolicy::validate`],
21148        // [`crate::AplicacaoSpec::validate_contratos`], and
21149        // [`crate::SupervisorSpec::validate_children`] compound gates
21150        // each carry on their axes.
21151        use crate::BehaviorSpec;
21152        use std::path::PathBuf;
21153        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21154        let b = BehaviorSpec {
21155            on_init: Some(PathBuf::from("/etc/passwd")),
21156            ..Default::default()
21157        };
21158        c.behavior = Some(b.clone());
21159        let via_method = c.validate_behavior().unwrap_err();
21160        let via_standalone = b.validate().unwrap_err();
21161        assert_eq!(
21162            via_method, via_standalone,
21163            "Caixa::validate_behavior must surface the present-slot \
21164             arm's diagnostic byte-equal to the standalone \
21165             `BehaviorSpec::validate` on the same `BehaviorSpec` value"
21166        );
21167        assert!(
21168            matches!(via_method, crate::BehaviorError::AbsolutePath { .. }),
21169            "expected AbsolutePath on the absolute `:on-init` path, \
21170             got {via_method:?}"
21171        );
21172    }
21173
21174    #[test]
21175    fn validate_behavior_accepts_none() {
21176        // Positive control on the absent-slot arm (the fold's identity
21177        // element): a caixa without any `:behavior` block (the
21178        // canonical "no callback declared — the runtime falls back to
21179        // the wasm-engine's default per arm" author shape
21180        // [`crate::BehaviorSpec::is_empty`]'s per-slot `None` cascade
21181        // reads, and the shape the [`Caixa::template`] scaffold emits
21182        // by construction) passes the compound gate cleanly,
21183        // regardless of any per-slot defect a subsequent `Some(_)`
21184        // binding would surface. Pins the identity element of the fold
21185        // on the absent-slot side, matching the peer
21186        // `validate_limits_accepts_none` (baa4688) and
21187        // `validate_upgrade_from_accepts_empty_upgrade_from` (d6801df)
21188        // positive-control postures on the sibling M2 slots.
21189        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21190        assert!(
21191            c.behavior().is_none(),
21192            "template caixa must carry an absent :behavior — got {:?}",
21193            c.behavior()
21194        );
21195        c.validate_behavior()
21196            .expect("absent :behavior must pass the compound gate cleanly");
21197    }
21198
21199    #[test]
21200    fn validate_behavior_accepts_clean_fixture() {
21201        // Positive control on the present-slot arm: a caixa whose
21202        // `:behavior` is `Some(BehaviorSpec::default())` (all six
21203        // slots `None` — every slot absent under the outer `Some(_)`
21204        // binding, so every present-slot arm on
21205        // [`crate::BehaviorSpec::validate`] is vacuous) passes the
21206        // compound gate cleanly. A future tightening of any one arm
21207        // that surfaces a diagnostic on the all-`None` `BehaviorSpec`
21208        // would land here as a test failure first. Pins the
21209        // present-slot arm's accept-shape on the canonical
21210        // "declared-but-empty" author fixture the sibling
21211        // `empty_behavior_round_trip` peer already round-trips
21212        // (`caixa-core/src/behavior.rs` tests). Mirror of the peer
21213        // `validate_limits_accepts_clean_fixture` (baa4688)
21214        // positive-control posture on the sibling M2 `:limits` slot.
21215        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21216        c.behavior = Some(crate::BehaviorSpec::default());
21217        c.validate_behavior()
21218            .expect("Some(BehaviorSpec::default()) must pass the compound gate cleanly");
21219    }
21220
21221    // ── Caixa::validate_deps — compound per-Caixa entry gate on the ──
21222    // ── dep-graph axis: folds the two standalone validators         ──
21223    // ── (per-entry + within-list duplicate walk that this method    ──
21224    // ── opened on, cross-slot self-edge via                         ──
21225    // ── `crate::dep::validate_no_self_dep`) onto one substrate      ──
21226    // ── primitive. Byte-for-byte equivalent to the pre-fold         ──
21227    // ── two-block cascade at                                        ──
21228    // ── `crate::layout::StandardLayout::verify` under the same      ──
21229    // ── canonical dispatch order (per-entry → self-edge).           ──
21230
21231    #[test]
21232    fn validate_deps_folds_per_entry_arm_matches_gate() {
21233        // Fail-before-pass-after per-arm equivalence pin on the
21234        // per-entry + within-list duplicate axis: a fixture whose
21235        // `:deps` carries a per-entry-invalid `:versao` (`"^bad"`,
21236        // which [`crate::parse_requirement`] rejects) surfaces the
21237        // same [`crate::DepError`] through the compound gate
21238        // [`Caixa::validate_deps`] and the standalone per-entry walk
21239        // ([`Dep::validate`]) on the offending entry. Pins the
21240        // fold — a silent regression that de-folded the per-entry arm
21241        // would surface here as a mismatch between the two
21242        // dispatches. Sibling in shape to the peer
21243        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21244        // per-arm equivalence pin (d6801df) on the M2
21245        // `:upgrade-from` compound gate's per-entry arm, extended
21246        // here onto the universal-axis `:deps` compound gate's
21247        // per-entry arm.
21248        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21249        c.deps = vec![Dep::simple("d", "^bad")];
21250        let via_method = c.validate_deps().unwrap_err();
21251        let via_standalone = c.deps()[0].validate().unwrap_err();
21252        assert_eq!(
21253            via_method, via_standalone,
21254            "Caixa::validate_deps must surface the per-entry arm's \
21255             diagnostic byte-equal to the standalone \
21256             `Dep::validate` on the same offending entry",
21257        );
21258        assert!(
21259            matches!(
21260                via_method,
21261                DepError::VersaoInvalid { ref nome, .. } if nome == "d"
21262            ),
21263            "expected VersaoInvalid on the malformed :versao, got {via_method:?}",
21264        );
21265    }
21266
21267    #[test]
21268    fn validate_deps_folds_self_edge_arm_matches_gate() {
21269        // Per-arm equivalence pin on the cross-slot self-edge axis:
21270        // a fixture whose `:deps` lists the caixa's own `:nome`
21271        // (a self-dep, which
21272        // [`crate::dep::validate_no_self_dep`] rejects as a
21273        // structurally-invalid one-node cycle in the lacre closure's
21274        // dep-graph) surfaces the same [`crate::DepError::DepIsSelf`]
21275        // through both the compound gate and the standalone
21276        // [`crate::dep::validate_no_self_dep`] gate keyed off the
21277        // same `(deps, deps_dev, nome)` triple. Pins the fold's
21278        // second arm — reaching this arm through the compound gate
21279        // requires the per-entry + within-list duplicate walk to
21280        // pass first, which itself pins one cross-arm ordering step.
21281        // Sibling in shape to the peer
21282        // `validate_upgrade_from_folds_versao_arm_matches_gate` /
21283        // `_folds_behavior_arm_matches_gate` cross-slot equivalence
21284        // pins (d6801df) on the M2 `:upgrade-from` compound gate's
21285        // cross-slot arms.
21286        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21287        c.deps = vec![Dep::simple("demo", "^0.1")];
21288        let via_method = c.validate_deps().unwrap_err();
21289        let via_standalone =
21290            crate::dep::validate_no_self_dep(c.deps(), c.deps_dev(), c.nome()).unwrap_err();
21291        assert_eq!(
21292            via_method, via_standalone,
21293            "Caixa::validate_deps must surface the cross-slot \
21294             self-edge diagnostic byte-equal to the standalone \
21295             `crate::dep::validate_no_self_dep` on the same \
21296             (deps, deps_dev, nome) triple",
21297        );
21298        assert!(
21299            matches!(
21300                via_method,
21301                DepError::DepIsSelf { ref nome, list }
21302                    if nome == "demo" && list == crate::render::DEP_AUTHOR_KEY_DEPS
21303            ),
21304            "expected DepIsSelf carrying (nome=\"demo\", list=\":deps\"), got {via_method:?}",
21305        );
21306    }
21307
21308    #[test]
21309    fn validate_deps_per_entry_arm_fires_before_self_edge_arm() {
21310        // Cross-arm ordering pin between the two arms of the fold:
21311        // a fixture carrying BOTH a per-entry-invalid `:versao`
21312        // (`"^bad"` — [`crate::parse_requirement`] rejects the
21313        // requirement grammar) on a non-self-dep entry AND a
21314        // would-be self-edge violation on a second entry (the
21315        // caixa's own `:nome` "demo") surfaces the per-entry
21316        // diagnostic first through the compound gate. Sanity
21317        // assertion: the second entry alone under the same parent
21318        // `:nome` trips the self-edge arm on its own via the
21319        // standalone [`crate::dep::validate_no_self_dep`], so the
21320        // per-entry-first surfacing is a real ordering property,
21321        // not a case where the self-edge arm silently accepts the
21322        // fixture. Pins the pre-fold layout wire-up's canonical
21323        // dispatch order (per-entry + within-list duplicate →
21324        // self-edge) as a property of the substrate primitive
21325        // rather than a convention of the layout call site. Sibling
21326        // in shape to
21327        // `validate_upgrade_from_per_entry_arm_fires_before_versao_arm`
21328        // (d6801df) on the M2 `:upgrade-from` compound gate's
21329        // per-arm ordering property.
21330        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21331        c.deps = vec![
21332            Dep::simple("orquestra", "^bad"),
21333            Dep::simple("demo", "^0.1"),
21334        ];
21335        let err = c.validate_deps().unwrap_err();
21336        assert!(
21337            matches!(
21338                err,
21339                DepError::VersaoInvalid { ref nome, .. } if nome == "orquestra"
21340            ),
21341            "per-entry arm must fire before self-edge arm — expected \
21342             VersaoInvalid on \"orquestra\", got {err:?}",
21343        );
21344        // Sanity: the self-referential entry alone under the same
21345        // parent `:nome` trips the self-edge arm on its own — proves
21346        // the per-entry-first surfacing above is a real ordering
21347        // property, not a case where the self-edge arm silently
21348        // accepts the fixture.
21349        let sanity = crate::dep::validate_no_self_dep(&[Dep::simple("demo", "^0.1")], &[], "demo")
21350            .unwrap_err();
21351        assert!(
21352            matches!(sanity, DepError::DepIsSelf { ref nome, .. } if nome == "demo"),
21353            "sanity: the self-referential entry alone must trip the \
21354             self-edge arm — got {sanity:?}",
21355        );
21356    }
21357
21358    #[test]
21359    fn validate_deps_accepts_clean_fixture() {
21360        // Positive control: a well-formed dep-graph (one `:deps`
21361        // entry naming a non-self DNS-1123 nome + Cargo-shaped
21362        // requirement, one `:deps-dev` entry on a distinct non-self
21363        // nome) passes the compound gate cleanly. A future
21364        // tightening of either arm's accepted set surfaces here as
21365        // a test failure first. Mirrors the peer
21366        // `validate_upgrade_from_accepts_clean_fixture` positive-
21367        // control posture on the sibling per-Caixa compound gate.
21368        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21369        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
21370        c.deps_dev = vec![Dep::simple("caixa-lint", "^0.2")];
21371        c.validate_deps()
21372            .expect("clean fixture must pass the compound `:deps` gate");
21373    }
21374
21375    #[test]
21376    fn validate_deps_accepts_empty_deps_lists() {
21377        // Positive control on the empty-list arm: a caixa without
21378        // any `:deps` or `:deps-dev` entries (the default
21379        // `Vec::new()` `#[serde(default)]` folds an omitted slot
21380        // onto) passes the compound gate cleanly regardless of
21381        // `:nome` — both the per-entry walk and the self-edge walk
21382        // are vacuous on the empty entry list. Pins the identity
21383        // element of the fold on the empty-slot side, peer with the
21384        // `validate_upgrade_from_accepts_empty_upgrade_from` empty-
21385        // arm positive control (d6801df) on the sibling
21386        // `:upgrade-from` compound gate.
21387        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21388        assert!(
21389            c.deps().is_empty(),
21390            "template caixa must carry an empty :deps — got {:?}",
21391            c.deps(),
21392        );
21393        assert!(
21394            c.deps_dev().is_empty(),
21395            "template caixa must carry an empty :deps-dev — got {:?}",
21396            c.deps_dev(),
21397        );
21398        c.validate_deps()
21399            .expect("empty :deps / :deps-dev must pass the compound gate cleanly");
21400    }
21401
21402    // ── Caixa::validate_aplicacao_shape — compound per-Caixa gate ────────
21403
21404    /// Build a minimal well-formed Aplicacao fixture on top of the
21405    /// canonical template. Every arm of the compound gate then patches
21406    /// exactly one axis away from clean so its per-arm diagnostic
21407    /// surfaces without collateral noise from a peer slot.
21408    fn aplicacao_fixture(nome: &str) -> Caixa {
21409        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
21410        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21411        c.kind = CaixaKind::Aplicacao;
21412        c.bibliotecas = vec![];
21413        c.membros = vec![
21414            Membro {
21415                caixa: "checkout".into(),
21416                versao: "^0.1".into(),
21417            },
21418            Membro {
21419                caixa: "cart".into(),
21420                versao: "^0.1".into(),
21421            },
21422        ];
21423        // `:placement` defaults to `Replicated` with an empty
21424        // `:clusters` list which
21425        // [`crate::AplicacaoSpec::validate_placement`] refuses; every
21426        // per-strategy variant needs at least one named cluster (per
21427        // MESH-COMPOSITION §II.1). Pin a single-cluster `SingleNode`
21428        // placement so the typed-shape cascade passes cleanly and the
21429        // per-arm fixtures below can each patch exactly one axis.
21430        c.placement = Some(Placement {
21431            estrategia: PlacementStrategy::SingleNode,
21432            clusters: vec!["rio".into()],
21433            shard_key: None,
21434            affinity: None,
21435        });
21436        c
21437    }
21438
21439    #[test]
21440    fn validate_aplicacao_shape_folds_view_arm_matches_gate() {
21441        // Fail-before-pass-after per-arm equivalence pin on the
21442        // typed-shape cascade arm: a fixture whose typed
21443        // [`crate::AplicacaoSpec`] view fails
21444        // [`crate::AplicacaoSpec::validate`] (here — empty `:membros`,
21445        // which [`crate::AplicacaoSpec::validate_membros`] rejects as
21446        // [`crate::AplicacaoError::NoMembros`] at the first per-slot
21447        // gate) surfaces the same [`crate::AplicacaoError`] diagnostic
21448        // through both the compound gate
21449        // [`Caixa::validate_aplicacao_shape`] and the standalone
21450        // [`crate::AplicacaoSpec::validate`] on the same folded view.
21451        // Pins the fold — a silent regression that de-folded the
21452        // typed-shape arm would surface here as a mismatch between the
21453        // two dispatches. Sibling in shape to the peer
21454        // `validate_deps_folds_per_entry_arm_matches_gate` (b5dd55e) /
21455        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21456        // (d6801df) per-arm equivalence pins on the sibling per-slot
21457        // compound gates.
21458        let mut c = aplicacao_fixture("demo");
21459        c.membros = vec![];
21460        let via_method = c.validate_aplicacao_shape().unwrap_err();
21461        let via_standalone = c.aplicacao_view().unwrap().validate().unwrap_err();
21462        assert_eq!(
21463            via_method, via_standalone,
21464            "Caixa::validate_aplicacao_shape must surface the typed-\
21465             shape arm's diagnostic byte-equal to the standalone \
21466             `AplicacaoSpec::validate` on the same folded view",
21467        );
21468        assert!(
21469            matches!(via_method, crate::AplicacaoError::NoMembros),
21470            "expected NoMembros on the empty :membros, got {via_method:?}",
21471        );
21472    }
21473
21474    #[test]
21475    fn validate_aplicacao_shape_folds_self_membership_arm_matches_gate() {
21476        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21477        // fixture whose `:membros` names the Aplicacao's own `:nome`
21478        // (which [`crate::aplicacao::validate_no_self_membership`]
21479        // rejects as [`crate::AplicacaoError::MembroIsSelfAplicacao`],
21480        // a one-node lacre-closure recursion in the Aplicacao's
21481        // mesh-graph) surfaces the same
21482        // [`crate::AplicacaoError::MembroIsSelfAplicacao`] through both
21483        // the compound gate and the standalone
21484        // [`crate::aplicacao::validate_no_self_membership`] keyed off
21485        // the same `(membros, nome)` pair. Pins the fold's second arm
21486        // — reaching this arm through the compound gate requires the
21487        // typed-shape cascade to pass first, which itself pins one
21488        // cross-arm ordering step. Sibling in shape to the peer
21489        // `validate_deps_folds_self_edge_arm_matches_gate` (b5dd55e)
21490        // cross-slot equivalence pin on the sibling per-slot compound
21491        // gate.
21492        use crate::aplicacao::Membro;
21493        let mut c = aplicacao_fixture("demo");
21494        c.membros = vec![Membro {
21495            caixa: "demo".into(),
21496            versao: "^0.1".into(),
21497        }];
21498        let via_method = c.validate_aplicacao_shape().unwrap_err();
21499        let via_standalone =
21500            crate::aplicacao::validate_no_self_membership(c.membros(), c.nome()).unwrap_err();
21501        assert_eq!(
21502            via_method, via_standalone,
21503            "Caixa::validate_aplicacao_shape must surface the cross-\
21504             slot self-edge diagnostic byte-equal to the standalone \
21505             `aplicacao::validate_no_self_membership` on the same \
21506             (membros, nome) pair",
21507        );
21508        assert!(
21509            matches!(
21510                via_method,
21511                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa } if caixa == "demo"
21512            ),
21513            "expected MembroIsSelfAplicacao carrying (caixa=\"demo\"), \
21514             got {via_method:?}",
21515        );
21516    }
21517
21518    #[test]
21519    fn validate_aplicacao_shape_view_arm_fires_before_self_membership_arm() {
21520        // Cross-arm ordering pin between the two arms of the fold: a
21521        // fixture carrying BOTH a typed-shape violation (a `:contratos`
21522        // edge whose `:para` is not a declared member — rejected by
21523        // [`crate::AplicacaoSpec::validate_contratos`] as
21524        // [`crate::AplicacaoError::ContratoMemberMissing`]) AND a
21525        // would-be self-edge violation (a `:membros` entry naming the
21526        // caixa's own `:nome`) surfaces the typed-shape diagnostic
21527        // first through the compound gate. Sanity assertion: the
21528        // self-referential `:membros` entry alone under the same
21529        // parent `:nome` trips the self-edge arm on its own via the
21530        // standalone [`crate::aplicacao::validate_no_self_membership`],
21531        // so the typed-shape-first surfacing is a real ordering
21532        // property, not a case where the self-edge arm silently
21533        // accepts the fixture. Pins the pre-fold layout wire-up's
21534        // canonical dispatch order (typed-shape cascade → cross-slot
21535        // self-edge) as a property of the substrate primitive rather
21536        // than a convention of the layout call site. Sibling in shape
21537        // to `validate_deps_per_entry_arm_fires_before_self_edge_arm`
21538        // (b5dd55e) on the sibling per-slot compound gate's per-arm
21539        // ordering property.
21540        use crate::aplicacao::{Membro, WitContract};
21541        let mut c = aplicacao_fixture("demo");
21542        c.membros = vec![Membro {
21543            caixa: "demo".into(),
21544            versao: "^0.1".into(),
21545        }];
21546        c.contratos = vec![WitContract {
21547            de: "demo".into(),
21548            para: "orphan".into(),
21549            wit: "wasi:http/proxy".into(),
21550            endpoint: Some("/x".into()),
21551            subject: None,
21552            slot: None,
21553        }];
21554        let err = c.validate_aplicacao_shape().unwrap_err();
21555        assert!(
21556            matches!(
21557                err,
21558                crate::AplicacaoError::ContratoMemberMissing { ref caixa }
21559                    if caixa == "orphan"
21560            ),
21561            "typed-shape arm must fire before self-edge arm — expected \
21562             ContratoMemberMissing on \"orphan\", got {err:?}",
21563        );
21564        // Sanity: the self-referential `:membros` entry alone under
21565        // the same parent `:nome` trips the self-edge arm on its own
21566        // — proves the typed-shape-first surfacing above is a real
21567        // ordering property, not a case where the self-edge arm
21568        // silently accepts the fixture.
21569        let sanity = crate::aplicacao::validate_no_self_membership(
21570            &[Membro {
21571                caixa: "demo".into(),
21572                versao: "^0.1".into(),
21573            }],
21574            "demo",
21575        )
21576        .unwrap_err();
21577        assert!(
21578            matches!(
21579                sanity,
21580                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa }
21581                    if caixa == "demo"
21582            ),
21583            "sanity: the self-referential :membros entry alone must \
21584             trip the self-edge arm — got {sanity:?}",
21585        );
21586    }
21587
21588    #[test]
21589    fn validate_aplicacao_shape_accepts_non_aplicacao_kind() {
21590        // Positive control on the identity-element arm: every non-
21591        // Aplicacao kind passes the compound gate trivially — the
21592        // paired [`Caixa::aplicacao_view`] accessor returns `None`
21593        // off the Aplicacao arm (by construction, keyed on
21594        // `caixa.kind().is_aplicacao()`), so the fold short-circuits
21595        // to `Ok(())` without touching the mesh slots. Pins the
21596        // identity element on every non-Aplicacao kind — a future
21597        // refactor that made the mesh-slot cascade fire on the wrong
21598        // kind (say, on a `Servico` whose mesh slots happen to be
21599        // populated in a mis-authored manifest, which the peer
21600        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
21601        // coherence gate would refuse upstream anyway) surfaces here
21602        // as a test failure first. Peer with the
21603        // `validate_limits_accepts_none` / `validate_behavior_accepts_none`
21604        // identity-element pins on the sibling M2 `Option`-shaped
21605        // per-Caixa compound gates.
21606        for kind in [
21607            CaixaKind::Biblioteca,
21608            CaixaKind::Binario,
21609            CaixaKind::Servico,
21610            CaixaKind::Supervisor,
21611            CaixaKind::Acao,
21612        ] {
21613            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21614            c.kind = kind;
21615            assert!(
21616                c.aplicacao_view().is_none(),
21617                "aplicacao_view must return None off the Aplicacao arm \
21618                 for kind {kind:?}",
21619            );
21620            c.validate_aplicacao_shape().expect(
21621                "non-Aplicacao kinds must pass the compound gate as the fold's identity element",
21622            );
21623        }
21624    }
21625
21626    #[test]
21627    fn validate_aplicacao_shape_accepts_clean_fixture() {
21628        // Positive control: a well-formed Aplicacao (two DNS-1123
21629        // members with valid semver constraints, no `:contratos` /
21630        // `:entrada` / `:placement` / `:politicas` set — every
21631        // per-slot gate accepts the vacuous / omitted arm) passes the
21632        // compound gate cleanly. A future tightening of either arm's
21633        // accepted set surfaces here as a test failure first. Mirrors
21634        // the peer `validate_deps_accepts_clean_fixture` (b5dd55e) /
21635        // `validate_upgrade_from_accepts_clean_fixture` (d6801df)
21636        // positive-control postures on the sibling per-Caixa
21637        // compound gates.
21638        let c = aplicacao_fixture("demo");
21639        c.validate_aplicacao_shape()
21640            .expect("clean Aplicacao fixture must pass the compound gate");
21641    }
21642
21643    // ── Caixa::validate_supervisor_shape — compound per-Caixa gate ───────
21644
21645    /// Build a minimal well-formed Supervisor fixture on top of the
21646    /// canonical template. Every arm of the compound gate then patches
21647    /// exactly one axis away from clean so its per-arm diagnostic
21648    /// surfaces without collateral noise from a peer slot. Peer of
21649    /// [`aplicacao_fixture`] on the sibling per-Aplicacao compound
21650    /// gate's pin family.
21651    fn supervisor_fixture(nome: &str) -> Caixa {
21652        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
21653        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21654        c.kind = CaixaKind::Supervisor;
21655        // Supervisors don't run code — clear the biblioteca slot the
21656        // template seeds so the fold's per-arm diagnostics surface
21657        // without the peer `SupervisorOwnsCode` kind-coherence gate
21658        // firing upstream at the layout altitude.
21659        c.bibliotecas = vec![];
21660        // `:estrategia` defaults to `OneForOne` at the typed view level,
21661        // and `OneForOne` requires at least one `:children` entry — pin
21662        // a single-child `Permanent` worker so the typed-shape cascade
21663        // passes cleanly and the per-arm fixtures below can each patch
21664        // exactly one axis.
21665        c.estrategia = Some(RestartStrategy::OneForOne);
21666        c.children = vec![ChildSpec {
21667            caixa: "worker".into(),
21668            versao: "^0.1".into(),
21669            restart: RestartPolicy::Permanent,
21670        }];
21671        c
21672    }
21673
21674    #[test]
21675    fn validate_supervisor_shape_folds_view_arm_matches_gate() {
21676        // Fail-before-pass-after per-arm equivalence pin on the
21677        // typed-shape cascade arm: a fixture whose typed
21678        // [`crate::SupervisorSpec`] view fails
21679        // [`crate::SupervisorSpec::validate`] (here — a duplicate
21680        // `:children` `:caixa` entry, which
21681        // [`crate::SupervisorSpec::validate`]'s set-not-multiset gate
21682        // rejects as [`crate::SupervisorError::DuplicateChildCaixa`])
21683        // surfaces the same [`crate::SupervisorError`] diagnostic
21684        // through both the compound gate
21685        // [`Caixa::validate_supervisor_shape`] and the standalone
21686        // [`crate::SupervisorSpec::validate`] on the same folded view.
21687        // Pins the fold — a silent regression that de-folded the
21688        // typed-shape arm would surface here as a mismatch between the
21689        // two dispatches. Sibling in shape to the peer
21690        // `validate_aplicacao_shape_folds_view_arm_matches_gate`
21691        // (949a7a0) on the sibling per-Aplicacao compound gate.
21692        use crate::supervisor::{ChildSpec, RestartPolicy};
21693        let mut c = supervisor_fixture("demo");
21694        c.children = vec![
21695            ChildSpec {
21696                caixa: "worker".into(),
21697                versao: "^0.1".into(),
21698                restart: RestartPolicy::Permanent,
21699            },
21700            ChildSpec {
21701                caixa: "worker".into(),
21702                versao: "^0.1".into(),
21703                restart: RestartPolicy::Permanent,
21704            },
21705        ];
21706        let via_method = c.validate_supervisor_shape().unwrap_err();
21707        let via_standalone = c.supervisor_view().unwrap().validate().unwrap_err();
21708        assert_eq!(
21709            via_method, via_standalone,
21710            "Caixa::validate_supervisor_shape must surface the typed-\
21711             shape arm's diagnostic byte-equal to the standalone \
21712             `SupervisorSpec::validate` on the same folded view",
21713        );
21714        assert!(
21715            matches!(
21716                via_method,
21717                crate::SupervisorError::DuplicateChildCaixa { ref caixa }
21718                    if caixa == "worker"
21719            ),
21720            "expected DuplicateChildCaixa on the duplicate 'worker' \
21721             child, got {via_method:?}",
21722        );
21723    }
21724
21725    #[test]
21726    fn validate_supervisor_shape_folds_self_supervision_arm_matches_gate() {
21727        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21728        // fixture whose `:children :caixa` names the Supervisor's own
21729        // `:nome` (which
21730        // [`crate::supervisor::validate_no_self_supervision`] rejects
21731        // as [`crate::SupervisorError::ChildSupervisesSelf`], a
21732        // one-node reconciliation cycle in the supervisor's
21733        // supervision-tree) surfaces the same
21734        // [`crate::SupervisorError::ChildSupervisesSelf`] through both
21735        // the compound gate and the standalone
21736        // [`crate::supervisor::validate_no_self_supervision`] keyed
21737        // off the same `(children, nome)` pair. Pins the fold's
21738        // second arm — reaching this arm through the compound gate
21739        // requires the typed-shape cascade to pass first, which itself
21740        // pins one cross-arm ordering step. Sibling in shape to the
21741        // peer
21742        // `validate_aplicacao_shape_folds_self_membership_arm_matches_gate`
21743        // (949a7a0) cross-slot equivalence pin on the sibling
21744        // per-Aplicacao compound gate.
21745        use crate::supervisor::{ChildSpec, RestartPolicy};
21746        let mut c = supervisor_fixture("demo");
21747        c.children = vec![ChildSpec {
21748            caixa: "demo".into(),
21749            versao: "^0.1".into(),
21750            restart: RestartPolicy::Permanent,
21751        }];
21752        let via_method = c.validate_supervisor_shape().unwrap_err();
21753        let via_standalone =
21754            crate::supervisor::validate_no_self_supervision(c.children(), c.nome()).unwrap_err();
21755        assert_eq!(
21756            via_method, via_standalone,
21757            "Caixa::validate_supervisor_shape must surface the cross-\
21758             slot self-edge diagnostic byte-equal to the standalone \
21759             `supervisor::validate_no_self_supervision` on the same \
21760             (children, nome) pair",
21761        );
21762        assert!(
21763            matches!(
21764                via_method,
21765                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21766            ),
21767            "expected ChildSupervisesSelf carrying (caixa=\"demo\"), \
21768             got {via_method:?}",
21769        );
21770    }
21771
21772    #[test]
21773    fn validate_supervisor_shape_view_arm_fires_before_self_supervision_arm() {
21774        // Cross-arm ordering pin between the two arms of the fold: a
21775        // fixture carrying BOTH a typed-shape violation (a per-child
21776        // empty `:caixa` name — rejected by
21777        // [`crate::SupervisorSpec::validate`] as
21778        // [`crate::SupervisorError::EmptyChildName`]) AND a would-be
21779        // self-edge violation (a `:children` entry naming the
21780        // supervisor's own `:nome`) surfaces the typed-shape
21781        // diagnostic first through the compound gate. Sanity
21782        // assertion: the self-referential `:children` entry alone
21783        // under the same parent `:nome` trips the self-edge arm on
21784        // its own via the standalone
21785        // [`crate::supervisor::validate_no_self_supervision`], so the
21786        // typed-shape-first surfacing is a real ordering property, not
21787        // a case where the self-edge arm silently accepts the fixture.
21788        // Pins the pre-fold layout wire-up's canonical dispatch order
21789        // (typed-shape cascade → cross-slot self-edge) as a property
21790        // of the substrate primitive rather than a convention of the
21791        // layout call site. Sibling in shape to
21792        // `validate_aplicacao_shape_view_arm_fires_before_self_membership_arm`
21793        // (949a7a0) on the sibling per-Aplicacao compound gate.
21794        use crate::supervisor::{ChildSpec, RestartPolicy};
21795        let mut c = supervisor_fixture("demo");
21796        c.children = vec![
21797            ChildSpec {
21798                caixa: String::new(),
21799                versao: "^0.1".into(),
21800                restart: RestartPolicy::Permanent,
21801            },
21802            ChildSpec {
21803                caixa: "demo".into(),
21804                versao: "^0.1".into(),
21805                restart: RestartPolicy::Permanent,
21806            },
21807        ];
21808        let err = c.validate_supervisor_shape().unwrap_err();
21809        assert!(
21810            matches!(err, crate::SupervisorError::EmptyChildName),
21811            "typed-shape arm must fire before self-edge arm — expected \
21812             EmptyChildName on the empty :caixa child, got {err:?}",
21813        );
21814        // Sanity: the self-referential `:children` entry alone under
21815        // the same parent `:nome` trips the self-edge arm on its own
21816        // — proves the typed-shape-first surfacing above is a real
21817        // ordering property, not a case where the self-edge arm
21818        // silently accepts the fixture.
21819        let sanity = crate::supervisor::validate_no_self_supervision(
21820            &[ChildSpec {
21821                caixa: "demo".into(),
21822                versao: "^0.1".into(),
21823                restart: RestartPolicy::Permanent,
21824            }],
21825            "demo",
21826        )
21827        .unwrap_err();
21828        assert!(
21829            matches!(
21830                sanity,
21831                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21832            ),
21833            "sanity: the self-referential :children entry alone must \
21834             trip the self-edge arm — got {sanity:?}",
21835        );
21836    }
21837
21838    #[test]
21839    fn validate_supervisor_shape_accepts_non_supervisor_kind() {
21840        // Positive control on the identity-element arm: every non-
21841        // Supervisor kind passes the compound gate trivially — the
21842        // paired [`Caixa::supervisor_view`] accessor returns `None`
21843        // off the Supervisor arm (by construction, keyed on
21844        // `caixa.kind().is_supervisor()`), so the fold short-circuits
21845        // to `Ok(())` without touching the supervision-tree slots.
21846        // Pins the identity element on every non-Supervisor kind — a
21847        // future refactor that made the supervision-tree cascade fire
21848        // on the wrong kind (say, on a `Servico` whose supervision
21849        // slots happen to be populated in a mis-authored manifest,
21850        // which the peer
21851        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
21852        // kind-coherence gate would refuse upstream anyway) surfaces
21853        // here as a test failure first. Peer with the
21854        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
21855        // (949a7a0) / `validate_limits_accepts_none` /
21856        // `validate_behavior_accepts_none` identity-element pins on
21857        // the sibling per-Caixa compound gates.
21858        for kind in [
21859            CaixaKind::Biblioteca,
21860            CaixaKind::Binario,
21861            CaixaKind::Servico,
21862            CaixaKind::Aplicacao,
21863            CaixaKind::Acao,
21864        ] {
21865            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21866            c.kind = kind;
21867            assert!(
21868                c.supervisor_view().is_none(),
21869                "supervisor_view must return None off the Supervisor \
21870                 arm for kind {kind:?}",
21871            );
21872            c.validate_supervisor_shape().expect(
21873                "non-Supervisor kinds must pass the compound gate as the fold's identity element",
21874            );
21875        }
21876    }
21877
21878    #[test]
21879    fn validate_supervisor_shape_accepts_clean_fixture() {
21880        // Positive control: a well-formed Supervisor (single
21881        // DNS-1123-valid `Permanent` worker child under the
21882        // `OneForOne` strategy — the OTP MaxIntensity/Period defaults
21883        // accept the vacuous `:max-restarts` / `:restart-window`
21884        // arms) passes the compound gate cleanly. A future tightening
21885        // of either arm's accepted set surfaces here as a test
21886        // failure first. Mirrors the peer
21887        // `validate_aplicacao_shape_accepts_clean_fixture` (949a7a0)
21888        // positive-control posture on the sibling per-Caixa compound
21889        // gate.
21890        let c = supervisor_fixture("demo");
21891        c.validate_supervisor_shape()
21892            .expect("clean Supervisor fixture must pass the compound gate");
21893    }
21894
21895    // ── Caixa::validate_acao_shape — compound per-Caixa gate ─────────────
21896
21897    /// Build a minimal well-formed `:kind Acao` fixture with a valid
21898    /// two-node acyclic `:ci` slot. Every arm of the compound gate
21899    /// then patches exactly one axis away from clean so its per-arm
21900    /// diagnostic surfaces without collateral noise from a peer slot.
21901    /// Peer of [`supervisor_fixture`] / [`aplicacao_fixture`] on the
21902    /// sibling per-kind compound gates' pin families.
21903    fn acao_fixture(nome: &str) -> Caixa {
21904        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21905        c.kind = CaixaKind::Acao;
21906        // Acaos don't run code — clear the biblioteca slot the template
21907        // seeds so the compound gate's per-arm diagnostics surface
21908        // without the peer `AcaoOwnsCode` kind-coherence gate firing
21909        // upstream at the layout altitude.
21910        c.bibliotecas = vec![];
21911        c.ci = Some(canteiro_types::CiRun {
21912            workspace: "pleme-io".into(),
21913            repo: "caixa".into(),
21914            nodes: vec![
21915                canteiro_types::CiNode::new(
21916                    "build",
21917                    canteiro_types::EnvClass::None,
21918                    canteiro_types::ActionRef {
21919                        name: "build".into(),
21920                        command: "true".into(),
21921                        args: vec![],
21922                    },
21923                    vec![],
21924                ),
21925                canteiro_types::CiNode::new(
21926                    "test",
21927                    canteiro_types::EnvClass::None,
21928                    canteiro_types::ActionRef {
21929                        name: "test".into(),
21930                        command: "true".into(),
21931                        args: vec![],
21932                    },
21933                    vec!["build".into()],
21934                ),
21935            ],
21936        });
21937        c
21938    }
21939
21940    #[test]
21941    fn validate_acao_shape_folds_decompose_arm_matches_gate() {
21942        // Fail-before-pass-after per-arm equivalence pin on the
21943        // decompose axis: a fixture whose `:ci` slot fails
21944        // [`canteiro_types::decompose`] (here — a minimal two-node
21945        // cycle `a → b → a`, which the sibling
21946        // [`crate::render::decompose_ci`] wraps as
21947        // [`crate::CiDecomposeFailure`] carrying
21948        // [`canteiro_types::DecomposeError::Cycle`]) surfaces the same
21949        // [`crate::CiDecomposeFailure`] diagnostic through both the
21950        // compound gate [`Caixa::validate_acao_shape`] and the
21951        // standalone [`crate::render::decompose_ci`] on the same
21952        // `(caixa, ci)` fixture. Pins the fold — a silent regression
21953        // that de-folded the decompose arm would surface here as a
21954        // mismatch between the two dispatches. Sibling in shape to the
21955        // peer `validate_supervisor_shape_folds_view_arm_matches_gate`
21956        // / `validate_aplicacao_shape_folds_view_arm_matches_gate` on
21957        // the sibling per-kind compound gates.
21958        //
21959        // [`crate::CiDecomposeFailure`] does not derive `PartialEq`
21960        // (its `#[source]` carrier [`canteiro_types::DecomposeError`]
21961        // does, but the wrapper deliberately does not), so the two
21962        // dispatches are compared through their field pair
21963        // (`nome` + `source`) rather than through `assert_eq!` on the
21964        // wrapper itself — every field on the wrapper is thereby
21965        // pinned byte-equal without depending on an implementation
21966        // detail of `CiDecomposeFailure`'s derive set.
21967        let mut c = acao_fixture("demo");
21968        c.ci = Some(canteiro_types::CiRun {
21969            workspace: "pleme-io".into(),
21970            repo: "caixa".into(),
21971            nodes: vec![
21972                canteiro_types::CiNode::new(
21973                    "a",
21974                    canteiro_types::EnvClass::None,
21975                    canteiro_types::ActionRef {
21976                        name: "a".into(),
21977                        command: "true".into(),
21978                        args: vec![],
21979                    },
21980                    vec!["b".into()],
21981                ),
21982                canteiro_types::CiNode::new(
21983                    "b",
21984                    canteiro_types::EnvClass::None,
21985                    canteiro_types::ActionRef {
21986                        name: "b".into(),
21987                        command: "true".into(),
21988                        args: vec![],
21989                    },
21990                    vec!["a".into()],
21991                ),
21992            ],
21993        });
21994        let via_method = c.validate_acao_shape().unwrap_err();
21995        let via_standalone =
21996            crate::render::decompose_ci(&c, c.ci().expect("fixture has a :ci")).unwrap_err();
21997        assert_eq!(
21998            via_method.nome, via_standalone.nome,
21999            "Caixa::validate_acao_shape must surface the decompose \
22000             failure's `nome` byte-equal to the standalone \
22001             `decompose_ci` on the same (caixa, ci) fixture",
22002        );
22003        assert_eq!(
22004            via_method.source, via_standalone.source,
22005            "Caixa::validate_acao_shape must surface the decompose \
22006             failure's `source` byte-equal to the standalone \
22007             `decompose_ci` on the same (caixa, ci) fixture",
22008        );
22009        assert_eq!(
22010            via_method.source,
22011            canteiro_types::DecomposeError::Cycle,
22012            "expected the two-node cycle `a → b → a` to surface as \
22013             DecomposeError::Cycle, got {source:?}",
22014            source = via_method.source,
22015        );
22016    }
22017
22018    #[test]
22019    fn validate_acao_shape_folds_duplicate_node_arm_matches_gate() {
22020        // Per-arm equivalence pin on the `DuplicateNode` decompose
22021        // arm — the sibling of `Cycle` on the substrate's
22022        // `canteiro_types::DecomposeError` enumeration. A fixture
22023        // whose `:ci` slot carries two nodes sharing one name
22024        // surfaces the same [`crate::CiDecomposeFailure`] through
22025        // both dispatches, pinned by field pair. The three
22026        // decompose arms (`DuplicateNode` / `UnknownDep` / `Cycle`)
22027        // together enumerate every failure mode
22028        // [`canteiro_types::decompose`] refuses, so the per-arm
22029        // pins collectively cover the whole decompose axis.
22030        let mut c = acao_fixture("demo");
22031        c.ci = Some(canteiro_types::CiRun {
22032            workspace: "pleme-io".into(),
22033            repo: "caixa".into(),
22034            nodes: vec![
22035                canteiro_types::CiNode::new(
22036                    "twin",
22037                    canteiro_types::EnvClass::None,
22038                    canteiro_types::ActionRef {
22039                        name: "twin".into(),
22040                        command: "true".into(),
22041                        args: vec![],
22042                    },
22043                    vec![],
22044                ),
22045                canteiro_types::CiNode::new(
22046                    "twin",
22047                    canteiro_types::EnvClass::None,
22048                    canteiro_types::ActionRef {
22049                        name: "twin".into(),
22050                        command: "true".into(),
22051                        args: vec![],
22052                    },
22053                    vec![],
22054                ),
22055            ],
22056        });
22057        let via_method = c.validate_acao_shape().unwrap_err();
22058        assert_eq!(
22059            via_method.source,
22060            canteiro_types::DecomposeError::DuplicateNode("twin".into()),
22061            "expected DuplicateNode on the two-\"twin\"-name fixture, \
22062             got {source:?}",
22063            source = via_method.source,
22064        );
22065    }
22066
22067    #[test]
22068    fn validate_acao_shape_folds_unknown_dep_arm_matches_gate() {
22069        // Per-arm equivalence pin on the `UnknownDep` decompose arm —
22070        // the third and last arm on `canteiro_types::DecomposeError`
22071        // after `Cycle` and `DuplicateNode`. A fixture whose `:ci`
22072        // slot names a `deps` entry no declared node satisfies
22073        // surfaces the same [`crate::CiDecomposeFailure`] through
22074        // both dispatches. Pins the third decompose arm at the
22075        // compound gate.
22076        let mut c = acao_fixture("demo");
22077        c.ci = Some(canteiro_types::CiRun {
22078            workspace: "pleme-io".into(),
22079            repo: "caixa".into(),
22080            nodes: vec![canteiro_types::CiNode::new(
22081                "orphan",
22082                canteiro_types::EnvClass::None,
22083                canteiro_types::ActionRef {
22084                    name: "orphan".into(),
22085                    command: "true".into(),
22086                    args: vec![],
22087                },
22088                vec!["ghost".into()],
22089            )],
22090        });
22091        let via_method = c.validate_acao_shape().unwrap_err();
22092        assert_eq!(
22093            via_method.source,
22094            canteiro_types::DecomposeError::UnknownDep {
22095                node: "orphan".into(),
22096                dep: "ghost".into(),
22097            },
22098            "expected UnknownDep on the orphan-node-depends-on-ghost \
22099             fixture, got {source:?}",
22100            source = via_method.source,
22101        );
22102    }
22103
22104    #[test]
22105    fn validate_acao_shape_accepts_non_acao_kind() {
22106        // Positive control on the identity-element arm: every non-
22107        // Acao kind passes the compound gate trivially — the paired
22108        // `caixa.kind().is_acao()` guard short-circuits before the
22109        // decompose gate ever fires, so the fold returns `Ok(())`
22110        // without touching the `:ci` slot even when a non-Acao
22111        // fixture happens to declare one (the sibling
22112        // [`crate::LayoutError::CiOnNonAcao`] kind-coherence gate
22113        // catches that at the layout altitude anyway). Pins the
22114        // identity element on every non-Acao kind. Peer with the
22115        // `validate_supervisor_shape_accepts_non_supervisor_kind` /
22116        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
22117        // identity-element pins on the sibling per-Caixa compound
22118        // gates.
22119        for kind in [
22120            CaixaKind::Biblioteca,
22121            CaixaKind::Binario,
22122            CaixaKind::Servico,
22123            CaixaKind::Supervisor,
22124            CaixaKind::Aplicacao,
22125        ] {
22126            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22127            c.kind = kind;
22128            c.validate_acao_shape().expect(
22129                "non-Acao kinds must pass the compound gate as the fold's identity element",
22130            );
22131        }
22132    }
22133
22134    #[test]
22135    fn validate_acao_shape_accepts_absent_ci_slot() {
22136        // Positive control on the second identity-element arm: a
22137        // `:kind Acao` caixa with `ci = None` passes the compound
22138        // gate trivially — the presence gate is the sibling axis
22139        // owned by [`crate::LayoutError::MissingCi`] /
22140        // [`crate::require_ci`] / [`crate::MissingCiSlot`], not by
22141        // the decompose gate. A caixa that carries no `:ci` slot
22142        // has no run to decompose, so the fold's `let Some(ci) = …
22143        // else { return Ok(()) }` arm short-circuits before the
22144        // decompose gate fires. Pins that the two axes stay
22145        // separately diagnosable at the layout altitude — a future
22146        // regression that collapsed the presence gate onto the
22147        // shape gate here would land a
22148        // [`crate::CiDecomposeFailure`] on the wrong axis and
22149        // surface an off-target diagnostic at `feira build` time.
22150        let mut c = acao_fixture("demo");
22151        c.ci = None;
22152        c.validate_acao_shape().expect(
22153            "an :kind Acao caixa with absent :ci must pass the compound gate — \
22154             the presence gate is layout's MissingCi axis, not the decompose gate",
22155        );
22156    }
22157
22158    #[test]
22159    fn validate_acao_shape_accepts_clean_fixture() {
22160        // Positive control: a well-formed Acao (a two-node acyclic
22161        // `:ci` run with `test` depending on `build`) passes the
22162        // compound gate cleanly. A future tightening of the
22163        // decompose gate's accepted set surfaces here as a test
22164        // failure first. Mirrors the peer
22165        // `validate_supervisor_shape_accepts_clean_fixture` /
22166        // `validate_aplicacao_shape_accepts_clean_fixture`
22167        // positive-control posture on the sibling per-Caixa
22168        // compound gates.
22169        let c = acao_fixture("demo");
22170        c.validate_acao_shape()
22171            .expect("clean Acao fixture must pass the compound gate");
22172    }
22173
22174    fn bare_servico_fixture(nome: &str) -> Caixa {
22175        // A minimal Servico caixa with no code and no typed slots —
22176        // the cross-family fold's identity element on every arm.
22177        // Clears the biblioteca slot the template seeds so the
22178        // per-arm patches below can each add exactly one typed slot
22179        // without a peer `ServicoOwnsCode` / layout-side kind-gate
22180        // firing upstream.
22181        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
22182        c.kind = CaixaKind::Servico;
22183        c.bibliotecas = vec![];
22184        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22185        c
22186    }
22187
22188    #[test]
22189    fn validate_kind_slot_coherence_folds_mesh_arm_matches_gate() {
22190        // Fail-before-pass-after per-arm equivalence pin on the M3
22191        // mesh-slot arm of the cross-family kind-coherence fold: a
22192        // non-Aplicacao caixa carrying a declared M3 mesh slot (here
22193        // a `:kind Servico` fixture with a single `:membros` entry —
22194        // the smallest possible M3 slot declaration on a foreign
22195        // kind) surfaces the same
22196        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] variant
22197        // through both the compound gate
22198        // [`Caixa::validate_kind_slot_coherence`] and the standalone
22199        // constructor [`crate::LayoutError::mesh_slots_on_non_aplicacao`]
22200        // dispatched on the same `declared_mesh_slots` list. Pins
22201        // the fold — a silent regression that de-folded the mesh
22202        // arm would surface here as a mismatch between the two
22203        // dispatches. Sibling in shape to the peer
22204        // `validate_aplicacao_shape_folds_view_arm_matches_gate` /
22205        // `validate_supervisor_shape_folds_view_arm_matches_gate` /
22206        // `validate_acao_shape_folds_decompose_arm_matches_gate`
22207        // per-arm equivalence pins on the sibling per-kind compound
22208        // gates.
22209        use crate::aplicacao::Membro;
22210        let mut c = bare_servico_fixture("demo");
22211        c.membros = vec![Membro {
22212            caixa: "cart".into(),
22213            versao: "^0.1".into(),
22214        }];
22215        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22216        let via_standalone =
22217            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22218        assert_eq!(
22219            via_method, via_standalone,
22220            "Caixa::validate_kind_slot_coherence must surface the M3 \
22221             mesh-slot arm's diagnostic byte-equal to the standalone \
22222             LayoutError::mesh_slots_on_non_aplicacao ctor on the same \
22223             declared_mesh_slots list",
22224        );
22225    }
22226
22227    #[test]
22228    fn validate_kind_slot_coherence_folds_supervisor_arm_matches_gate() {
22229        // Per-arm equivalence pin on the supervisor-tree arm — the
22230        // sibling of the mesh arm on the cross-family fold. A
22231        // non-Supervisor caixa carrying a declared supervisor slot
22232        // (a `:kind Servico` fixture with `:estrategia` set — the
22233        // smallest possible supervisor slot declaration on a
22234        // foreign kind) surfaces the same
22235        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
22236        // variant through both dispatches, pinned by field pair
22237        // through `PartialEq`.
22238        use crate::supervisor::RestartStrategy;
22239        let mut c = bare_servico_fixture("demo");
22240        c.estrategia = Some(RestartStrategy::OneForOne);
22241        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22242        let via_standalone = crate::LayoutError::supervisor_slots_on_non_supervisor(
22243            &c,
22244            c.declared_supervisor_slots(),
22245        );
22246        assert_eq!(
22247            via_method, via_standalone,
22248            "Caixa::validate_kind_slot_coherence must surface the \
22249             supervisor-tree arm's diagnostic byte-equal to the \
22250             standalone LayoutError::supervisor_slots_on_non_supervisor \
22251             ctor on the same declared_supervisor_slots list",
22252        );
22253    }
22254
22255    #[test]
22256    fn validate_kind_slot_coherence_folds_servico_arm_matches_gate() {
22257        // Per-arm equivalence pin on the M2 Servico-runtime arm —
22258        // the third and last arm on the cross-family fold. A
22259        // non-Servico caixa carrying a declared M2 slot (a `:kind
22260        // Biblioteca` fixture with `:limits` set — the smallest
22261        // possible M2 slot declaration on a foreign kind) surfaces
22262        // the same [`crate::LayoutError::ServicoSlotsOnNonServico`]
22263        // variant through both dispatches. The three arms together
22264        // enumerate every typed-slot family the substrate carries
22265        // whose "declared but ignored" footgun is gated at the
22266        // layout altitude by a `{ caixa, kind, slots }` wrap variant,
22267        // so the per-arm pins collectively cover the whole
22268        // cross-family kind-coherence axis.
22269        use crate::limits::LimitsSpec;
22270        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22271        c.kind = CaixaKind::Biblioteca;
22272        c.limits = Some(LimitsSpec {
22273            memory: Some(64 * 1024 * 1024),
22274            fuel: None,
22275            wall_clock: None,
22276            cpu: None,
22277        });
22278        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22279        let via_standalone =
22280            crate::LayoutError::servico_slots_on_non_servico(&c, c.declared_servico_slots());
22281        assert_eq!(
22282            via_method, via_standalone,
22283            "Caixa::validate_kind_slot_coherence must surface the M2 \
22284             Servico-runtime arm's diagnostic byte-equal to the \
22285             standalone LayoutError::servico_slots_on_non_servico ctor \
22286             on the same declared_servico_slots list",
22287        );
22288    }
22289
22290    #[test]
22291    fn validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm() {
22292        // Cross-arm ordering pin between the first two arms of the
22293        // fold: a fixture carrying BOTH a declared M3 mesh slot
22294        // (`:membros`) AND a declared supervisor-tree slot
22295        // (`:estrategia`) on a foreign kind (a `:kind Servico` here —
22296        // foreign to both the Aplicacao arm and the Supervisor arm)
22297        // surfaces the M3 mesh diagnostic first through the compound
22298        // gate. Pins the pre-fold layout wire-up's canonical
22299        // diagnostic sequence (mesh → supervisor → servico) as a
22300        // property of the substrate primitive rather than a
22301        // convention of the layout call site. A silent reordering
22302        // regression at the primitive would surface here as a
22303        // wrong-variant match before landing at a downstream
22304        // consumer's diagnostic-ordering expectation.
22305        use crate::aplicacao::Membro;
22306        use crate::supervisor::RestartStrategy;
22307        let mut c = bare_servico_fixture("demo");
22308        c.membros = vec![Membro {
22309            caixa: "cart".into(),
22310            versao: "^0.1".into(),
22311        }];
22312        c.estrategia = Some(RestartStrategy::OneForOne);
22313        let err = c.validate_kind_slot_coherence().unwrap_err();
22314        assert!(
22315            matches!(err, crate::LayoutError::MeshSlotsOnNonAplicacao { .. }),
22316            "expected MeshSlotsOnNonAplicacao to fire before \
22317             SupervisorSlotsOnNonSupervisor under the canonical \
22318             mesh → supervisor → servico order, got {err:?}",
22319        );
22320    }
22321
22322    #[test]
22323    fn validate_kind_slot_coherence_supervisor_arm_fires_before_servico_arm() {
22324        // Cross-arm ordering pin between the second and third arms
22325        // of the fold: a fixture carrying BOTH a declared
22326        // supervisor-tree slot (`:estrategia`) AND a declared M2 slot
22327        // (`:limits`) on a kind foreign to both (a `:kind Biblioteca`
22328        // here — foreign to both the Supervisor and the Servico
22329        // arms) surfaces the supervisor-tree diagnostic first
22330        // through the compound gate. Together with the peer
22331        // `_mesh_arm_fires_before_supervisor_arm` pin above this
22332        // pins the whole three-arm canonical order (mesh →
22333        // supervisor → servico) at the substrate primitive.
22334        use crate::limits::LimitsSpec;
22335        use crate::supervisor::RestartStrategy;
22336        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22337        c.kind = CaixaKind::Biblioteca;
22338        c.estrategia = Some(RestartStrategy::OneForOne);
22339        c.limits = Some(LimitsSpec {
22340            memory: Some(64 * 1024 * 1024),
22341            fuel: None,
22342            wall_clock: None,
22343            cpu: None,
22344        });
22345        let err = c.validate_kind_slot_coherence().unwrap_err();
22346        assert!(
22347            matches!(
22348                err,
22349                crate::LayoutError::SupervisorSlotsOnNonSupervisor { .. }
22350            ),
22351            "expected SupervisorSlotsOnNonSupervisor to fire before \
22352             ServicoSlotsOnNonServico under the canonical mesh → \
22353             supervisor → servico order, got {err:?}",
22354        );
22355    }
22356
22357    #[test]
22358    fn validate_kind_slot_coherence_accepts_owner_kind_on_every_arm() {
22359        // Positive control on the identity-element arm: the owner
22360        // kind of each typed-slot family passes the compound gate
22361        // even when it declares the full slot set that family owns.
22362        // Aplicacao with `:membros` populated passes the mesh arm;
22363        // Supervisor with `:estrategia` populated passes the
22364        // supervisor arm; Servico with `:limits` populated passes
22365        // the servico arm. Pins the fold's identity element on
22366        // every owner kind — a silent regression that dropped the
22367        // paired `!kind().is_<owner>()` short-circuit guard would
22368        // surface here as a false-positive rejection of every
22369        // native-slot declaration. Peer with the
22370        // `validate_<kind>_shape_accepts_non_<kind>_kind` identity-
22371        // element pins on the sibling per-Caixa compound gates.
22372        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
22373        use crate::limits::LimitsSpec;
22374        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
22375
22376        let mut apli = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22377        apli.kind = CaixaKind::Aplicacao;
22378        apli.bibliotecas = vec![];
22379        apli.membros = vec![Membro {
22380            caixa: "cart".into(),
22381            versao: "^0.1".into(),
22382        }];
22383        apli.placement = Some(Placement {
22384            estrategia: PlacementStrategy::SingleNode,
22385            clusters: vec!["rio".into()],
22386            shard_key: None,
22387            affinity: None,
22388        });
22389        apli.validate_kind_slot_coherence().expect(
22390            "an :kind Aplicacao caixa with declared M3 mesh slots must \
22391             pass the compound gate — Aplicacao is the mesh-slot family's \
22392             owner kind and the fold's identity element on that arm",
22393        );
22394
22395        let mut sup = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22396        sup.kind = CaixaKind::Supervisor;
22397        sup.bibliotecas = vec![];
22398        sup.estrategia = Some(RestartStrategy::OneForOne);
22399        sup.children = vec![ChildSpec {
22400            caixa: "worker".into(),
22401            versao: "^0.1".into(),
22402            restart: RestartPolicy::Permanent,
22403        }];
22404        sup.validate_kind_slot_coherence().expect(
22405            "an :kind Supervisor caixa with declared supervisor-tree slots \
22406             must pass the compound gate — Supervisor is the \
22407             supervisor-slot family's owner kind and the fold's identity \
22408             element on that arm",
22409        );
22410
22411        let mut svc = bare_servico_fixture("svc");
22412        svc.limits = Some(LimitsSpec {
22413            memory: Some(64 * 1024 * 1024),
22414            fuel: None,
22415            wall_clock: None,
22416            cpu: None,
22417        });
22418        svc.validate_kind_slot_coherence().expect(
22419            "an :kind Servico caixa with declared M2 slots must pass the \
22420             compound gate — Servico is the M2-slot family's owner kind \
22421             and the fold's identity element on that arm",
22422        );
22423    }
22424
22425    #[test]
22426    fn validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind() {
22427        // Positive control on the second identity-element arm: a
22428        // bare caixa (no declared typed slots) passes the compound
22429        // gate on every kind. Pins the fold's identity element on
22430        // the empty-slot axis — the paired `Vec::is_empty` short-
22431        // circuit guard fires before the wrap dispatch on all three
22432        // arms, so a bare caixa of any kind surfaces no diagnostic.
22433        // A silent regression that dropped the emptiness guard
22434        // would surface here as a false-positive rejection of every
22435        // no-slot caixa across the whole kind axis.
22436        for kind in CaixaKind::ALL {
22437            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22438            c.kind = *kind;
22439            c.bibliotecas = vec![];
22440            c.validate_kind_slot_coherence().unwrap_or_else(|err| {
22441                panic!(
22442                    "a bare :kind {kind:?} caixa (no declared typed slots) \
22443                     must pass the compound gate — the fold's identity \
22444                     element on the empty-slot axis is the paired \
22445                     Vec::is_empty short-circuit guard, got {err:?}",
22446                )
22447            });
22448        }
22449    }
22450
22451    #[test]
22452    fn run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator() {
22453        // Fail-before-pass-after identity-element pin on the owner-kind
22454        // arm of the substrate primitive: on a caixa whose kind IS the
22455        // owner of the family named by `is_owner`, the primitive
22456        // short-circuits before dispatching `accumulator` — pinned here
22457        // by a poison-pill accumulator that panics on call. If a
22458        // regression drops the `is_owner` short-circuit and always
22459        // invokes the accumulator, the poison panic surfaces here
22460        // rather than a spurious pass. Byte-equal to the pre-lift
22461        // `if !self.kind().is_<owner>() { … }` outer guard's
22462        // short-circuit at the pre-fold layout call site.
22463        let c = bare_servico_fixture("demo");
22464        c.run_kind_owned_slot_family_gate(
22465            CaixaKind::is_servico,
22466            |_| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking accumulator on the owner kind"),
22467            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on the owner kind"),
22468        )
22469        .expect(
22470            "the owner kind of a slot family must pass the substrate \
22471             primitive as the fold's identity element on the outer \
22472             is_owner guard, without invoking accumulator or wrap",
22473        );
22474    }
22475
22476    #[test]
22477    fn run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap() {
22478        // Fail-before-pass-after identity-element pin on the empty-
22479        // accumulator arm: on a non-owner kind whose per-family
22480        // accumulator yields no declared slot, the primitive short-
22481        // circuits before dispatching `wrap` — pinned here by a
22482        // poison-pill wrap that panics on call. Byte-equal to the
22483        // pre-lift `if !<slots>.is_empty() { … }` inner emptiness
22484        // guard's short-circuit at the pre-fold layout call site.
22485        let c = bare_servico_fixture("demo");
22486        c.run_kind_owned_slot_family_gate(
22487            CaixaKind::is_aplicacao,
22488            Caixa::declared_mesh_slots,
22489            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on an empty accumulator"),
22490        )
22491        .expect(
22492            "a non-owner kind carrying no declared slot in the family \
22493             must pass the substrate primitive as the fold's identity \
22494             element on the inner emptiness guard, without invoking \
22495             wrap",
22496        );
22497    }
22498
22499    #[test]
22500    fn run_kind_owned_slot_family_gate_non_owner_non_empty_wraps_verbatim() {
22501        // Equivalence pin on the refusal arm: on a non-owner kind
22502        // whose accumulator yields a non-empty slot list, the primitive
22503        // returns the caller-supplied wrap byte-equal to the direct
22504        // ctor dispatch on the same `(caixa, slots)` pair. Pins the
22505        // three-argument route through — `is_owner` fires false, the
22506        // accumulator produces the slot list, and the wrap ctor
22507        // receives verbatim what a direct dispatch would receive.
22508        // Sibling of the peer per-arm equivalence pins on
22509        // [`Caixa::validate_kind_slot_coherence`].
22510        use crate::aplicacao::Membro;
22511        let mut c = bare_servico_fixture("demo");
22512        c.membros = vec![Membro {
22513            caixa: "cart".into(),
22514            versao: "^0.1".into(),
22515        }];
22516        let via_primitive = c
22517            .run_kind_owned_slot_family_gate(
22518                CaixaKind::is_aplicacao,
22519                Caixa::declared_mesh_slots,
22520                crate::LayoutError::mesh_slots_on_non_aplicacao,
22521            )
22522            .unwrap_err();
22523        let via_direct =
22524            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22525        assert_eq!(
22526            via_primitive, via_direct,
22527            "Caixa::run_kind_owned_slot_family_gate must route the \
22528             non-owner-kind + non-empty-accumulator arm through the \
22529             caller-supplied wrap byte-equal to the direct ctor \
22530             dispatch on the same (caixa, slots) pair",
22531        );
22532    }
22533
22534    #[test]
22535    fn validate_kind_slot_coherence_routes_each_arm_through_run_kind_owned_slot_family_gate() {
22536        // Cross-primitive routing pin: every arm of the compound gate
22537        // [`Caixa::validate_kind_slot_coherence`] routes through the
22538        // substrate primitive [`Caixa::run_kind_owned_slot_family_gate`]
22539        // on its `(is_owner, accumulator, wrap)` triple. A silent
22540        // regression that de-folded one arm and re-inlined the four-
22541        // line block would surface here as a mismatch between the
22542        // compound-gate error and the direct-primitive-dispatch error
22543        // on the same fixture. Sibling of the peer
22544        // `probe_declared_entries_routes_miss_arm_through_probe_declared_entry`
22545        // cross-primitive routing pin on the layout-pipeline
22546        // existence-probe axis.
22547        use crate::aplicacao::Membro;
22548        use crate::limits::LimitsSpec;
22549        use crate::supervisor::RestartStrategy;
22550
22551        // Mesh arm — non-Aplicacao carrying a declared M3 slot.
22552        let mut mesh = bare_servico_fixture("demo");
22553        mesh.membros = vec![Membro {
22554            caixa: "cart".into(),
22555            versao: "^0.1".into(),
22556        }];
22557        let via_compound = mesh.validate_kind_slot_coherence().unwrap_err();
22558        let via_primitive = mesh
22559            .run_kind_owned_slot_family_gate(
22560                CaixaKind::is_aplicacao,
22561                Caixa::declared_mesh_slots,
22562                crate::LayoutError::mesh_slots_on_non_aplicacao,
22563            )
22564            .unwrap_err();
22565        assert_eq!(
22566            via_compound, via_primitive,
22567            "validate_kind_slot_coherence's mesh arm must route \
22568             byte-equal through the run_kind_owned_slot_family_gate \
22569             substrate primitive",
22570        );
22571
22572        // Supervisor arm — non-Supervisor carrying a declared
22573        // supervisor-tree slot on a kind foreign to both the Aplicacao
22574        // arm and this one.
22575        let mut sup = bare_servico_fixture("demo");
22576        sup.estrategia = Some(RestartStrategy::OneForOne);
22577        let via_compound = sup.validate_kind_slot_coherence().unwrap_err();
22578        let via_primitive = sup
22579            .run_kind_owned_slot_family_gate(
22580                CaixaKind::is_supervisor,
22581                Caixa::declared_supervisor_slots,
22582                crate::LayoutError::supervisor_slots_on_non_supervisor,
22583            )
22584            .unwrap_err();
22585        assert_eq!(
22586            via_compound, via_primitive,
22587            "validate_kind_slot_coherence's supervisor arm must route \
22588             byte-equal through the run_kind_owned_slot_family_gate \
22589             substrate primitive",
22590        );
22591
22592        // Servico arm — non-Servico carrying a declared M2 slot on a
22593        // kind foreign to every prior arm (Biblioteca — foreign to
22594        // both the Aplicacao mesh arm and the Supervisor supervisor
22595        // arm and the Servico M2 arm).
22596        let mut svc = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22597        svc.kind = CaixaKind::Biblioteca;
22598        svc.limits = Some(LimitsSpec {
22599            memory: Some(64 * 1024 * 1024),
22600            fuel: None,
22601            wall_clock: None,
22602            cpu: None,
22603        });
22604        let via_compound = svc.validate_kind_slot_coherence().unwrap_err();
22605        let via_primitive = svc
22606            .run_kind_owned_slot_family_gate(
22607                CaixaKind::is_servico,
22608                Caixa::declared_servico_slots,
22609                crate::LayoutError::servico_slots_on_non_servico,
22610            )
22611            .unwrap_err();
22612        assert_eq!(
22613            via_compound, via_primitive,
22614            "validate_kind_slot_coherence's servico arm must route \
22615             byte-equal through the run_kind_owned_slot_family_gate \
22616             substrate primitive",
22617        );
22618    }
22619
22620    #[test]
22621    fn validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate() {
22622        // Fail-before-pass-after per-arm equivalence pin on the
22623        // Supervisor no-code arm of the reciprocal code-surface
22624        // fold: a `:kind Supervisor` caixa carrying a declared
22625        // `:bibliotecas` entry (the smallest possible code-surface
22626        // declaration on a no-code kind) surfaces the same
22627        // [`crate::LayoutError::SupervisorOwnsCode`] variant
22628        // through both the compound gate
22629        // [`Caixa::validate_no_code_kind_coherence`] and the
22630        // standalone constructor
22631        // [`crate::LayoutError::supervisor_owns_code`]. Pins the
22632        // fold — a silent regression that de-folded the Supervisor
22633        // arm would surface here as a mismatch between the two
22634        // dispatches. Sibling in shape to the peer
22635        // `validate_kind_slot_coherence_folds_supervisor_arm_matches_gate`
22636        // per-arm equivalence pin on the cross-family
22637        // typed-slot-coherence fold.
22638        let mut c = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22639        c.kind = CaixaKind::Supervisor;
22640        c.bibliotecas = vec!["lib/sup.lisp".into()];
22641        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22642        let via_standalone = crate::LayoutError::supervisor_owns_code(&c);
22643        assert_eq!(
22644            via_method, via_standalone,
22645            "Caixa::validate_no_code_kind_coherence must surface the \
22646             Supervisor arm's diagnostic byte-equal to the standalone \
22647             LayoutError::supervisor_owns_code ctor",
22648        );
22649    }
22650
22651    #[test]
22652    fn validate_no_code_kind_coherence_folds_aplicacao_arm_matches_gate() {
22653        // Per-arm equivalence pin on the Aplicacao no-code arm —
22654        // the sibling of the Supervisor arm on the code-surface
22655        // fold. A `:kind Aplicacao` caixa carrying a declared
22656        // `:exe` entry surfaces the same
22657        // [`crate::LayoutError::AplicacaoOwnsCode`] variant through
22658        // both dispatches. Uses the `:exe` code-surface axis (a
22659        // second axis distinct from the Supervisor arm's
22660        // `:bibliotecas` fixture) so the three per-arm pins
22661        // collectively exercise every arm of the `has_code`
22662        // disjunction (`:bibliotecas || :exe || :servicos`).
22663        let mut c = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22664        c.kind = CaixaKind::Aplicacao;
22665        c.bibliotecas = vec![];
22666        c.exe = vec!["exe/app".into()];
22667        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22668        let via_standalone = crate::LayoutError::aplicacao_owns_code(&c);
22669        assert_eq!(
22670            via_method, via_standalone,
22671            "Caixa::validate_no_code_kind_coherence must surface the \
22672             Aplicacao arm's diagnostic byte-equal to the standalone \
22673             LayoutError::aplicacao_owns_code ctor",
22674        );
22675    }
22676
22677    #[test]
22678    fn validate_no_code_kind_coherence_folds_acao_arm_matches_gate() {
22679        // Per-arm equivalence pin on the Acao no-code arm — the
22680        // third and last arm on the code-surface fold. A `:kind
22681        // Acao` caixa carrying a declared `:servicos` entry
22682        // surfaces the same [`crate::LayoutError::AcaoOwnsCode`]
22683        // variant through both dispatches. Uses the `:servicos`
22684        // code-surface axis (the third distinct axis of the
22685        // `has_code` disjunction) so the three per-arm pins
22686        // collectively cover every arm of the code-surface
22687        // disjunction plus every no-code kind of the arm
22688        // dispatch.
22689        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
22690        c.kind = CaixaKind::Acao;
22691        c.bibliotecas = vec![];
22692        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22693        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22694        let via_standalone = crate::LayoutError::acao_owns_code(&c);
22695        assert_eq!(
22696            via_method, via_standalone,
22697            "Caixa::validate_no_code_kind_coherence must surface the \
22698             Acao arm's diagnostic byte-equal to the standalone \
22699             LayoutError::acao_owns_code ctor",
22700        );
22701    }
22702
22703    #[test]
22704    fn validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis() {
22705        // Positive control on the code-owning-kind identity
22706        // element: each of the three code-owning kinds
22707        // (`Biblioteca` owning `:bibliotecas`, `Binario` owning
22708        // `:exe`, `Servico` owning `:servicos`) passes the
22709        // compound gate cleanly when it declares its native code
22710        // surface. Pins the fold's second identity element — the
22711        // paired per-arm `is_<no-code-kind>()` short-circuit
22712        // fires on every code-owning kind, so a caixa with any
22713        // native code declaration on its owner kind surfaces no
22714        // diagnostic. A silent regression that dropped the paired
22715        // `is_<no-code-kind>()` short-circuit guard on any arm
22716        // would surface here as a false-positive rejection of the
22717        // corresponding owner kind. Peer with the
22718        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22719        // identity-element pin on the sibling cross-family fold.
22720        let mut bib = Caixa::from_lisp(&Caixa::template("bib")).unwrap();
22721        bib.kind = CaixaKind::Biblioteca;
22722        bib.bibliotecas = vec!["lib/bib.lisp".into()];
22723        bib.validate_no_code_kind_coherence().expect(
22724            "a :kind Biblioteca caixa with declared :bibliotecas must pass \
22725             the compound gate — Biblioteca owns the :bibliotecas code surface",
22726        );
22727
22728        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22729        bin.kind = CaixaKind::Binario;
22730        bin.bibliotecas = vec![];
22731        bin.exe = vec!["exe/bin".into()];
22732        bin.validate_no_code_kind_coherence().expect(
22733            "a :kind Binario caixa with declared :exe must pass the compound \
22734             gate — Binario owns the :exe code surface",
22735        );
22736
22737        let svc = bare_servico_fixture("svc");
22738        svc.validate_no_code_kind_coherence().expect(
22739            "a :kind Servico caixa with declared :servicos must pass the \
22740             compound gate — Servico owns the :servicos code surface",
22741        );
22742    }
22743
22744    #[test]
22745    fn validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22746        // Positive control on the has-no-code identity element:
22747        // a bare caixa (no declared code) passes the compound
22748        // gate on every kind — including the three no-code kinds
22749        // that would otherwise fire an OwnsCode diagnostic. Pins
22750        // the fold's first identity element — the paired
22751        // `!has_code` short-circuit fires before every per-arm
22752        // wrap dispatch, so a bare caixa of any kind surfaces no
22753        // diagnostic. A silent regression that dropped the
22754        // has_code guard would surface here as a false-positive
22755        // rejection of every no-code kind that declares no code.
22756        // Peer with the
22757        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22758        // identity-element pin on the sibling cross-family fold.
22759        for kind in CaixaKind::ALL {
22760            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22761            c.kind = *kind;
22762            c.bibliotecas = vec![];
22763            c.exe = vec![];
22764            c.servicos = vec![];
22765            c.validate_no_code_kind_coherence().unwrap_or_else(|err| {
22766                panic!(
22767                    "a bare :kind {kind:?} caixa (no declared code) must pass \
22768                     the compound gate — the fold's first identity element is \
22769                     the paired !has_code short-circuit, got {err:?}",
22770                )
22771            });
22772        }
22773    }
22774
22775    #[test]
22776    fn validate_ci_kind_coherence_folds_arm_matches_gate() {
22777        // Fail-before-pass-after per-arm equivalence pin on the
22778        // `:ci`-on-non-`Acao` arm: a `:kind Biblioteca` caixa
22779        // (the smallest non-`Acao` kind) carrying a declared
22780        // `:ci` slot surfaces the same
22781        // [`crate::LayoutError::CiOnNonAcao`] variant through the
22782        // compound gate [`Caixa::validate_ci_kind_coherence`] and
22783        // an inlined struct-literal wrap carrying `caixa.nome()`
22784        // + `caixa.kind()` verbatim. Pins the fold — a silent
22785        // regression that de-folded the arm would surface here as
22786        // a mismatch between the two dispatches. Sibling in shape
22787        // to the peer
22788        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22789        // per-arm equivalence pin on the reciprocal
22790        // code-surface fold.
22791        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22792        c.kind = CaixaKind::Biblioteca;
22793        c.ci = Some(canteiro_types::CiRun {
22794            workspace: "pleme-io".into(),
22795            repo: "caixa".into(),
22796            nodes: vec![],
22797        });
22798        let via_method = c.validate_ci_kind_coherence().unwrap_err();
22799        let via_standalone = crate::LayoutError::CiOnNonAcao {
22800            caixa: c.nome().to_string(),
22801            kind: c.kind(),
22802        };
22803        assert_eq!(
22804            via_method, via_standalone,
22805            "Caixa::validate_ci_kind_coherence must surface the \
22806             :ci-on-non-Acao arm's diagnostic byte-equal to a \
22807             LayoutError::CiOnNonAcao struct literal carrying the \
22808             caixa's nome + kind",
22809        );
22810    }
22811
22812    #[test]
22813    fn validate_ci_kind_coherence_fold_names_offending_kind_on_every_non_acao_kind() {
22814        // Exhaustive per-kind sweep on the non-`Acao` arm: for each
22815        // of the five non-`Acao` kinds
22816        // (`Biblioteca` / `Binario` / `Servico` / `Supervisor` /
22817        // `Aplicacao`), a caixa carrying a declared `:ci` slot
22818        // surfaces the [`crate::LayoutError::CiOnNonAcao`]
22819        // variant naming the offending kind verbatim. A silent
22820        // regression that mistyped one arm's kind-projection
22821        // (e.g. always threading `CaixaKind::Biblioteca` regardless
22822        // of the caixa's actual kind) would surface here as a
22823        // mismatch on every kind past the first. Peer of the
22824        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22825        // exhaustive-sweep pin on the sibling code-surface fold.
22826        for kind in CaixaKind::ALL {
22827            if kind.is_acao() {
22828                continue;
22829            }
22830            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22831            c.kind = *kind;
22832            c.ci = Some(canteiro_types::CiRun {
22833                workspace: "pleme-io".into(),
22834                repo: "caixa".into(),
22835                nodes: vec![],
22836            });
22837            let err = c.validate_ci_kind_coherence().unwrap_err();
22838            match err {
22839                crate::LayoutError::CiOnNonAcao {
22840                    caixa: got_caixa,
22841                    kind: got_kind,
22842                } => {
22843                    assert_eq!(
22844                        got_caixa,
22845                        c.nome(),
22846                        "CiOnNonAcao must name the offending caixa's nome verbatim on kind {kind:?}",
22847                    );
22848                    assert_eq!(
22849                        got_kind, *kind,
22850                        "CiOnNonAcao must name the offending kind verbatim on kind {kind:?}",
22851                    );
22852                }
22853                other => panic!(
22854                    "expected CiOnNonAcao on :kind {kind:?} with declared :ci, got {other:?}",
22855                ),
22856            }
22857        }
22858    }
22859
22860    #[test]
22861    fn validate_ci_kind_coherence_accepts_acao_on_every_ci_shape() {
22862        // Positive control on the owner-kind identity element: an
22863        // `:kind Acao` caixa passes the coherence gate cleanly on
22864        // every `:ci` shape — the arm's paired
22865        // `!kind().is_acao()` short-circuit fires before the
22866        // dispatch, so the fold surfaces no diagnostic even on
22867        // fixtures whose `:ci` would fail the peer
22868        // [`Self::validate_acao_shape`] decompose gate (a
22869        // duplicate-node fixture, an unknown-dep fixture, a
22870        // cyclic fixture). Pins the fold's first identity element
22871        // — a silent regression that dropped the paired
22872        // `!kind().is_acao()` short-circuit guard would surface
22873        // here as a false-positive rejection of every `Acao`
22874        // caixa. Peer with the
22875        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
22876        // identity-element pin on the sibling code-surface fold.
22877        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22878        c.kind = CaixaKind::Acao;
22879        c.bibliotecas = vec![];
22880        c.ci = Some(canteiro_types::CiRun {
22881            workspace: "pleme-io".into(),
22882            repo: "caixa".into(),
22883            nodes: vec![],
22884        });
22885        c.validate_ci_kind_coherence().expect(
22886            "a :kind Acao caixa with declared :ci must pass the compound \
22887             coherence gate — Acao is the :ci-owning kind (a malformed \
22888             :ci on Acao surfaces via validate_acao_shape's decompose gate, \
22889             not via this kind-coherence gate)",
22890        );
22891    }
22892
22893    #[test]
22894    fn validate_ci_kind_coherence_accepts_absent_ci_on_every_kind() {
22895        // Positive control on the absent-`:ci` identity element:
22896        // a caixa with `ci = None` passes the coherence gate on
22897        // every kind — including `Acao`, whose absent `:ci`
22898        // fails a separate presence gate ([`crate::LayoutError::MissingCi`])
22899        // downstream at the layout altitude, not this coherence
22900        // gate. Pins the fold's second identity element — the
22901        // paired `ci().is_some()` short-circuit fires before every
22902        // per-arm dispatch, so a caixa with no declared `:ci`
22903        // surfaces no coherence diagnostic. A silent regression
22904        // that dropped the paired `ci().is_some()` short-circuit
22905        // would surface here as a false-positive rejection on
22906        // every non-`Acao` kind. Peer with the
22907        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22908        // identity-element pin on the sibling code-surface fold.
22909        for kind in CaixaKind::ALL {
22910            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22911            c.kind = *kind;
22912            c.ci = None;
22913            c.validate_ci_kind_coherence().unwrap_or_else(|err| {
22914                panic!(
22915                    "a :kind {kind:?} caixa with no declared :ci must pass \
22916                     the compound coherence gate — the fold's second identity \
22917                     element is the paired ci().is_some() short-circuit, got \
22918                     {err:?}",
22919                )
22920            });
22921        }
22922    }
22923
22924    #[test]
22925    fn validate_foreign_code_kind_coherence_folds_arm_matches_gate() {
22926        // Fail-before-pass-after equivalence pin on the compound
22927        // foreign-code-slot coherence fold: a `:kind Servico` caixa
22928        // carrying a declared `:exe` entry (the smallest possible
22929        // foreign-code-slot declaration on a code-running kind that
22930        // is not its owner — Servico owns `:servicos`, not `:exe`)
22931        // surfaces the same [`crate::LayoutError::ForeignCodeSlot`]
22932        // variant through both the compound gate
22933        // [`Caixa::validate_foreign_code_kind_coherence`] and the
22934        // standalone constructor
22935        // [`crate::LayoutError::foreign_code_slot`] dispatched on the
22936        // same `declared_foreign_code_slots` list. Pins the fold — a
22937        // silent regression that de-folded the arm would surface here
22938        // as a mismatch between the two dispatches. Sibling in shape
22939        // to the peer
22940        // `validate_kind_slot_coherence_folds_mesh_arm_matches_gate`
22941        // / `validate_ci_kind_coherence_folds_arm_matches_gate` /
22942        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22943        // per-arm equivalence pins on the sibling kind-coherence folds.
22944        let mut c = bare_servico_fixture("demo");
22945        c.exe = vec!["exe/foreign".into()];
22946        let via_method = c.validate_foreign_code_kind_coherence().unwrap_err();
22947        let via_standalone =
22948            crate::LayoutError::foreign_code_slot(&c, c.declared_foreign_code_slots());
22949        assert_eq!(
22950            via_method, via_standalone,
22951            "Caixa::validate_foreign_code_kind_coherence must surface the \
22952             foreign-code-slot diagnostic byte-equal to the standalone \
22953             LayoutError::foreign_code_slot ctor on the same \
22954             declared_foreign_code_slots list",
22955        );
22956    }
22957
22958    #[test]
22959    fn validate_foreign_code_kind_coherence_exe_arm_precedes_servicos_arm() {
22960        // Cross-arm ordering pin on the fold's accumulator: a fixture
22961        // carrying BOTH a declared `:exe` AND a declared `:servicos`
22962        // on a kind foreign to both (a `:kind Biblioteca` here —
22963        // foreign to both the Binario arm and the Servico arm)
22964        // surfaces `:exe` first in the `ForeignCodeSlot`'s slots
22965        // list. Pins the canonical `:exe` → `:servicos` diagnostic
22966        // order [`Caixa::declared_foreign_code_slots`] establishes,
22967        // as a property of the substrate primitive rather than an
22968        // implicit accumulator convention. A silent reordering
22969        // regression at the accumulator would surface here as a
22970        // wrong-first-slot list before landing at a downstream
22971        // consumer's diagnostic-ordering expectation.
22972        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22973        c.kind = CaixaKind::Biblioteca;
22974        c.exe = vec!["exe/demo".into()];
22975        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22976        let err = c.validate_foreign_code_kind_coherence().unwrap_err();
22977        let crate::LayoutError::ForeignCodeSlot { slots, .. } = &err else {
22978            panic!("expected ForeignCodeSlot variant, got {err:?}");
22979        };
22980        assert!(
22981            slots.starts_with(":exe"),
22982            "expected the :exe arm to precede the :servicos arm in the \
22983             ForeignCodeSlot slots list under the canonical :exe → :servicos \
22984             order, got slots = {slots:?}",
22985        );
22986        assert!(
22987            slots.contains(":servicos"),
22988            "expected the :servicos arm to also fire in the ForeignCodeSlot \
22989             slots list on a fixture carrying both foreign code surfaces, \
22990             got slots = {slots:?}",
22991        );
22992    }
22993
22994    #[test]
22995    fn validate_foreign_code_kind_coherence_accepts_native_slot_on_owner_kind() {
22996        // Positive control on the native-slot identity element: each
22997        // code-surface slot's owner kind passes the fold trivially
22998        // when it declares only its native code surface. `:kind
22999        // Binario` with a declared `:exe` and no `:servicos` passes
23000        // (the `!requires_exe()` guard short-circuits the arm inside
23001        // [`Caixa::declared_foreign_code_slots`], so the accumulator
23002        // returns empty); `:kind Servico` with a declared `:servicos`
23003        // and no `:exe` passes for the mirror reason. Pins the fold's
23004        // native-slot identity element on both arms — a silent
23005        // regression that dropped either per-arm `!requires_<slot>()`
23006        // predicate would surface here as a false-positive rejection
23007        // of every native-slot declaration on its owner kind. Peer
23008        // with the
23009        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
23010        // identity-element pin on the sibling cross-family fold.
23011        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
23012        bin.kind = CaixaKind::Binario;
23013        bin.bibliotecas = vec![];
23014        bin.exe = vec!["exe/bin".into()];
23015        bin.servicos = vec![];
23016        bin.validate_foreign_code_kind_coherence().expect(
23017            "a :kind Binario caixa with a declared native :exe and no \
23018             :servicos must pass the compound coherence gate — Binario is \
23019             the :exe slot's owner kind and the fold's native-slot identity \
23020             element on that arm",
23021        );
23022
23023        let mut svc = bare_servico_fixture("svc");
23024        svc.exe = vec![];
23025        svc.validate_foreign_code_kind_coherence().expect(
23026            "a :kind Servico caixa with a declared native :servicos and no \
23027             :exe must pass the compound coherence gate — Servico is the \
23028             :servicos slot's owner kind and the fold's native-slot identity \
23029             element on that arm",
23030        );
23031    }
23032
23033    #[test]
23034    fn validate_foreign_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
23035        // Positive control on the empty-slot identity element: a
23036        // bare caixa (no declared `:exe` and no declared `:servicos`)
23037        // passes the compound gate on every kind. Pins the fold's
23038        // identity element on the empty-accumulator axis — the outer
23039        // `is_empty` short-circuit fires before the wrap dispatch on
23040        // every kind, so a bare caixa of any kind surfaces no
23041        // foreign-code-slot diagnostic. A silent regression that
23042        // dropped the emptiness guard would surface here as a
23043        // false-positive rejection of every no-code-slot caixa
23044        // across the whole kind axis. Peer with the
23045        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
23046        // identity-element pin on the sibling cross-family fold.
23047        for kind in CaixaKind::ALL {
23048            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
23049            c.kind = *kind;
23050            c.bibliotecas = vec![];
23051            c.exe = vec![];
23052            c.servicos = vec![];
23053            c.validate_foreign_code_kind_coherence()
23054                .unwrap_or_else(|err| {
23055                    panic!(
23056                        "a bare :kind {kind:?} caixa (no declared :exe / \
23057                         :servicos) must pass the compound coherence gate — \
23058                         the fold's identity element on the empty-accumulator \
23059                         axis is the outer Vec::is_empty short-circuit, got \
23060                         {err:?}",
23061                    )
23062                });
23063        }
23064    }
23065
23066    #[test]
23067    fn validate_required_kind_slot_folds_binario_arm_matches_gate() {
23068        // Fail-before-pass-after per-arm equivalence pin on the
23069        // `Binario` required-`:exe` arm of the required-slot fold:
23070        // a `:kind Binario` caixa carrying no declared `:exe` entry
23071        // surfaces the same
23072        // [`crate::LayoutError::BinarioWithoutExe`] variant through
23073        // both the compound gate
23074        // [`Caixa::validate_required_kind_slot`] and the standalone
23075        // constructor [`crate::LayoutError::binario_without_exe`].
23076        // Pins the fold — a silent regression that de-folded the
23077        // `Binario` arm would surface here as a mismatch between
23078        // the two dispatches. Sibling in shape to the peer
23079        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
23080        // per-arm equivalence pin on the reciprocal code-surface
23081        // fold.
23082        let mut c = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
23083        c.kind = CaixaKind::Binario;
23084        c.bibliotecas = vec![];
23085        c.exe = vec![];
23086        let via_method = c.validate_required_kind_slot().unwrap_err();
23087        let via_standalone = crate::LayoutError::binario_without_exe(&c);
23088        assert_eq!(
23089            via_method, via_standalone,
23090            "Caixa::validate_required_kind_slot must surface the \
23091             Binario arm's diagnostic byte-equal to the standalone \
23092             LayoutError::binario_without_exe ctor",
23093        );
23094    }
23095
23096    #[test]
23097    fn validate_required_kind_slot_folds_servico_arm_matches_gate() {
23098        // Per-arm equivalence pin on the `Servico` required-
23099        // `:servicos` arm — the sibling of the Binario arm on the
23100        // required-slot fold. A `:kind Servico` caixa carrying no
23101        // declared `:servicos` entry surfaces the same
23102        // [`crate::LayoutError::ServicoWithoutServicos`] variant
23103        // through both dispatches.
23104        let mut c = Caixa::from_lisp(&Caixa::template("svc")).unwrap();
23105        c.kind = CaixaKind::Servico;
23106        c.bibliotecas = vec![];
23107        c.servicos = vec![];
23108        let via_method = c.validate_required_kind_slot().unwrap_err();
23109        let via_standalone = crate::LayoutError::servico_without_servicos(&c);
23110        assert_eq!(
23111            via_method, via_standalone,
23112            "Caixa::validate_required_kind_slot must surface the \
23113             Servico arm's diagnostic byte-equal to the standalone \
23114             LayoutError::servico_without_servicos ctor",
23115        );
23116    }
23117
23118    #[test]
23119    fn validate_required_kind_slot_folds_acao_arm_matches_gate() {
23120        // Per-arm equivalence pin on the `Acao` required-`:ci` arm
23121        // — the third and last arm on the required-slot fold. A
23122        // `:kind Acao` caixa carrying no declared `:ci` slot
23123        // surfaces the same [`crate::LayoutError::MissingCi`]
23124        // variant through both dispatches. The three per-arm pins
23125        // collectively cover every required-slot axis and every
23126        // owner kind of the arm dispatch.
23127        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
23128        c.kind = CaixaKind::Acao;
23129        c.bibliotecas = vec![];
23130        c.ci = None;
23131        let via_method = c.validate_required_kind_slot().unwrap_err();
23132        let via_standalone = crate::LayoutError::missing_ci(&c);
23133        assert_eq!(
23134            via_method, via_standalone,
23135            "Caixa::validate_required_kind_slot must surface the \
23136             Acao arm's diagnostic byte-equal to the standalone \
23137             LayoutError::missing_ci ctor",
23138        );
23139    }
23140
23141    #[test]
23142    fn validate_required_kind_slot_accepts_owner_kind_with_required_slot_present() {
23143        // Positive control on the owner-kind-with-slot-present
23144        // identity element: each of the three owner kinds
23145        // (`Binario` with a non-empty `:exe`, `Servico` with a
23146        // non-empty `:servicos`, `Acao` with `ci = Some(_)`)
23147        // passes the compound gate cleanly when it declares its
23148        // required slot. Pins the fold's second identity element
23149        // — the paired `is_empty` / `is_none` short-circuit fires
23150        // on every owner kind whose required slot is present, so
23151        // a caixa with its native required slot surfaces no
23152        // diagnostic. A silent regression that dropped the paired
23153        // `is_empty` / `is_none` short-circuit guard on any arm
23154        // would surface here as a false-positive rejection of the
23155        // corresponding owner kind. Peer with the
23156        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
23157        // identity-element pin on the sibling code-surface fold.
23158        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
23159        bin.kind = CaixaKind::Binario;
23160        bin.bibliotecas = vec![];
23161        bin.exe = vec!["exe/bin".into()];
23162        bin.validate_required_kind_slot().expect(
23163            "a :kind Binario caixa with declared :exe must pass the \
23164             required-slot gate — Binario's required slot is present",
23165        );
23166
23167        let svc = bare_servico_fixture("svc");
23168        svc.validate_required_kind_slot().expect(
23169            "a :kind Servico caixa with declared :servicos must pass \
23170             the required-slot gate — Servico's required slot is present",
23171        );
23172
23173        let acao = acao_fixture("acao");
23174        acao.validate_required_kind_slot().expect(
23175            "a :kind Acao caixa with declared :ci must pass the \
23176             required-slot gate — Acao's required slot is present",
23177        );
23178    }
23179
23180    #[test]
23181    fn validate_required_kind_slot_accepts_non_owner_kinds() {
23182        // Positive control on the non-owner-kind identity element:
23183        // every kind that is not one of the three owner kinds
23184        // (`Binario` / `Servico` / `Acao`) passes the compound gate
23185        // trivially — each per-arm predicate is
23186        // `self.kind().requires_<slot>()`, which returns `true`
23187        // only for the owner kind of that arm, so a non-owner kind
23188        // short-circuits every per-arm dispatch. Bibliotheca,
23189        // Supervisor, and Aplicacao are the three non-owner kinds
23190        // this pin exercises — none of them owns a required slot in
23191        // this fold (`Biblioteca`'s `:bibliotecas` default-file
23192        // fallback stays on the layout-side `MissingLib` fs-oracle
23193        // gate outside this fold; `Supervisor`'s `:children` and
23194        // `Aplicacao`'s `:membros` are carried by
23195        // [`CaixaKind::requires_children`] /
23196        // [`CaixaKind::requires_membros`] without a paired
23197        // layout-side wire-up). A silent regression that swapped a
23198        // per-arm predicate for a non-`requires_*` guard would
23199        // surface here as a false-positive rejection of the
23200        // corresponding non-owner kind. Peer with the
23201        // `validate_ci_kind_coherence_accepts_absent_ci_on_every_kind`
23202        // identity-element pin on the sibling `:ci` fold.
23203        for kind in CaixaKind::ALL {
23204            if kind.requires_exe() || kind.requires_servicos() || kind.requires_ci() {
23205                continue;
23206            }
23207            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
23208            c.kind = *kind;
23209            c.bibliotecas = vec![];
23210            c.exe = vec![];
23211            c.servicos = vec![];
23212            c.ci = None;
23213            c.validate_required_kind_slot().unwrap_or_else(|err| {
23214                panic!(
23215                    "a :kind {kind:?} caixa (a non-owner kind on every \
23216                     required-slot arm) must pass the compound gate — the \
23217                     fold's identity element is the paired \
23218                     `self.kind().requires_<slot>()` short-circuit, got \
23219                     {err:?}",
23220                )
23221            });
23222        }
23223    }
23224
23225    // ── `manifest_code_path_slot_path_ctors!` — the paired `{ slot:
23226    //    &'static str, path: PathBuf }` two-slot envelope on
23227    //    `ManifestError`, strict sibling of the peer
23228    //    [`crate::behavior::behavior_slot_path_ctors!`] (67c31ec) on the
23229    //    sibling `BehaviorError` envelope's identical
23230    //    `{ slot: &'static str, path: PathBuf }` two-slot shape.
23231    //    Five-variant lift closing the five open-coded ctor sites
23232    //    remaining on the `:bibliotecas` / `:exe` / `:servicos`
23233    //    code-path-list value-shape trajectory this envelope carries.
23234
23235    #[test]
23236    fn code_path_absolute_ctor_matches_struct_literal_wrap() {
23237        let path = Path::new("/abs/lib/x.lisp");
23238        assert_eq!(
23239            ManifestError::code_path_absolute(":bibliotecas", path),
23240            ManifestError::CodePathAbsolute {
23241                slot: ":bibliotecas",
23242                path: path.to_path_buf(),
23243            },
23244            "generated code_path_absolute ctor must produce byte-equal \
23245             `ManifestError::CodePathAbsolute` to the pre-lift \
23246             struct-literal wrap on the same `(&'static str, &Path)` \
23247             fixture",
23248        );
23249    }
23250
23251    #[test]
23252    fn code_path_parent_escape_ctor_matches_struct_literal_wrap() {
23253        let path = Path::new("lib/../../etc/x.lisp");
23254        assert_eq!(
23255            ManifestError::code_path_parent_escape(":bibliotecas", path),
23256            ManifestError::CodePathParentEscape {
23257                slot: ":bibliotecas",
23258                path: path.to_path_buf(),
23259            },
23260            "generated code_path_parent_escape ctor must produce \
23261             byte-equal `ManifestError::CodePathParentEscape` to the \
23262             pre-lift struct-literal wrap on the same `(&'static str, \
23263             &Path)` fixture",
23264        );
23265    }
23266
23267    #[test]
23268    fn code_path_non_lisp_extension_ctor_matches_struct_literal_wrap() {
23269        let path = Path::new("lib/x.txt");
23270        assert_eq!(
23271            ManifestError::code_path_non_lisp_extension(":bibliotecas", path),
23272            ManifestError::CodePathNonLispExtension {
23273                slot: ":bibliotecas",
23274                path: path.to_path_buf(),
23275            },
23276            "generated code_path_non_lisp_extension ctor must produce \
23277             byte-equal `ManifestError::CodePathNonLispExtension` to \
23278             the pre-lift struct-literal wrap on the same \
23279             `(&'static str, &Path)` fixture",
23280        );
23281    }
23282
23283    #[test]
23284    fn code_path_non_computeunit_yaml_extension_ctor_matches_struct_literal_wrap() {
23285        let path = Path::new("servicos/x.yaml");
23286        assert_eq!(
23287            ManifestError::code_path_non_computeunit_yaml_extension(":servicos", path),
23288            ManifestError::CodePathNonComputeUnitYamlExtension {
23289                slot: ":servicos",
23290                path: path.to_path_buf(),
23291            },
23292            "generated code_path_non_computeunit_yaml_extension ctor \
23293             must produce byte-equal \
23294             `ManifestError::CodePathNonComputeUnitYamlExtension` to \
23295             the pre-lift struct-literal wrap on the same \
23296             `(&'static str, &Path)` fixture",
23297        );
23298    }
23299
23300    #[test]
23301    fn code_path_duplicate_ctor_matches_struct_literal_wrap() {
23302        let path = Path::new("lib/x.lisp");
23303        assert_eq!(
23304            ManifestError::code_path_duplicate(":bibliotecas", path),
23305            ManifestError::CodePathDuplicate {
23306                slot: ":bibliotecas",
23307                path: path.to_path_buf(),
23308            },
23309            "generated code_path_duplicate ctor must produce byte-equal \
23310             `ManifestError::CodePathDuplicate` to the pre-lift \
23311             struct-literal wrap on the same `(&'static str, &Path)` \
23312             fixture",
23313        );
23314    }
23315
23316    #[test]
23317    fn manifest_code_path_slot_path_ctors_route_slot_and_path_through_uniformly() {
23318        // Cross-axis routing pin: sweep the two constructor input axes
23319        // (`slot: &'static str`, `path: &Path`) through non-default
23320        // fixtures against every generated arm in the
23321        // [`manifest_code_path_slot_path_ctors!`] macro, so any
23322        // wrapper-side lowercase / trim / truncate / canonicalization at
23323        // codegen time — or a silent field re-name away from the
23324        // canonical `slot` / `path` axes on any one variant, or a `slot`
23325        // axis silently rerouted through `.to_string()` instead of
23326        // passed as `&'static str` verbatim, or a `path` axis silently
23327        // rerouted through `.canonicalize()` / `PathBuf::from(<lossy
23328        // string>)` instead of `.to_path_buf()` — surfaces here rather
23329        // than at a downstream diagnostic-shape mismatch. Peer of the
23330        // sibling
23331        // [`crate::behavior::tests::behavior_slot_path_ctors_route_slot_and_path_through_uniformly`]
23332        // pin (67c31ec) on the sibling `BehaviorError` envelope's
23333        // identical two-slot family.
23334        //
23335        // The `path` fixture carries three distinguishing traits at
23336        // once: a non-`root/`-relative leading segment (`weird/`), a
23337        // `..` component (a canonicalization trap that would collapse
23338        // to `weird/x.lisp` under `.canonicalize()`), and a mixed-case
23339        // extension (a lowercase-normalization trap that would collapse
23340        // `.LISP` to `.lisp` under any `to_ascii_lowercase()` codegen)
23341        // so a routing regression on any one of the three trap axes
23342        // surfaces at assert time. Similarly the `slot` fixture
23343        // sweeps the three canonical code-path author-key literals
23344        // (`:bibliotecas` / `:exe` / `:servicos`) so a silent lookup
23345        // against a per-variant const roster would surface here.
23346        let path = Path::new("weird/../nested/x.LISP");
23347        let cases: [(ManifestError, ManifestError); 5] = [
23348            (
23349                ManifestError::code_path_absolute(":bibliotecas", path),
23350                ManifestError::CodePathAbsolute {
23351                    slot: ":bibliotecas",
23352                    path: path.to_path_buf(),
23353                },
23354            ),
23355            (
23356                ManifestError::code_path_parent_escape(":exe", path),
23357                ManifestError::CodePathParentEscape {
23358                    slot: ":exe",
23359                    path: path.to_path_buf(),
23360                },
23361            ),
23362            (
23363                ManifestError::code_path_non_lisp_extension(":servicos", path),
23364                ManifestError::CodePathNonLispExtension {
23365                    slot: ":servicos",
23366                    path: path.to_path_buf(),
23367                },
23368            ),
23369            (
23370                ManifestError::code_path_non_computeunit_yaml_extension(":bibliotecas", path),
23371                ManifestError::CodePathNonComputeUnitYamlExtension {
23372                    slot: ":bibliotecas",
23373                    path: path.to_path_buf(),
23374                },
23375            ),
23376            (
23377                ManifestError::code_path_duplicate(":exe", path),
23378                ManifestError::CodePathDuplicate {
23379                    slot: ":exe",
23380                    path: path.to_path_buf(),
23381                },
23382            ),
23383        ];
23384        for (via_ctor, via_struct_literal) in cases {
23385            assert_eq!(
23386                via_ctor, via_struct_literal,
23387                "manifest_code_path_slot_path_ctors!-generated ctor \
23388                 must pass `slot` verbatim onto the canonical \
23389                 `&'static str` `slot` field and route `path` through \
23390                 `.to_path_buf()` onto the canonical `PathBuf` `path` \
23391                 field — a field-rename, silent-conversion, or \
23392                 axis-swap regression surfaces here rather than at a \
23393                 downstream diagnostic-shape mismatch",
23394            );
23395        }
23396    }
23397
23398    // ── `manifest_field_reason_ctors!` — the paired `{ <field>: String,
23399    //    reason: String }` two-slot envelope on `ManifestError`, direct
23400    //    sibling of the peer
23401    //    [`crate::aplicacao::aplicacao_field_reason_ctors!`] (981060b)
23402    //    on the M3 mesh `AplicacaoError` envelope's identical two-slot
23403    //    shape and of the peer [`crate::dep::dep_nome_axis_reason_ctors!`]
23404    //    (5621f8a) on the sibling `:deps` envelope's mirror-symmetric
23405    //    three-slot shape (the `nome` axis added at the per-dep-owned
23406    //    altitude). Ten-variant lift closing the ten open-coded ctor
23407    //    sites at the per-axis [`Caixa::validate_*`] cascade — the tenth
23408    //    (`restart_window_malformed => RestartWindowMalformed
23409    //    { restart_window }`) closes the last open-coded four-line
23410    //    `.map_err(|reason| ManifestError::RestartWindowMalformed
23411    //    { restart_window: s.to_string(), reason })` block at
23412    //    [`Caixa::validate_restart_window`] onto the same substrate
23413    //    primitive per typed variant.
23414
23415    #[test]
23416    fn nome_invalid_ctor_matches_struct_literal_wrap() {
23417        let nome = "cart-svc";
23418        let reason = "sample reason text";
23419        assert_eq!(
23420            ManifestError::nome_invalid(nome, reason),
23421            ManifestError::NomeInvalid {
23422                nome: nome.to_string(),
23423                reason: reason.to_string(),
23424            },
23425            "generated nome_invalid ctor must produce byte-equal \
23426             `ManifestError::NomeInvalid` to the pre-lift struct-literal \
23427             wrap on the same `(&str, &str)` fixture",
23428        );
23429    }
23430
23431    #[test]
23432    fn nome_chart_name_budget_exceeded_ctor_matches_struct_literal_wrap() {
23433        let nome = "a-very-long-cart-service-name";
23434        let reason = "sample reason text";
23435        assert_eq!(
23436            ManifestError::nome_chart_name_budget_exceeded(nome, reason),
23437            ManifestError::NomeChartNameBudgetExceeded {
23438                nome: nome.to_string(),
23439                reason: reason.to_string(),
23440            },
23441            "generated nome_chart_name_budget_exceeded ctor must produce \
23442             byte-equal `ManifestError::NomeChartNameBudgetExceeded` to \
23443             the pre-lift struct-literal wrap on the same `(&str, &str)` \
23444             fixture",
23445        );
23446    }
23447
23448    #[test]
23449    fn versao_invalid_ctor_matches_struct_literal_wrap() {
23450        let versao = "0.1";
23451        let reason = "sample reason text";
23452        assert_eq!(
23453            ManifestError::versao_invalid(versao, reason),
23454            ManifestError::VersaoInvalid {
23455                versao: versao.to_string(),
23456                reason: reason.to_string(),
23457            },
23458            "generated versao_invalid ctor must produce byte-equal \
23459             `ManifestError::VersaoInvalid` to the pre-lift \
23460             struct-literal wrap on the same `(&str, &str)` fixture",
23461        );
23462    }
23463
23464    #[test]
23465    fn etiqueta_invalid_ctor_matches_struct_literal_wrap() {
23466        let etiqueta = "MyKeyword";
23467        let reason = "sample reason text";
23468        assert_eq!(
23469            ManifestError::etiqueta_invalid(etiqueta, reason),
23470            ManifestError::EtiquetaInvalid {
23471                etiqueta: etiqueta.to_string(),
23472                reason: reason.to_string(),
23473            },
23474            "generated etiqueta_invalid ctor must produce byte-equal \
23475             `ManifestError::EtiquetaInvalid` to the pre-lift \
23476             struct-literal wrap on the same `(&str, &str)` fixture",
23477        );
23478    }
23479
23480    #[test]
23481    fn autor_invalid_ctor_matches_struct_literal_wrap() {
23482        let autor = "Ada Lovelace";
23483        let reason = "sample reason text";
23484        assert_eq!(
23485            ManifestError::autor_invalid(autor, reason),
23486            ManifestError::AutorInvalid {
23487                autor: autor.to_string(),
23488                reason: reason.to_string(),
23489            },
23490            "generated autor_invalid ctor must produce byte-equal \
23491             `ManifestError::AutorInvalid` to the pre-lift struct-literal \
23492             wrap on the same `(&str, &str)` fixture",
23493        );
23494    }
23495
23496    #[test]
23497    fn repositorio_invalid_ctor_matches_struct_literal_wrap() {
23498        let repositorio = "https://example.com/no-dot-git";
23499        let reason = "sample reason text";
23500        assert_eq!(
23501            ManifestError::repositorio_invalid(repositorio, reason),
23502            ManifestError::RepositorioInvalid {
23503                repositorio: repositorio.to_string(),
23504                reason: reason.to_string(),
23505            },
23506            "generated repositorio_invalid ctor must produce byte-equal \
23507             `ManifestError::RepositorioInvalid` to the pre-lift \
23508             struct-literal wrap on the same `(&str, &str)` fixture",
23509        );
23510    }
23511
23512    #[test]
23513    fn descricao_invalid_ctor_matches_struct_literal_wrap() {
23514        let descricao = "some description";
23515        let reason = "sample reason text";
23516        assert_eq!(
23517            ManifestError::descricao_invalid(descricao, reason),
23518            ManifestError::DescricaoInvalid {
23519                descricao: descricao.to_string(),
23520                reason: reason.to_string(),
23521            },
23522            "generated descricao_invalid ctor must produce byte-equal \
23523             `ManifestError::DescricaoInvalid` to the pre-lift \
23524             struct-literal wrap on the same `(&str, &str)` fixture",
23525        );
23526    }
23527
23528    #[test]
23529    fn licenca_invalid_ctor_matches_struct_literal_wrap() {
23530        let licenca = "not-an-spdx";
23531        let reason = "sample reason text";
23532        assert_eq!(
23533            ManifestError::licenca_invalid(licenca, reason),
23534            ManifestError::LicencaInvalid {
23535                licenca: licenca.to_string(),
23536                reason: reason.to_string(),
23537            },
23538            "generated licenca_invalid ctor must produce byte-equal \
23539             `ManifestError::LicencaInvalid` to the pre-lift \
23540             struct-literal wrap on the same `(&str, &str)` fixture",
23541        );
23542    }
23543
23544    #[test]
23545    fn edicao_invalid_ctor_matches_struct_literal_wrap() {
23546        let edicao = "26";
23547        let reason = "sample reason text";
23548        assert_eq!(
23549            ManifestError::edicao_invalid(edicao, reason),
23550            ManifestError::EdicaoInvalid {
23551                edicao: edicao.to_string(),
23552                reason: reason.to_string(),
23553            },
23554            "generated edicao_invalid ctor must produce byte-equal \
23555             `ManifestError::EdicaoInvalid` to the pre-lift \
23556             struct-literal wrap on the same `(&str, &str)` fixture",
23557        );
23558    }
23559
23560    #[test]
23561    fn restart_window_malformed_ctor_matches_struct_literal_wrap() {
23562        let restart_window = "1.5s";
23563        let reason = "sample reason text";
23564        assert_eq!(
23565            ManifestError::restart_window_malformed(restart_window, reason),
23566            ManifestError::RestartWindowMalformed {
23567                restart_window: restart_window.to_string(),
23568                reason: reason.to_string(),
23569            },
23570            "generated restart_window_malformed ctor must produce byte-equal \
23571             `ManifestError::RestartWindowMalformed` to the pre-lift \
23572             struct-literal wrap on the same `(&str, &str)` fixture",
23573        );
23574    }
23575
23576    // Routing pin against the actual [`Caixa::validate_restart_window`]
23577    // wire-up: the codec surfaces its parse error as `Result<Duration, String>`,
23578    // and the pre-lift `.map_err(|reason| ManifestError::RestartWindowMalformed
23579    // { restart_window: s.to_string(), reason })` closure passed the owned
23580    // `String` verbatim onto the `reason: String` slot. The lifted
23581    // `restart_window_malformed(&str, impl Into<String>)` ctor must produce
23582    // byte-equal output on the same `(offending_value, owned_reason)` pair a
23583    // real parse-failure fixture surfaces, so a silent regression on the
23584    // owned-`String` axis (a future `reason` bound change dropping the
23585    // `Into<String>` route the owned reason threads through) surfaces here
23586    // rather than at a downstream diagnostic-shape drift.
23587    #[test]
23588    fn restart_window_malformed_ctor_matches_wire_up_owned_reason_shape() {
23589        let raw = "1.5s";
23590        let reason: String = crate::supervisor::duration_codec::parse(raw)
23591            .expect_err("fractional-seconds `1.5s` must fail the shared codec");
23592        assert_eq!(
23593            ManifestError::restart_window_malformed(raw, reason.clone()),
23594            ManifestError::RestartWindowMalformed {
23595                restart_window: raw.to_string(),
23596                reason: reason.clone(),
23597            },
23598            "generated restart_window_malformed ctor must accept the owned \
23599             `String` the [`crate::supervisor::duration_codec::parse`] parse-\
23600             error carrier surfaces (the exact shape the \
23601             [`Caixa::validate_restart_window`] `.map_err(|reason| ...)` \
23602             closure passes into it) and produce byte-equal \
23603             `ManifestError::RestartWindowMalformed` to the pre-lift \
23604             struct-literal wrap on the same `(offending_value, owned_reason)` \
23605             pair",
23606        );
23607    }
23608
23609    // Cross-family invariance pin — the ten sibling ctors all route
23610    // `reason: impl Into<String>` + `<field>: &str` verbatim onto their
23611    // respective typed variants through the shared
23612    // [`manifest_field_reason_ctors!`] macro. Sweeps three fixture
23613    // shapes for `reason` (`&str` literal, owned `String`, `format!(…)`
23614    // output — the three shapes every in-crate wire-up threads through:
23615    // the parser-shaped `String` every `Result<(), String>` predicate
23616    // returns, the `e.to_string()` owned `String` the
23617    // `semver::Version::parse` arm passes, and the literal-shape reason
23618    // the `EdicaoInvalid` direct arm passes) against every generated arm
23619    // so any per-arm wrapper transformation drift surfaces here rather
23620    // than at a downstream diagnostic-shape mismatch. Peer of the
23621    // sibling
23622    // [`crate::aplicacao::tests::aplicacao_field_reason_ctors_route_reason_through_into_uniformly`]
23623    // pin (981060b) on the sibling `AplicacaoError` envelope's identical
23624    // two-slot family.
23625    #[test]
23626    fn manifest_field_reason_ctors_route_reason_through_into_uniformly() {
23627        let via_literal = "literal reason text";
23628        let via_owned: String = String::from("literal reason text");
23629        let via_format = format!("{} reason text", "literal");
23630        assert_eq!(
23631            ManifestError::nome_invalid("n", via_literal),
23632            ManifestError::nome_invalid("n", via_owned.clone()),
23633        );
23634        assert_eq!(
23635            ManifestError::nome_invalid("n", via_literal),
23636            ManifestError::nome_invalid("n", via_format.clone()),
23637        );
23638        assert_eq!(
23639            ManifestError::nome_chart_name_budget_exceeded("n", via_literal),
23640            ManifestError::nome_chart_name_budget_exceeded("n", via_owned.clone()),
23641        );
23642        assert_eq!(
23643            ManifestError::versao_invalid("0.1", via_literal),
23644            ManifestError::versao_invalid("0.1", via_owned.clone()),
23645        );
23646        assert_eq!(
23647            ManifestError::etiqueta_invalid("k", via_literal),
23648            ManifestError::etiqueta_invalid("k", via_owned.clone()),
23649        );
23650        assert_eq!(
23651            ManifestError::autor_invalid("a", via_literal),
23652            ManifestError::autor_invalid("a", via_owned.clone()),
23653        );
23654        assert_eq!(
23655            ManifestError::repositorio_invalid("r", via_literal),
23656            ManifestError::repositorio_invalid("r", via_owned.clone()),
23657        );
23658        assert_eq!(
23659            ManifestError::descricao_invalid("d", via_literal),
23660            ManifestError::descricao_invalid("d", via_owned.clone()),
23661        );
23662        assert_eq!(
23663            ManifestError::licenca_invalid("l", via_literal),
23664            ManifestError::licenca_invalid("l", via_owned.clone()),
23665        );
23666        assert_eq!(
23667            ManifestError::edicao_invalid("26", via_literal),
23668            ManifestError::edicao_invalid("26", via_owned.clone()),
23669        );
23670        assert_eq!(
23671            ManifestError::edicao_invalid("26", via_literal),
23672            ManifestError::edicao_invalid("26", via_format.clone()),
23673        );
23674        assert_eq!(
23675            ManifestError::restart_window_malformed("1.5s", via_literal),
23676            ManifestError::restart_window_malformed("1.5s", via_owned),
23677        );
23678        assert_eq!(
23679            ManifestError::restart_window_malformed("1.5s", via_literal),
23680            ManifestError::restart_window_malformed("1.5s", via_format),
23681        );
23682    }
23683
23684    // Cross-arm routing pin — the ten sibling ctors accept both `&str`
23685    // (from the [`Caixa::nome`] / [`Caixa::versao`] / [`Caixa::repositorio`]
23686    // / [`Caixa::descricao`] / [`Caixa::licenca`] / [`Caixa::edicao`]
23687    // accessors that return `&str`) and `&String` (from the
23688    // [`Caixa::etiquetas`] / [`Caixa::autores`] slice iterators that yield
23689    // `&String`) at the `<field>: &str` parameter via Deref coercion. This
23690    // pin sweeps both call shapes against the two accessors' actual
23691    // wire-up postures so a future rebrand of the etiquetas / autores
23692    // slice-iterator type (a lift from `&[String]` to `&[Cow<'_, str>]`,
23693    // a `smol_str::SmolStr` per-entry swap) that silently broke the
23694    // Deref-coercion path surfaces at this pin rather than at a
23695    // recompile-time type-mismatch far from the ctor family.
23696    #[test]
23697    fn manifest_field_reason_ctors_accept_both_str_and_string_slice_iters() {
23698        let owned: String = String::from("MyKeyword");
23699        // `&str` literal — the canonical accessor-return shape
23700        // ([`Caixa::nome`] etc. yield `&str`).
23701        assert_eq!(
23702            ManifestError::etiqueta_invalid("MyKeyword", "r"),
23703            ManifestError::EtiquetaInvalid {
23704                etiqueta: "MyKeyword".to_string(),
23705                reason: "r".to_string(),
23706            },
23707        );
23708        // `&String` — the canonical slice-iterator-yield shape
23709        // ([`Caixa::etiquetas`] / [`Caixa::autores`] yield `&String`).
23710        assert_eq!(
23711            ManifestError::etiqueta_invalid(&owned, "r"),
23712            ManifestError::EtiquetaInvalid {
23713                etiqueta: owned.clone(),
23714                reason: "r".to_string(),
23715            },
23716        );
23717        // Both call shapes must produce byte-equal
23718        // [`ManifestError::EtiquetaInvalid`] values on the same
23719        // underlying `String`, so a wire-up threading `etiqueta: &String`
23720        // through the same ctor as a peer wire-up threading `nome: &str`
23721        // through it collapses onto one canonical shape.
23722        assert_eq!(
23723            ManifestError::etiqueta_invalid("MyKeyword", "r"),
23724            ManifestError::etiqueta_invalid(&owned, "r"),
23725        );
23726    }
23727
23728    // ── `manifest_field_only_ctors!` — the paired `{ <field>: String }`
23729    //    single-slot envelope on `ManifestError`, direct sibling of the
23730    //    peer [`crate::aplicacao::aplicacao_caixa_only_ctors!`] (d9f6867,
23731    //    `{ caixa: String }` on `AplicacaoError`) and
23732    //    [`crate::aplicacao::aplicacao_path_only_ctors!`] (3ba8de6,
23733    //    `{ path: String }` on `AplicacaoError`) on the M3 mesh envelope,
23734    //    of the peer [`crate::supervisor::supervisor_caixa_only_ctors!`]
23735    //    (db09650, `{ caixa: String }` on `SupervisorError`), and of the
23736    //    peer [`crate::dep::dep_nome_only_ctors!`] (792aa92,
23737    //    `{ nome: String }` on `DepError`) folds on their sibling
23738    //    envelopes. Two-variant lift closing the last two open-coded
23739    //    single-`String`-slot ctor sites at
23740    //    [`Caixa::validate_etiquetas`] and [`Caixa::validate_autores`].
23741
23742    #[test]
23743    fn etiqueta_duplicate_ctor_matches_struct_literal_wrap() {
23744        assert_eq!(
23745            ManifestError::etiqueta_duplicate("mesh"),
23746            ManifestError::EtiquetaDuplicate {
23747                etiqueta: "mesh".to_string(),
23748            },
23749            "generated etiqueta_duplicate ctor must produce byte-equal \
23750             `ManifestError::EtiquetaDuplicate` to the pre-lift \
23751             struct-literal wrap on the same `&str` fixture",
23752        );
23753    }
23754
23755    #[test]
23756    fn autor_duplicate_ctor_matches_struct_literal_wrap() {
23757        assert_eq!(
23758            ManifestError::autor_duplicate("pleme-io"),
23759            ManifestError::AutorDuplicate {
23760                autor: "pleme-io".to_string(),
23761            },
23762            "generated autor_duplicate ctor must produce byte-equal \
23763             `ManifestError::AutorDuplicate` to the pre-lift \
23764             struct-literal wrap on the same `&str` fixture",
23765        );
23766    }
23767
23768    #[test]
23769    fn manifest_field_only_ctors_route_field_through_to_string() {
23770        // Cross-axis pin: sweep the sole constructor input axis
23771        // (`<field>: &str`) through a non-default fixture value against
23772        // every generated arm in the [`manifest_field_only_ctors!`]
23773        // macro, so any wrapper-side lowercase / trim / truncate / silent
23774        // constant-substitution on the `<field>.to_string()` sole-field
23775        // construction surfaces here rather than at a downstream
23776        // diagnostic-shape mismatch. Peer of the sibling
23777        // [`crate::aplicacao::tests::aplicacao_caixa_only_ctors_route_caixa_through_to_string`]
23778        // (d9f6867) and
23779        // [`crate::aplicacao::tests::aplicacao_path_only_ctors_route_path_through_to_string`]
23780        // (3ba8de6) cross-axis pins on the peer `AplicacaoError`
23781        // single-`String`-slot envelopes.
23782        let value = "cache-v2";
23783        assert_eq!(
23784            ManifestError::etiqueta_duplicate(value),
23785            ManifestError::EtiquetaDuplicate {
23786                etiqueta: value.to_string(),
23787            },
23788        );
23789        assert_eq!(
23790            ManifestError::autor_duplicate(value),
23791            ManifestError::AutorDuplicate {
23792                autor: value.to_string(),
23793            },
23794        );
23795    }
23796
23797    #[test]
23798    fn manifest_field_only_ctors_accept_both_str_and_string_slice_iters() {
23799        // The two wire-up sites at [`Caixa::validate_etiquetas`] and
23800        // [`Caixa::validate_autores`] each thread a `&String` loop head
23801        // through the ctor via Deref coercion at the `<field>: &str`
23802        // parameter — this pin locks that call shape's byte-equality
23803        // against the direct `&str` shape so a future rebrand of the
23804        // `:etiquetas` / `:autores` slice-iterator type that silently
23805        // broke the Deref-coercion path surfaces here rather than at a
23806        // recompile-time type-mismatch far from the ctor family. Peer of
23807        // the sibling
23808        // [`manifest_field_reason_ctors_accept_both_str_and_string_slice_iters`]
23809        // pin on the peer two-slot `{ <field>: String, reason: String }`
23810        // envelope.
23811        let etiqueta: String = String::from("mesh");
23812        assert_eq!(
23813            ManifestError::etiqueta_duplicate("mesh"),
23814            ManifestError::etiqueta_duplicate(&etiqueta),
23815        );
23816        let autor: String = String::from("pleme-io");
23817        assert_eq!(
23818            ManifestError::autor_duplicate("pleme-io"),
23819            ManifestError::autor_duplicate(&autor),
23820        );
23821    }
23822}