Skip to main content

caixa_core/
manifest.rs

1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4use tatara_lisp::DeriveTataraDomain;
5
6use thiserror::Error;
7
8use crate::{
9    CaixaKind, Dep,
10    behavior::BehaviorSpec,
11    dep::DepError,
12    limits::LimitsSpec,
13    render::{
14        PathShapeViolation, is_computeunit_yaml_extension, is_git_repo_url, is_lisp_extension,
15        is_sandboxed_relative_path,
16    },
17    supervisor::SupervisorSpec,
18    upgrade::UpgradeFromEntry,
19};
20
21/// Top-level manifest for a caixa (a tatara-lisp package).
22///
23/// Authored as `caixa.lisp`:
24///
25/// ```lisp
26/// (defcaixa
27///   :nome        "pangea-tatara-aws"
28///   :versao      "0.1.0"
29///   :kind        Biblioteca
30///   :edicao      "2026"
31///   :descricao   "AWS provider caixa for tatara-lisp"
32///   :repositorio "github:pleme-io/pangea-tatara-aws"
33///   :licenca     "MIT"
34///   :autores     ("pleme-io")
35///   :etiquetas   ("iac" "aws" "pangea")
36///   :deps        ((:nome "caixa-teia"    :versao "^0.1")
37///                 (:nome "iac-forge-ir"  :versao "^0.5"))
38///   :deps-dev    ((:nome "tatara-check"  :versao "*"))
39///   :bibliotecas ("lib/pangea-tatara-aws.lisp"))
40/// ```
41///
42/// Because `Caixa` derives [`tatara_lisp::domain::TataraDomain`], the manifest
43/// is parsed directly by the tatara-lisp compiler — an ill-formed manifest is
44/// a compile error, not a runtime error.
45#[derive(DeriveTataraDomain, Serialize, Deserialize, Debug, Clone, PartialEq)]
46#[serde(rename_all = "camelCase")]
47#[tatara(keyword = "defcaixa")]
48pub struct Caixa {
49    /// Package name — the canonical string used in `:deps`, the registry, and
50    /// the default lib/exe entry names.
51    pub nome: String,
52
53    /// Package version — a semver literal like `"0.1.0"`. Parsed lazily via
54    /// [`crate::CaixaVersion::parse`].
55    pub versao: String,
56
57    /// What this caixa produces. See [`CaixaKind`].
58    pub kind: CaixaKind,
59
60    /// Language edition — determines macro surface + compatibility flags.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub edicao: Option<String>,
63
64    /// Free-form description shown in the registry listing.
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub descricao: Option<String>,
67
68    /// Homepage or repo URL.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub repositorio: Option<String>,
71
72    /// SPDX license expression — `"MIT"`, `"Apache-2.0 OR MIT"`, etc.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub licenca: Option<String>,
75
76    /// Authors — free-form strings.
77    #[serde(default)]
78    pub autores: Vec<String>,
79
80    /// Topical tags used for registry search.
81    #[serde(default)]
82    pub etiquetas: Vec<String>,
83
84    /// Runtime dependencies.
85    #[serde(default)]
86    pub deps: Vec<Dep>,
87
88    /// Development-only dependencies (tests, lint, bench).
89    #[serde(default)]
90    pub deps_dev: Vec<Dep>,
91
92    /// Paths to executable entry points (relative to the package root).
93    /// Required when `:kind Binario`.
94    #[serde(default)]
95    pub exe: Vec<String>,
96
97    /// Paths to library entry points (relative to the package root).
98    /// First entry is the canonical `lib/<nome>.lisp`; when omitted under
99    /// `:kind Biblioteca`, the layout check expects `lib/<nome>.lisp`.
100    #[serde(default)]
101    pub bibliotecas: Vec<String>,
102
103    /// Paths to service manifests (relative to the package root).
104    /// Required when `:kind Servico`.
105    #[serde(default)]
106    pub servicos: Vec<String>,
107
108    // ── M2 typed-substrate extensions per theory/ABSORPTION-ROADMAP.md ──
109    //
110    // All four are optional + default to "absent"; existing caixas
111    // round-trip unchanged. Each maps onto a prior-art primitive named
112    // in theory/INSPIRATIONS.md:
113    //
114    //   :limits        — Lunatic per-process limits (§III.1)
115    //   :behavior      — OTP gen_server callbacks  (§II.3)
116    //   :upgrade-from  — OTP appup migration       (§II.4)
117    //   :estrategia    — OTP supervisor strategy   (§II.2 + §III.2)
118    //   :children      — OTP supervisor children    (§II.2 + §III.2)
119    //
120    // The supervisor slots are flat on Caixa (vs nested under a
121    // SupervisorSpec sub-form) to keep tatara-lisp authoring at one
122    // level of nesting; SupervisorSpec exists for validation +
123    // composition convenience (`Caixa::supervisor_view()`).
124    /// Lunatic-style per-process resource limits. None = unbounded.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub limits: Option<LimitsSpec>,
127
128    /// OTP-shaped behavior callbacks for Servico-kind caixas.
129    /// Authored as `(:on-init "..." :on-call "..." …)`.
130    #[serde(default, skip_serializing_if = "Option::is_none")]
131    pub behavior: Option<BehaviorSpec>,
132
133    /// OTP appup — declarative upgrade instructions per prior version.
134    /// Empty list = no hot-upgrade path declared (caller falls back to
135    /// `:Restart` strategy).
136    #[serde(default)]
137    pub upgrade_from: Vec<UpgradeFromEntry>,
138
139    /// OTP supervisor strategy. Required when `:kind Supervisor`;
140    /// ignored otherwise.
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub estrategia: Option<crate::supervisor::RestartStrategy>,
143
144    /// Max restarts before the supervisor itself fails. Defaults via
145    /// SupervisorSpec at validation time.
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub max_restarts: Option<u32>,
148
149    /// Sliding window for `max_restarts`. Authored as a duration
150    /// string (`"60s"`, `"5m"`).
151    #[serde(default, skip_serializing_if = "Option::is_none")]
152    pub restart_window: Option<String>,
153
154    /// Static children of a supervisor. Required for OneForOne /
155    /// OneForAll / RestForOne; must be empty for SimpleOneForOne.
156    #[serde(default)]
157    pub children: Vec<crate::supervisor::ChildSpec>,
158
159    // ── M3 Aplicacao slots (theory/MESH-COMPOSITION.md) ─────────────────
160    //
161    // Required when :kind Aplicacao; ignored otherwise.
162    // Composed into a typed AplicacaoSpec via Caixa::aplicacao_view().
163    /// Member Servicos that make up this Aplicacao. Each is a
164    /// caixa-name + version-constraint pair. Required for Aplicacao.
165    #[serde(default)]
166    pub membros: Vec<crate::aplicacao::Membro>,
167
168    /// WIT-typed inter-Servico contracts. Each `:de` and `:para`
169    /// must reference a name in `:membros`.
170    #[serde(default)]
171    pub contratos: Vec<crate::aplicacao::WitContract>,
172
173    /// Mesh-level policies (timeout, retries, circuit-breaker, mTLS,
174    /// rate-limit). Apply to every contrato unless overridden per-edge
175    /// in M4.
176    #[serde(default, skip_serializing_if = "Option::is_none")]
177    pub politicas: Option<crate::aplicacao::MeshPolicy>,
178
179    /// Placement strategy across the cluster fleet
180    /// (single-node | replicated | sharded).
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub placement: Option<crate::aplicacao::Placement>,
183
184    /// External entry point — gateway / ingress shape. Optional;
185    /// only for public Aplicacaos.
186    #[serde(default, skip_serializing_if = "Option::is_none")]
187    pub entrada: Option<crate::aplicacao::Entrada>,
188
189    // ── Acao slot (CANTEIRO §7.1-C) ──────────────────────────────────────
190    //
191    // Required when :kind Acao; ignored otherwise (mirrors the M2/
192    // supervisor-tree/M3 slot triads above — a declared-but-foreign `:ci`
193    // is a `LayoutError::CiOnNonAcao` build error, not a silent drop).
194    /// Typed CI run — a repo's CI run as a set of typed nodes + their
195    /// dependency edges. Required for `:kind Acao`; validated (not
196    /// rendered) by the `caixa-actions` renderer via
197    /// `canteiro_types::decompose`. See `caixa-actions`' crate docs for
198    /// the M0 validate-only contract.
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub ci: Option<canteiro_types::CiRun>,
201}
202
203/// Why reading a manifest into a [`Caixa`] failed.
204///
205/// Split from [`ManifestError`] (which reports a *parsed* manifest that is
206/// semantically wrong) because the two answer different questions, and the
207/// distinction is the whole point of this type: `ManifestError` means "your
208/// caixa is wrong", `LeituraError::DialetoEstrangeiro` means "this file is not
209/// a caixa".
210#[derive(Debug, thiserror::Error)]
211pub enum LeituraError {
212    /// The source is not readable as a `(defcaixa …)` package manifest — bad
213    /// syntax, a wrong head symbol, an unknown or mistyped slot.
214    ///
215    /// `#[source]`, not `#[error(transparent)]`. Transparent delegates
216    /// `source()` past the inner error to ITS source, which drops the
217    /// `LispError` off the cause chain — and `feira`'s
218    /// `load_caixa_parse_error_preserves_underlying_lisp_error_on_chain`
219    /// pins that a caller can `downcast_ref::<tatara_lisp::LispError>()`
220    /// through an anyhow context to read the typed payload. That pin caught
221    /// this exact regression when the variant first landed transparent.
222    #[error("{0}")]
223    Leitura(
224        #[source]
225        #[from]
226        tatara_lisp::LispError,
227    ),
228
229    /// The source IS a well-formed `(defcaixa …)` form, but of a different
230    /// declaration than this crate's.
231    ///
232    /// The variant that did not exist before, and whose absence is the defect.
233    /// A `(defcaixa :name "x" :ecosystem :go …)` used to reach the derive's
234    /// `parse_kwargs_strict` and come back as an unknown-keyword rejection —
235    /// byte-identical in shape to a typo in a real manifest. Measured over the
236    /// org checkout on 2026-07-31, that shape is the MAJORITY of the corpus, so
237    /// the confusing error was also the common one.
238    ///
239    /// Carrying the dialect means a consumer can branch on "not mine" without
240    /// re-parsing, and a census can count it. Every user-facing byte-string
241    /// (canonical keyword, one-line description, consuming crate) is a
242    /// projection of [`crate::dialeto::CaixaDialeto`] — the variant stores the
243    /// typed dialect and the `#[error]` template calls
244    /// [`CaixaDialeto::palavra_canonica`] /
245    /// [`CaixaDialeto::descricao`] / [`CaixaDialeto::consumidor`] on it, so
246    /// the three axes cannot silently diverge from the classification. Prior
247    /// to this closure the variant carried each accessor's return value as a
248    /// stored `&'static str` snapshot alongside `dialeto`, and the sole
249    /// constructor at [`Caixa::from_lisp`] filled all four fields — a caller
250    /// could construct `DialetoEstrangeiro { dialeto: Molde,
251    /// palavra_canonica: "defcaixa", … }` and every downstream consumer
252    /// (Display, ad-hoc audit, future JSON serialization) would silently
253    /// disagree with `dialeto.palavra_canonica() == "defmolde"`. The typed
254    /// enum owns the projections; the variant only carries the axis.
255    #[error(
256        "this is a `{palavra}` declaration ({desc}), read by \
257         {cons} — not a caixa-core package manifest. `defcaixa` is the \
258         tatara-lisp package manifest (`:nome :versao :kind :deps …`); the two \
259         are different declarations that shared one keyword until 2026-07-31",
260        palavra = dialeto.palavra_canonica(),
261        desc = dialeto.descricao(),
262        cons = dialeto.consumidor()
263    )]
264    DialetoEstrangeiro {
265        /// Which declaration this actually is. Sole authoritative axis;
266        /// every user-facing projection routes through
267        /// [`crate::dialeto::CaixaDialeto`]'s typed accessors so the four
268        /// axes cannot silently disagree.
269        dialeto: crate::dialeto::CaixaDialeto,
270    },
271
272    /// Not a manifest declaration at all.
273    #[error(transparent)]
274    Dialeto(#[from] crate::dialeto::DialetoError),
275}
276
277/// Substrate-canonical universal-axis per-[`Caixa`] `:licenca` SPDX-shaped
278/// license-expression fallback for the `Option<String>` `:licenca` slot —
279/// the `"MIT"` SPDX identifier every [`caixa-helm`]-rendered
280/// `lareira-<nome>` Helm chart's `README.md` `## License` section folds an
281/// author-omitted (`None`) `:licenca` slot through, extracted as a typed
282/// `pub const` so every substrate-side consumer that resolves "what license
283/// scalar does an author-omitted `:licenca` degrade onto?" reaches for
284/// exactly one substrate-primitive `&'static str`.
285///
286/// The `:licenca` fallback axis has one production consumer today — the
287/// [`caixa-helm`] `build_readme` fold at `caixa-helm/src/lib.rs`'s
288/// `caixa.licenca().unwrap_or(CAIXA_LICENCA_DEFAULT)` `README.md`
289/// `## License` section body — with three sibling caixa-core sites that
290/// cite the `"MIT"` fallback in prose (this crate's [`Caixa::licenca`]
291/// accessor's docstring, [`Self::validate_licenca`]'s docstring, and the
292/// [`ManifestError::LicencaEmpty`] `#[error]` template's user-facing text)
293/// all quoting the exact byte-string a future substrate-side rebrand of the
294/// fallback (a tightening to `"Apache-2.0"` as the substrate absorbs the
295/// wasm-component-model conventions the `wasi:*` WIT worlds already carry,
296/// a per-cluster license-default overlay the M4 CR materializer resolves
297/// per-CR, a promotion to the plain `Option<String>` byte-string into a
298/// richer `SpdxExpression` enum once the SPDX-expression parser lands per
299/// [`Self::validate_licenca`]'s docstring roadmap) would silently split
300/// against — the caixa-helm renderer would emit the new byte, the
301/// docstrings would still cite the prior byte, and every author who reads
302/// the accessor docstring before authoring would file a fresh
303/// `:licenca "MIT"` verbatim rather than defer to the substrate default,
304/// with the drift surfacing at chart-README-audit time far from the
305/// substrate rebrand commit.
306///
307/// Prior to this lift the sole production emitter (`build_readme`) carried
308/// an inline `"MIT"` byte literal at
309/// `caixa-helm/src/lib.rs:1018`'s `.unwrap_or("MIT")` fallback arm — one
310/// occurrence of the same load-bearing per-`Caixa` universal-axis
311/// SPDX-shaped license-expression convention as the four sibling caixa-core
312/// docstring citations, drift-prone by construction ahead of the second
313/// occurrence the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
314/// materializer's per-Aplicacao registry-annotation synthesis (the
315/// [`Self::validate_licenca`] roadmap already names the `Chart.yaml
316/// annotations["artifacthub.io/license"]` axis every registry-facing chart
317/// carries as the second consumer) will surface.
318///
319/// The `"MIT"` value pins the canonical CAIXA-SDLC §I license scaffold
320/// every `feira init`-emitted [`Self::template`] carries verbatim
321/// (`:licenca "MIT"`) and every substrate-side renderer fixture
322/// ([`caixa-helm`]'s `sample_caixa`, [`caixa-flux`]'s renderer fixtures,
323/// [`caixa-mesh`]'s renderer fixtures) seeds by construction, matching the
324/// pleme-io repo `LICENSE` header this workspace itself ships under. The
325/// alternatives an author declares explicitly (compound SPDX expressions
326/// like `"Apache-2.0 OR MIT"`, permissive-family peers like
327/// `"Apache-2.0"` / `"BSD-3-Clause"`, license-with-exception forms like
328/// `"Apache-2.0 WITH LLVM-exception"`) express deliberate license postures
329/// an author declares explicitly, never a posture an author-omitted slot
330/// should silently assume by default.
331///
332/// Lifted as a typed `pub const` so the substrate's chosen license
333/// fallback has exactly one source of truth on the `:licenca` fallback
334/// axis, on the same substrate-primitive lift discipline the peer
335/// per-`Caixa` load-bearing-scalar constants
336/// ([`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`],
337/// [`crate::version::DEFAULT_GIT_REMOTE`],
338/// [`crate::version::DEFAULT_PLEME_GIT_ORG`]) already carry on the sibling
339/// per-`Caixa` universal-axis publish-side convention surface, and the
340/// same discipline the sibling M2 per-supervisor default set carries
341/// end-to-end ([`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`],
342/// [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`],
343/// [`crate::supervisor::SUPERVISOR_RESTART_WINDOW_DEFAULT`],
344/// [`crate::supervisor::SUPERVISOR_CHILD_RESTART_DEFAULT`]) and the M3
345/// per-`:placement` default set already carries
346/// ([`crate::aplicacao::PLACEMENT_ESTRATEGIA_DEFAULT`]) on the paired
347/// M2 / M3 typed-slot-default axes. First typed default on the outer
348/// top-level [`Caixa`] universal-axis surface to converge onto the
349/// substrate-primitive-lift discipline the M2 / M3 typed-slot families
350/// already carry.
351pub const CAIXA_LICENCA_DEFAULT: &str = "MIT";
352
353impl Caixa {
354    /// Parse a `caixa.lisp` source string to a typed `Caixa`.
355    ///
356    /// Classifies the dialect **before** parsing. A `(defcaixa …)` of another
357    /// declaration is [`LeituraError::DialetoEstrangeiro`], naming what it is
358    /// and who reads it, instead of an unknown-keyword rejection that reads as
359    /// "your manifest is broken".
360    ///
361    /// The ordering is load-bearing. Handing a foreign dialect to the derive
362    /// first and interpreting the failure afterwards would mean guessing from
363    /// an error message, and the guess would be wrong for every file whose
364    /// first unknown slot happens to be one both schemas could plausibly carry.
365    pub fn from_lisp(src: &str) -> Result<Self, LeituraError> {
366        use tatara_lisp::domain::TataraDomain;
367        let forms = tatara_lisp::read(src).map_err(LeituraError::Leitura)?;
368        let first = forms.first().ok_or(crate::dialeto::DialetoError::Vazio)?;
369
370        // Route the foreign-dialect rejection gate through the lifted
371        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
372        // typed predicate rather than the pre-lift hand-rolled three-arm
373        // `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
374        // literal — the `defmolde` declaration-family partition (the two-
375        // arity closure of [`crate::dialeto::CaixaDialeto::Molde`] and
376        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two arms
377        // whose sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
378        // projection already collapses onto `"defmolde"` and whose sibling
379        // [`crate::dialeto::CaixaDialeto::consumidor`] projection already
380        // collapses onto `"pleme-doc-gen"`) resolves through one dispatch
381        // on the substrate primitive. `Pacote` (the tatara-lisp package
382        // manifest this derive can parse) and `Desconhecido` (deliberately
383        // falls through to the derive rather than short-circuiting: a
384        // `(defcaixa …)` matching neither schema is most likely a genuine
385        // package manifest with a typo in `:nome`, and the derive's
386        // diagnostic — which names the offending keyword and suggests the
387        // nearest slot — is far better than anything this classifier
388        // could say) both return `false` from `is_molde_family()` and fall
389        // through to the derive. Only the typed dialect flows into the
390        // error — the three user-facing projections (canonical keyword,
391        // description, consumer) are read at Display time through
392        // [`crate::dialeto::CaixaDialeto`]'s own accessors, so the
393        // variant cannot carry a snapshot that drifts from
394        // [`crate::dialeto::CaixaDialeto::palavra_canonica`] /
395        // `descricao` / `consumidor`. A future fifth dialect the
396        // [`crate::dialeto`] module doc's "third dialect" hazard
397        // actualises that belongs to the `defmolde` family lands one
398        // match arm at [`crate::dialeto::CaixaDialeto::is_molde_family`]
399        // and this gate picks up the new arm by construction — the pre-
400        // lift wildcard `foreign =>` was compile-time-anonymous and would
401        // silently absorb any hypothetical fifth `defcaixa`-family arm as
402        // foreign; routing the partition through the typed predicate
403        // closes both drift surfaces.
404        let dialeto = crate::dialeto::classify_form(first)?;
405        if dialeto.is_molde_family() {
406            return Err(LeituraError::DialetoEstrangeiro { dialeto });
407        }
408
409        Self::compile_from_sexp(first).map_err(LeituraError::Leitura)
410    }
411
412    /// Register `Caixa` with the global tatara-lisp domain registry so
413    /// `defcaixa` is dispatchable from any tatara-lisp binary that seeds
414    /// the registry (e.g. `tatara-check`).
415    ///
416    /// Returns the typed [`tatara_lisp::KeywordCollision`] on the second
417    /// (and every subsequent) call in the same process — one keyword,
418    /// one type, per process is a hard invariant of the upstream
419    /// registry, and a caller that hits it must fix its crate graph
420    /// rather than swallowing the error. Peer of the sibling per-crate
421    /// `register()` entry points at `caixa-flake/src/flake.rs`,
422    /// `caixa-fmt/src/lisp_config.rs`, `caixa-lacre/src/lock.rs`,
423    /// `caixa-lint/src/lisp_config.rs`, `caixa-resolver/src/lisp_config.rs`
424    /// — every substrate crate that owns a tatara-lisp keyword now
425    /// propagates the same typed error verbatim, so a downstream binary
426    /// that seeds the registry (`tatara-check`, the future LSP) reaches
427    /// for one shape at every call site.
428    ///
429    /// # Errors
430    ///
431    /// [`tatara_lisp::KeywordCollision`] when a peer type has already
432    /// claimed the `defcaixa` keyword in this process.
433    pub fn register() -> Result<(), tatara_lisp::KeywordCollision> {
434        tatara_lisp::domain::register::<Self>()
435    }
436
437    /// Substrate-canonical per-`Caixa` `:licenca` SPDX-expression scalar
438    /// accessor every consumer of the top-level manifest's license axis
439    /// keys off — returns the author-declared `:licenca` byte-string
440    /// verbatim as an `Option<&str>`, borrowed from the typed slot's own
441    /// `Option<String>` storage. `None` when the slot is absent (the
442    /// canonical "omit to defer to the caixa-helm renderer's `MIT`
443    /// fallback" shape [`Self::validate_licenca`] documents at
444    /// caixa-core/src/manifest.rs:1560; the peer [`caixa-helm`]
445    /// `build_readme` fold at caixa-helm/src/lib.rs:962 reads this
446    /// predicate too, so an authored-but-unset `:licenca` round-trips to
447    /// a rendered `lareira-<nome>` chart's `README.md` `## License`
448    /// section structurally identical to one that omits the slot).
449    ///
450    /// The `:licenca` slot carries the universal-axis SPDX-expression
451    /// license identifier every kind of caixa emits under (CAIXA-SDLC
452    /// §I — the author-facing surface every `defcaixa` form supplies) —
453    /// the typed slot's `Option<String>` accept-set (empty-string
454    /// rejected through [`ManifestError::LicencaEmpty`], SPDX-alphabet-
455    /// invalid rejected through [`ManifestError::LicencaInvalid`]) maps
456    /// onto the `lareira-<nome>` Helm chart's `README.md` `## License`
457    /// section (caixa-helm/src/lib.rs:962) and (through future
458    /// tightening documented at [`Self::validate_licenca`]) the
459    /// Chart.yaml `annotations["artifacthub.io/license"]` axis every
460    /// registry-facing chart carries. Every downstream consumer that
461    /// reads the license byte-string keys off this scalar (the
462    /// [`Self::validate_licenca`] empty-arm + SPDX-shape gate that
463    /// routes through `self.licenca.as_deref()`, the caixa-helm
464    /// `build_readme` `unwrap_or_else(|| "MIT".into())` fold that keys
465    /// the fallback off the `Option::is_none()` arm, every future
466    /// per-`Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
467    /// acknowledges).
468    ///
469    /// Prior to this lift the `.licenca` field was accessed inline at
470    /// two production sites — [`Self::validate_licenca`]'s
471    /// `self.licenca.as_deref()` empty-and-shape gate binding and the
472    /// caixa-helm `build_readme` `caixa.licenca.clone().unwrap_or_else(||
473    /// "MIT".into())` `README.md` `## License` fold — two open-coded
474    /// field-accesses that expressed no compile-time link back to the
475    /// typed slot. A future extension of the `:licenca` axis to a
476    /// richer author surface — a per-`:licenca` structured SPDX
477    /// expression parser + license-id allowlist (the future tightening
478    /// [`Self::validate_licenca`]'s docstring acknowledges), a
479    /// per-cluster license-default overlay the M4 CR materializer
480    /// resolves per-CR (the "cluster policy pins `Apache-2.0` for every
481    /// unlisted caixa" arm), a promotion of the plain
482    /// `Option<String>` byte-string to a richer `SpdxExpression` enum
483    /// once the SPDX-expression parser lands — would have had to be
484    /// threaded through both open-coded copies in lockstep or the
485    /// validate gate and the caixa-helm emit path would silently
486    /// disagree on which license a given [`Caixa`] resolves to (an
487    /// author's `:licenca "MIT OR Apache-2.0"` would satisfy validate
488    /// while the emit path silently rendered a stale `MIT` fallback,
489    /// or vice versa). Lifting the resolution to a typed method on the
490    /// substrate primitive means every downstream consumer of the
491    /// caixa's per-`Caixa` license surface reaches for exactly one
492    /// typed dispatch — the resolver's accept-set migrates as a unit
493    /// on any future axis addition.
494    ///
495    /// First `Option<&str>`-return top-level [`Caixa`] scalar accessor —
496    /// opens the "outer [`Caixa`] `Option<&str>` scalar" projection
497    /// pattern the sibling per-`Caixa` `:descricao` / `:repositorio` /
498    /// `:edicao` future lifts fold on. Same "one typed dispatch on the
499    /// substrate primitive, thin projections at each consumer"
500    /// discipline the peer per-`:placement` [`crate::aplicacao::Placement::shard_key`]
501    /// (7cd2a28) / [`crate::aplicacao::Placement::affinity`] (74ec2d3)
502    /// / per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
503    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
504    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
505    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
506    /// typed-slot atom axes, extended here to the outer top-level
507    /// `Caixa` universal-axis surface. Named `licenca()` to match the
508    /// storage field's name; the accessor's identity maps onto the
509    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
510    /// carries.
511    #[must_use]
512    pub const fn licenca(&self) -> Option<&str> {
513        match &self.licenca {
514            Some(s) => Some(s.as_str()),
515            None => None,
516        }
517    }
518
519    /// Substrate-canonical per-`Caixa` `:repositorio` git-repo-URL scalar
520    /// accessor every consumer of the top-level manifest's homepage /
521    /// source-of-truth axis keys off — returns the author-declared
522    /// `:repositorio` byte-string verbatim as an `Option<&str>`, borrowed
523    /// from the typed slot's own `Option<String>` storage. `None` when
524    /// the slot is absent (the canonical "omit to defer to the renderer's
525    /// per-target placeholder" shape — [`caixa-helm`]'s `ChartYaml.home`
526    /// carries the `Option<String>` through verbatim so an author-omitted
527    /// `:repositorio` renders a `Chart.yaml` without a `home:` field
528    /// (`skip_serializing_if = "Option::is_none"`), while [`caixa-flux`]'s
529    /// `ClusterBundleOpts::for_caixa` folds the omitted slot through a
530    /// `format!("https://github.com/{DEFAULT_PLEME_GIT_ORG}/{nome}")`
531    /// fallback derived from `caixa.nome`).
532    ///
533    /// The `:repositorio` slot carries the universal-axis git-repo-URL
534    /// homepage identifier every kind of caixa emits under (CAIXA-SDLC
535    /// §I — the author-facing surface every `defcaixa` form supplies) —
536    /// the typed slot's `Option<String>` accept-set (empty-string
537    /// rejected through [`ManifestError::RepositorioEmpty`], git-repo-URL-
538    /// shape-invalid rejected through [`ManifestError::RepositorioInvalid`]
539    /// past the shared [`crate::render::is_git_repo_url`] predicate the
540    /// peer per-`:deps :fonte :repo` axis also routes through) maps onto
541    /// four load-bearing downstream consumers:
542    ///
543    ///   - [`Self::validate_repositorio`]'s empty-arm + shape-predicate
544    ///     gate binding at caixa-core/src/manifest.rs:1456 — the
545    ///     universal-axis identity gate wired at caixa-build time.
546    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.home` fold at
547    ///     caixa-helm/src/lib.rs:840 — the rendered `lareira-<nome>`
548    ///     Helm chart's `Chart.yaml` `home:` field, which every registry
549    ///     that ingests the chart (ArtifactHub, chartmuseum,
550    ///     `helm search repo`) surfaces as the chart's canonical source-
551    ///     of-truth link.
552    ///   - [`caixa-helm`]'s `build_readme` `## Source` fold at
553    ///     caixa-helm/src/lib.rs:957 — the rendered `lareira-<nome>`
554    ///     chart's `README.md` header link back to the source repo,
555    ///     which every author who inspects the rendered chart bundle
556    ///     lands at.
557    ///   - [`caixa-flux`]'s `ClusterBundleOpts::for_caixa`
558    ///     `GitRepository.spec.url` fold at caixa-flux/src/lib.rs:2006 —
559    ///     the rendered `GitRepository` CR's `spec.url` field, which
560    ///     FluxCD's `source-controller` polls to reconcile the caixa's
561    ///     manifest bundle from git.
562    ///
563    /// Prior to this lift the `.repositorio` field was accessed inline
564    /// at four production sites — [`Self::validate_repositorio`]'s
565    /// `self.repositorio.as_deref()` empty-and-shape gate binding, the
566    /// caixa-helm `build_chart_yaml` `caixa.repositorio.clone()`
567    /// `Chart.yaml` `home:` field fold, the caixa-helm `build_readme`
568    /// `caixa.repositorio.clone().unwrap_or_else(|| caixa.nome.clone())`
569    /// `README.md` `## Source` fold, and the caixa-flux
570    /// `ClusterBundleOpts::for_caixa`
571    /// `caixa.repositorio.clone().unwrap_or_else(|| format!(...))`
572    /// `GitRepository.spec.url` fold — four open-coded field-accesses
573    /// that expressed no compile-time link back to the typed slot. A
574    /// future extension of the `:repositorio` axis to a richer author
575    /// surface — a per-`:repositorio` structured
576    /// [`crate::render::GitRepoUrl`]-shaped scheme+host+path parse
577    /// (the future tightening [`Self::validate_repositorio`]'s
578    /// docstring anticipates alongside the peer per-`:deps :fonte
579    /// :repo` axis), a per-cluster repo-mirror overlay the M4 CR
580    /// materializer resolves per-CR (the "cluster policy rewrites
581    /// `github:pleme-io/...` to `git.internal/mirror/pleme-io/...`"
582    /// arm the private-registry story acknowledges), a promotion of
583    /// the plain `Option<String>` byte-string to a richer
584    /// `RepoUrl` enum discriminated on scheme — would have had to be
585    /// threaded through all four open-coded copies in lockstep or the
586    /// validate gate and the three emit paths would silently disagree
587    /// on which URL a given [`Caixa`] resolves to (an author's
588    /// `:repositorio "github:pleme-io/checkout"` would satisfy validate
589    /// while one of the emit paths silently rendered a stale URL, or
590    /// vice versa). Lifting the resolution to a typed method on the
591    /// substrate primitive means every downstream consumer of the
592    /// caixa's per-`Caixa` repo-URL surface reaches for exactly one
593    /// typed dispatch — the resolver's accept-set migrates as a unit on
594    /// any future axis addition.
595    ///
596    /// Second outer top-level [`Caixa`] `Option<&str>`-return scalar
597    /// accessor — sibling of [`Self::licenca`] (6d5bc28), the accessor
598    /// that opened the "outer [`Caixa`] `Option<&str>` scalar"
599    /// projection pattern this lift folds on. Same "one typed dispatch
600    /// on the substrate primitive, thin projections at each consumer"
601    /// discipline the peer per-`:placement`
602    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
603    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
604    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
605    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
606    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
607    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
608    /// typed-slot atom axes, extended here to the second outer top-level
609    /// `Caixa` universal-axis surface. Named `repositorio()` to match
610    /// the storage field's name; the accessor's identity maps onto the
611    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
612    /// carries.
613    #[must_use]
614    pub const fn repositorio(&self) -> Option<&str> {
615        match &self.repositorio {
616            Some(s) => Some(s.as_str()),
617            None => None,
618        }
619    }
620
621    /// Substrate-canonical per-`Caixa` **resolved-git-repo-URL** composer —
622    /// returns the caixa's canonical git-source-of-truth URL as an owned
623    /// [`String`], author-declared `:repositorio` byte-string verbatim on
624    /// the `Some` arm and the substrate's canonical pleme-org github URL
625    /// fallback ([`crate::DEFAULT_PLEME_GIT_ORG`] and [`Self::nome`]
626    /// interpolated into `https://github.com/<org>/<nome>`) on the
627    /// `None` arm. Every substrate-side consumer that resolves
628    /// "which git URL does this caixa's source live at?" reaches for
629    /// exactly one typed dispatch on the substrate primitive — the raw
630    /// `caixa.repositorio().map(str::to_owned).unwrap_or_else(|| format!(
631    /// "https://github.com/{org}/{nome}", org = DEFAULT_PLEME_GIT_ORG,
632    /// nome = caixa.nome()))` open-coded composition every prior caller
633    /// re-derived collapses onto one canonical arm.
634    ///
635    /// Distinct from [`Self::repositorio`] (`Option<&str>`, exposes the
636    /// author-omitted / author-declared partition to the caller) — this
637    /// accessor is the **resolved** URL surface, folding the fallback in
638    /// at the substrate-primitive boundary. Every consumer that keys off
639    /// the `Option::is_none()` discriminator (a [`Chart.yaml`] `home:`
640    /// field emit that must omit the field entirely on an author-omitted
641    /// `:repositorio`, per the [`Self::repositorio`] docstring's
642    /// documented four-consumer list) reaches through the raw
643    /// [`Self::repositorio`] `Option<&str>` accessor by construction — the
644    /// resolved-URL composer sits alongside it as the second projection
645    /// on the same underlying `:repositorio` slot rather than replacing
646    /// the raw accessor.
647    ///
648    /// The fallback branch is the exact byte-image of the prior inline
649    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url` composer at
650    /// caixa-flux/src/lib.rs:2080 — pinned by the sibling caixa-flux
651    /// byte-parity test
652    /// `cluster_bundle_opts_for_caixa_git_url_routes_through_canonical_git_url_accessor`
653    /// against a future implementation of this method that reordered the
654    /// `format!` template arguments, migrated the `<org>` segment to a
655    /// different constant (the [`crate::DEFAULT_PLEME_GIT_ORG`] axis a
656    /// future substrate-side git-org migration may split off), or
657    /// silently absorbed the empty-string arm (a hypothetical
658    /// `Some("") → fallback` collapse the raw [`Self::repositorio`]
659    /// accessor's docstring explicitly rejects on the sibling raw
660    /// accessor).
661    ///
662    /// Peer of the sibling per-`&Caixa`-axis composed helpers
663    /// [`caixa-flux::cluster_bundle_for_caixa`] (06d52d7) on the sibling
664    /// substrate-side renderer surface — same "close the composed
665    /// substrate-primitive at one canonical arm on the single-`&Caixa`
666    /// dispatch, converge every prior open-coded caller onto the arm"
667    /// discipline extended onto the resolved-git-URL projection of the
668    /// per-`Caixa` `:repositorio` axis. Owns per-call [`String`]
669    /// allocation on both arms (the `Some` arm's `str::to_owned` and the
670    /// `None` arm's `format!`) — the by-value return matches every
671    /// downstream consumer's field-fill shape (the caixa-flux
672    /// `ClusterBundleOpts::git_url: String` field, every future
673    /// `Chart.yaml` `home:` fold's `Option<String>` field-fill on the
674    /// `Some` arm).
675    #[must_use]
676    pub fn canonical_git_url(&self) -> String {
677        self.repositorio().map_or_else(
678            || {
679                format!(
680                    "https://github.com/{org}/{nome}",
681                    org = crate::DEFAULT_PLEME_GIT_ORG,
682                    nome = self.nome(),
683                )
684            },
685            str::to_owned,
686        )
687    }
688
689    /// Substrate-canonical per-`Caixa` **resolved-publish-tag** composer —
690    /// returns the caixa's canonical Zig-style git-publish-tag as an owned
691    /// [`String`], derived by concatenating
692    /// [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] with the typed
693    /// [`Self::versao`] byte-string on a single `format!` template.
694    /// Every substrate-side consumer that resolves "which git tag does this
695    /// caixa publish under?" reaches for exactly one typed dispatch on the
696    /// substrate primitive — the raw `format!("{prefix}{versao}", prefix =
697    /// caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao = caixa.versao())`
698    /// open-coded composition every prior caller re-derived collapses onto
699    /// one canonical arm.
700    ///
701    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
702    /// git-URL composer on the paired per-`Caixa` git-remote axis — same
703    /// "close the composed substrate-primitive at one canonical arm on the
704    /// single-`&Caixa` dispatch, converge every prior open-coded caller
705    /// onto the arm" discipline extended from the resolved-URL projection
706    /// of the per-`Caixa` `:repositorio` axis onto the resolved-tag
707    /// projection of the per-`Caixa` `:versao` axis. The two accessors
708    /// jointly close the pair of scalars every `FluxCD` `GitRepository` CR
709    /// keys off (`spec.url` via [`Self::canonical_git_url`],
710    /// `spec.ref.tag` via [`Self::publish_tag`]) at the substrate primitive
711    /// — a downstream consumer that reaches through both accessors reads
712    /// the complete published-git-identity of a caixa through two typed
713    /// dispatches, not four open-coded field accesses.
714    ///
715    /// The reader-side (`caixa-flux::cluster_bundle` /
716    /// `ClusterBundleOpts::for_caixa`'s `git_ref` field, every future
717    /// per-cluster snapshot bundle emitter, the future M4
718    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's tag-carrier
719    /// slot on the tatara `Process` intent) always resolves the tag under
720    /// the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] prefix — this
721    /// method encodes that reader-side convention. The writer-side
722    /// (`caixa-feira`'s `feira publish` `--prefix` clap flag) allows the
723    /// operator to override the prefix at publish time; the two surfaces
724    /// intentionally sit on the "canonical default + operator override"
725    /// pair the sibling [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] constant's
726    /// own docstring documents — a `feira publish --prefix release/`
727    /// override is the operator's explicit opt-out from the substrate
728    /// default, not a supported drift axis.
729    ///
730    /// The composition body is the exact byte-image of the prior inline
731    /// [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_ref` composer at
732    /// caixa-flux/src/lib.rs:2105 — pinned by the sibling caixa-flux
733    /// byte-parity test
734    /// `cluster_bundle_opts_for_caixa_git_ref_routes_through_publish_tag_accessor`
735    /// against a future implementation of this method that reordered the
736    /// `format!` template arguments, migrated the `<prefix>` segment to a
737    /// different constant (the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] axis
738    /// a future Zig-style-tag rebrand may split off — the constant's own
739    /// docstring anticipates a substrate-side move to `release/<versao>`
740    /// or bare `<versao>` shapes once a sibling forge convention adopts a
741    /// slash-namespaced or bare-scalar form), interposed a canonicalization
742    /// pass on the `:versao` axis (a SemVer-2 build-metadata strip an OCI-
743    /// tag normalizer might apply once the M4 registry-alignment slot
744    /// lands), or silently absorbed an empty `:versao` arm (which cannot
745    /// occur past the [`Self::validate_versao`] gate but which a
746    /// hypothetical bypass on the accessor path must not silently paper
747    /// over).
748    ///
749    /// Owns per-call [`String`] allocation via the single `format!`
750    /// invocation — the by-value return matches every downstream
751    /// consumer's field-fill shape (the caixa-flux `GitRefSpec::Tag(String)`
752    /// variant's owned payload, every future `intent.aplicacao.tag: String`
753    /// field-fill on the M4 CR materializer's tag-carrier slot).
754    #[must_use]
755    pub fn publish_tag(&self) -> String {
756        format!(
757            "{prefix}{versao}",
758            prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
759            versao = self.versao(),
760        )
761    }
762
763    /// Substrate-canonical per-`Caixa` **resolved-Helm-chart-name** composer
764    /// — returns the caixa's canonical `lareira-<nome>` per-Servico Helm
765    /// chart identity as an owned [`String`], derived by dispatching through
766    /// the substrate-canonical [`crate::lareira_chart_name`] helper against
767    /// the typed [`Self::nome`] byte-string. Every substrate-side consumer
768    /// that resolves "which Helm chart identity does this caixa render
769    /// under?" reaches for exactly one typed dispatch on the substrate
770    /// primitive — the raw `caixa_core::lareira_chart_name(caixa.nome())`
771    /// two-step compose every prior caller re-derived collapses onto one
772    /// canonical arm on the single-`&Caixa` dispatch.
773    ///
774    /// Peer of the sibling [`Self::canonical_git_url`] (124f864) resolved-
775    /// git-URL composer + [`Self::publish_tag`] (07e05b8) resolved-publish-
776    /// tag composer on the paired per-`Caixa` published-artifact-identity
777    /// axis — same "close the composed substrate-primitive at one canonical
778    /// arm on the single-`&Caixa` dispatch, converge every prior open-coded
779    /// caller onto the arm" discipline extended from the resolved-URL /
780    /// resolved-tag projections of the `:repositorio` / `:versao` axes onto
781    /// the resolved-chart-name projection of the `:nome` axis. The three
782    /// accessors jointly close the triple of scalars every per-Servico
783    /// deploy artifact keys off (git source URL via
784    /// [`Self::canonical_git_url`], git source tag via
785    /// [`Self::publish_tag`], per-Servico Helm chart identity via
786    /// [`Self::lareira_chart_name`]) at the substrate primitive — a
787    /// downstream consumer that reaches through all three reads the
788    /// complete deploy-artifact identity of a caixa through three typed
789    /// dispatches, not six open-coded compositions across three renderer
790    /// crates.
791    ///
792    /// The reader-side (three production sites at the time of the lift —
793    /// [`caixa-helm::render_chart_for_servico_with`]'s `ChartDir.name`
794    /// composer at caixa-helm/src/lib.rs:778, the peer
795    /// [`caixa-flux::cluster_bundle`]'s per-CR `chart_name` binding at
796    /// caixa-flux/src/lib.rs:2219, and
797    /// [`caixa-tatara::process_for_aplicacao`]'s `release_name`
798    /// composer at caixa-tatara/src/lib.rs:227, plus every future
799    /// per-Servico OCI publish emitter the CAIXA-SDLC §II
800    /// `caixa-publish.yml` reusable workflow's `skopeo push` step keys
801    /// off, the future per-cluster snapshot bundle emitter, the future
802    /// M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's
803    /// per-member chart-carrier slot on the tatara `Process` intent) —
804    /// always resolves the chart name under the canonical
805    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] prefix; this method encodes
806    /// that reader-side convention. The joint-length invariant the peer
807    /// [`Self::validate_nome_chart_name_budget`] gate enforces at
808    /// caixa-build time (author-declared `:nome` + fixed prefix ≤
809    /// [`crate::DNS_1123_LABEL_MAX_LEN`]) is verified on the input to
810    /// this composer by construction, so the produced `lareira-<nome>`
811    /// string is a valid Helm chart-name segment on every accept-set
812    /// input.
813    ///
814    /// The composition body is the exact byte-image of the prior inline
815    /// `caixa_core::lareira_chart_name(caixa.nome())` two-step form every
816    /// prior caller re-derived — pinned by the sibling caixa-helm /
817    /// caixa-flux / caixa-tatara byte-parity tests
818    /// `<crate>_lareira_chart_name_routes_through_caixa_accessor` against
819    /// a future implementation of this method that reordered the
820    /// composition arguments, migrated the `<prefix>` segment to a
821    /// different constant (the [`crate::LAREIRA_CHART_NAME_PREFIX`] axis a
822    /// future substrate-side chart-family rebrand may split off — the
823    /// constant's own docstring anticipates a substrate-side move once
824    /// the `lareira-` scoping intent outlives the family it names),
825    /// interposed a canonicalization pass on the `:nome` axis (a per-
826    /// registry namespace-qualification an M4 CR materializer might apply
827    /// per-CR — the "`pleme-io/checkout` vs `partner-org/checkout`
828    /// collision" arm the multi-tenant-registry story acknowledges), or
829    /// silently absorbed an empty `:nome` arm (which cannot occur past
830    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
831    /// on the accessor path must not silently paper over).
832    ///
833    /// Owns per-call [`String`] allocation via the single
834    /// [`crate::lareira_chart_name`] `format!` invocation — the by-value
835    /// return matches every downstream consumer's field-fill shape (the
836    /// caixa-helm `ChartDir.name: String` field, the caixa-flux per-CR
837    /// `chart_name: String` binding, the caixa-tatara
838    /// `AplicacaoIntent.release_name: Option<String>` field-fill on the
839    /// `Some` arm).
840    #[must_use]
841    pub fn lareira_chart_name(&self) -> String {
842        crate::lareira_chart_name(self.nome())
843    }
844
845    /// Substrate-canonical per-`Caixa` **resolved-OCI-chart-ref** composer
846    /// — returns the caixa's canonical `oci://<registry>/lareira-<nome>`
847    /// per-Servico Helm chart OCI artifact reference as an owned
848    /// [`String`], derived by dispatching through the substrate-canonical
849    /// [`crate::oci_chart_ref`] helper (which itself composes
850    /// [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied `registry` +
851    /// [`crate::lareira_chart_name`]-of-[`Self::nome`]) against the
852    /// caller-supplied `registry` and the typed [`Self::nome`] byte-string.
853    /// Every substrate-side consumer that resolves "which OCI chart
854    /// artifact does this caixa publish under, in this registry?" reaches
855    /// for exactly one typed dispatch on the substrate primitive — the raw
856    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step compose
857    /// every prior caller re-derived collapses onto one canonical arm on
858    /// the single-`(&Caixa, &str)` dispatch.
859    ///
860    /// Fourth member of the paired per-`Caixa` published-artifact-identity
861    /// axis alongside [`Self::canonical_git_url`] (124f864) /
862    /// [`Self::publish_tag`] (07e05b8) / [`Self::lareira_chart_name`]
863    /// (a8f0bee) — same "close the composed substrate-primitive at one
864    /// canonical arm on the single-`&Caixa` dispatch, converge every
865    /// prior open-coded caller onto the arm" discipline extended from the
866    /// resolved-URL / resolved-tag / resolved-chart-name projections of
867    /// the `:repositorio` / `:versao` / `:nome` axes onto the resolved-
868    /// OCI-ref projection over the paired `(registry, :nome)` inputs. The
869    /// four accessors jointly close the per-`Caixa` published-artifact-
870    /// identity surface every downstream consumer of a caixa's published
871    /// deploy artifacts keys off (git source URL via
872    /// [`Self::canonical_git_url`], git source tag via
873    /// [`Self::publish_tag`], per-Servico Helm chart identity via
874    /// [`Self::lareira_chart_name`], per-registry OCI chart artifact
875    /// reference via [`Self::oci_chart_ref`]) at the substrate primitive
876    /// — a downstream consumer that reaches through all four reads the
877    /// complete deploy-artifact identity of a caixa through four typed
878    /// dispatches, not eight open-coded compositions across four renderer
879    /// crates. The unique-signature dispatch (`(&Caixa, &str)` on this
880    /// method vs. `&Caixa` on the sibling three) reflects the extra input
881    /// axis this composer folds in: unlike the git-URL / git-tag / chart-
882    /// name axes (each derived purely from a `&Caixa`), the OCI-ref axis
883    /// pairs the caixa's per-`:nome` chart identity with the caller-
884    /// supplied per-registry authority segment, so the accessor threads
885    /// the registry byte-string through as a positional `&str`.
886    ///
887    /// The reader-side (one production site at the time of the lift —
888    /// [`caixa-tatara::process_for_aplicacao`]'s `derive_chart_ref` helper
889    /// at caixa-tatara/src/lib.rs:333 that composes the emitted
890    /// `AplicacaoIntent.chart_ref` scalar the tatara-reconciler feeds into
891    /// `helm install`, plus every future per-Servico OCI publish emitter
892    /// the CAIXA-SDLC §II `caixa-publish.yml` reusable workflow's
893    /// `skopeo push` step keys off, the future per-cluster snapshot bundle
894    /// emitter's per-CR `oci://…` field-fill on the M4 registry-alignment
895    /// slot, the future M4 `mesh.pleme.io/v1alpha1/Aplicacao` CR
896    /// materializer's per-member `chart_ref` slot on the tatara `Process`
897    /// intent, the `FluxCD` `HelmRelease` `spec.chart.spec.chart` field-fill
898    /// on the OCI-source path an M4 per-cluster registry-rewrite overlay
899    /// applies per-CR) — always resolves the OCI ref under the canonical
900    /// [`crate::OCI_SCHEME_PREFIX`] scheme prefix + the canonical
901    /// [`Self::lareira_chart_name`] chart-name segment; this method
902    /// encodes that reader-side convention.
903    ///
904    /// The composition body is the exact byte-image of the prior inline
905    /// `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step form
906    /// every prior caller re-derived — pinned by the sibling caixa-tatara
907    /// byte-parity test
908    /// `derive_chart_ref_routes_through_caixa_oci_chart_ref_accessor`
909    /// against a future implementation of this method that reordered the
910    /// composition arguments, migrated the `<scheme>` segment to a
911    /// different constant (the [`crate::OCI_SCHEME_PREFIX`] axis a future
912    /// substrate-side registry-protocol rebrand may split off — the
913    /// constant's own docstring anticipates a substrate-side move once
914    /// Helm 3 / `FluxCD` introduce a successor scheme past `oci://`),
915    /// migrated the `<chart>` segment off the paired
916    /// [`crate::lareira_chart_name`] composer (a per-registry
917    /// namespace-qualification an M4 CR materializer might apply per-CR),
918    /// interposed a canonicalization pass on the `registry` axis (an OCI-
919    /// authority normalization once the M4 registry-alignment slot lands),
920    /// or silently absorbed an empty `:nome` arm (which cannot occur past
921    /// the [`Self::validate_nome`] gate but which a hypothetical bypass
922    /// on the accessor path must not silently paper over).
923    ///
924    /// Owns per-call [`String`] allocation via the single
925    /// [`crate::oci_chart_ref`] `format!` invocation — the by-value return
926    /// matches every downstream consumer's field-fill shape (the caixa-
927    /// tatara `AplicacaoIntent.chart_ref: String` field-fill, every
928    /// future `intent.aplicacao.chart_ref: String` field-fill on the M4
929    /// CR materializer's chart-ref-carrier slot, every future
930    /// `HelmRelease.spec.chart.spec.chart: String` field-fill on the OCI-
931    /// source path).
932    #[must_use]
933    pub fn oci_chart_ref(&self, registry: &str) -> String {
934        crate::oci_chart_ref(registry, self.nome())
935    }
936
937    /// Substrate-canonical per-`Caixa` `:descricao` free-form-prose
938    /// chart-description scalar accessor every consumer of the top-level
939    /// manifest's Chart.yaml `description:` axis keys off — returns the
940    /// author-declared `:descricao` byte-string verbatim as an
941    /// `Option<&str>`, borrowed from the typed slot's own
942    /// `Option<String>` storage. `None` when the slot is absent (the
943    /// canonical "omit to defer to the per-renderer `caixa.nome`-derived
944    /// fallback" shape — [`caixa-helm`]'s `build_chart_yaml` folds the
945    /// omitted slot through a `format!("Generated chart for caixa Servico
946    /// {}", caixa.nome)` fallback, [`caixa-helm`]'s `build_readme` folds
947    /// it through a `format!("caixa Servico {}", caixa.nome)` fallback,
948    /// and [`caixa-feira`]'s `render_flake` folds it through a
949    /// `format!("caixa {}", c.nome)` `flake.nix` `description = ""`
950    /// fallback — each derived from `caixa.nome` on the null-carrier arm).
951    ///
952    /// The `:descricao` slot carries the universal-axis free-form-prose
953    /// chart-description identifier every kind of caixa emits under
954    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa` form
955    /// supplies) — the typed slot's `Option<String>` accept-set
956    /// (empty-string rejected through [`ManifestError::DescricaoEmpty`],
957    /// chart-description-shape-invalid rejected through
958    /// [`ManifestError::DescricaoInvalid`] past the shared
959    /// [`crate::render::is_chart_description_shape`] predicate the peer
960    /// per-`Caixa` `:descricao` axis also routes through) maps onto four
961    /// load-bearing downstream consumers:
962    ///
963    ///   - [`Self::validate_descricao`]'s empty-arm + shape-predicate
964    ///     gate binding — the universal-axis identity gate wired at
965    ///     caixa-build time.
966    ///   - [`caixa-helm`]'s `build_chart_yaml` `ChartYaml.description`
967    ///     `Chart.yaml` field fold — the rendered `lareira-<nome>` Helm
968    ///     chart's `Chart.yaml` `description:` field, which
969    ///     `apiVersion: v2` charts require non-empty (`helm lint` fires
970    ///     `WARNING [chart.metadata.description]: description is required`
971    ///     when absent) and which every registry that ingests the chart
972    ///     (ArtifactHub, chartmuseum, `helm search repo`) surfaces as the
973    ///     chart's canonical one-line prose descriptor.
974    ///   - [`caixa-helm`]'s `build_readme` chart-`README.md` header fold
975    ///     — the rendered `lareira-<nome>` chart's `README.md` prose
976    ///     header directly beneath the `# <chart-name>` title, which
977    ///     every author who inspects the rendered chart bundle lands at.
978    ///   - [`caixa-feira`]'s `render_flake` `flake.nix` `description = ""`
979    ///     top-level fold — the emitted `flake.nix`'s `description`
980    ///     field, which every Nix consumer (`nix flake show`,
981    ///     `nix flake metadata`, downstream flake-registry ingestors)
982    ///     surfaces as the flake's canonical descriptor.
983    ///
984    /// Prior to this lift the `.descricao` field was accessed inline at
985    /// four production sites — [`Self::validate_descricao`]'s
986    /// `self.descricao.as_deref()` empty-and-shape gate binding, the
987    /// caixa-helm `build_chart_yaml`
988    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
989    /// `Chart.yaml` `description:` fold, the caixa-helm `build_readme`
990    /// `caixa.descricao.clone().unwrap_or_else(|| format!(...))`
991    /// `README.md` header fold, and the caixa-feira `render_flake`
992    /// `c.descricao.clone().unwrap_or_else(|| format!(...))` `flake.nix`
993    /// `description = ""` fold — four open-coded field-accesses that
994    /// expressed no compile-time link back to the typed slot. A future
995    /// extension of the `:descricao` axis to a richer author surface —
996    /// a per-`:descricao` locale-tagged multi-language descriptor map
997    /// (the "one caixa, N language-tagged prose descriptions" arm
998    /// author-tooling internationalization anticipates), a
999    /// per-registry-target length-and-shape overlay the M4 CR
1000    /// materializer resolves per-CR (the "ArtifactHub caps description
1001    /// at 512 bytes but the internal registry caps at 256" arm), a
1002    /// promotion of the plain `Option<String>` byte-string to a richer
1003    /// `ChartDescription` newtype guaranteeing the
1004    /// `is_chart_description_shape` predicate at the type level — would
1005    /// have had to be threaded through all four open-coded copies in
1006    /// lockstep or the validate gate and the three emit paths would
1007    /// silently disagree on which prose string a given [`Caixa`]
1008    /// resolves to (an author's
1009    /// `:descricao "Checkout flow orchestration."` would satisfy
1010    /// validate while one of the emit paths silently rendered a stale
1011    /// `caixa.nome`-derived fallback, or vice versa). Lifting the
1012    /// resolution to a typed method on the substrate primitive means
1013    /// every downstream consumer of the caixa's per-`Caixa`
1014    /// chart-description surface reaches for exactly one typed dispatch
1015    /// — the resolver's accept-set migrates as a unit on any future
1016    /// axis addition.
1017    ///
1018    /// Third outer top-level [`Caixa`] `Option<&str>`-return scalar
1019    /// accessor — sibling of [`Self::licenca`] (6d5bc28) and
1020    /// [`Self::repositorio`] (cc7332d), the accessors that opened the
1021    /// "outer [`Caixa`] `Option<&str>` scalar" projection pattern this
1022    /// lift folds on. Same "one typed dispatch on the substrate
1023    /// primitive, thin projections at each consumer" discipline the
1024    /// peer per-`:placement`
1025    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1026    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1027    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1028    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1029    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1030    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1031    /// typed-slot atom axes, extended here to the third outer top-level
1032    /// `Caixa` universal-axis surface. Named `descricao()` to match the
1033    /// storage field's name; the accessor's identity maps onto the
1034    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1035    /// carries. The one remaining universal `Option<String>` slot
1036    /// (`:edicao`) folds on this pattern next.
1037    #[must_use]
1038    pub const fn descricao(&self) -> Option<&str> {
1039        match &self.descricao {
1040            Some(s) => Some(s.as_str()),
1041            None => None,
1042        }
1043    }
1044
1045    /// Substrate-canonical per-`Caixa` `:edicao` language-edition scalar
1046    /// accessor every consumer of the top-level manifest's tatara-lisp
1047    /// edition-selector axis keys off — returns the author-declared
1048    /// `:edicao` byte-string verbatim as an `Option<&str>`, borrowed from
1049    /// the typed slot's own `Option<String>` storage. `None` when the
1050    /// slot is absent (the canonical "omit the slot to defer to the
1051    /// substrate's default edition" shape every existing
1052    /// [`caixa-resolver`] integration test fixture carries via
1053    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`;
1054    /// the peer [`Self::validate_edicao`] gate is a no-op on the omitted
1055    /// arm by construction, so an author-omitted `:edicao` round-trips
1056    /// to a build without triggering the year-shape predicate).
1057    ///
1058    /// The `:edicao` slot carries the universal-axis 4-digit-ASCII-
1059    /// decimal-year language-edition identifier every kind of caixa
1060    /// emits under (CAIXA-SDLC §I — the author-facing surface every
1061    /// `defcaixa` form supplies) — the typed slot's `Option<String>`
1062    /// accept-set (empty-string rejected through
1063    /// [`ManifestError::EdicaoEmpty`], year-shape-invalid rejected
1064    /// through [`ManifestError::EdicaoInvalid`] past the 4-digit-ASCII-
1065    /// decimal-year predicate [`Self::validate_edicao`] enforces) maps
1066    /// onto one load-bearing downstream consumer today
1067    /// ([`Self::validate_edicao`]'s empty-arm + year-shape-predicate
1068    /// gate binding at caixa-core/src/manifest.rs:1959) plus every
1069    /// future edition-aware substrate consumer the CAIXA-SDLC §I
1070    /// roadmap anticipates (the tatara-lisp compiler's macro-surface
1071    /// selector every edition-aware build step keys off, the future
1072    /// per-edition compatibility-flag overlay the M4 CR materializer
1073    /// resolves per-CR, the peer [`Caixa::template`] canonical
1074    /// `:edicao "2026"` scaffold every `feira init` emits verbatim,
1075    /// and the renderer-side fixtures at `caixa-helm/src/lib.rs:978` /
1076    /// `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208` that
1077    /// carry `edicao: Some("2026".into())` by construction).
1078    ///
1079    /// Prior to this lift the `.edicao` field was accessed inline at
1080    /// one production site — [`Self::validate_edicao`]'s
1081    /// `self.edicao.as_deref()` empty-and-shape gate binding — one
1082    /// open-coded field-access that expressed no compile-time link
1083    /// back to the typed slot. A future extension of the `:edicao`
1084    /// axis to a richer author surface — a per-`:edicao` known-
1085    /// edition allowlist (the future tightening
1086    /// [`Self::validate_edicao`]'s docstring acknowledges past the
1087    /// structural year-shape floor, rejecting year-shaped values that
1088    /// don't name a tatara-lisp edition the substrate actually
1089    /// understands — `"1999"` is year-shaped but no `1999` edition
1090    /// exists), a per-edition compatibility-flag overlay the M4 CR
1091    /// materializer resolves per-CR (the "edition `"2026"` enables
1092    /// macro-surface features the sibling `"2018"` gates behind a
1093    /// feature flag" arm the edition-selector story anticipates), a
1094    /// promotion of the plain `Option<String>` byte-string to a
1095    /// richer `CaixaEdition` enum discriminated on year once a sibling
1096    /// edition to `"2026"` lands — would have had to be threaded
1097    /// through the open-coded copy in lockstep with every future
1098    /// edition-aware consumer, or the validate gate and the future
1099    /// edition-aware consumer path would silently disagree on which
1100    /// edition a given [`Caixa`] resolves to (an author's
1101    /// `:edicao "2026"` would satisfy validate while a future
1102    /// edition-aware consumer silently defaulted to a stale edition,
1103    /// or vice versa). Lifting the resolution to a typed method on
1104    /// the substrate primitive means every downstream consumer of the
1105    /// caixa's per-`Caixa` edition surface reaches for exactly one
1106    /// typed dispatch — the resolver's accept-set migrates as a unit
1107    /// on any future axis addition.
1108    ///
1109    /// Fourth and final outer top-level [`Caixa`] `Option<&str>`-return
1110    /// scalar accessor — sibling of [`Self::licenca`] (6d5bc28),
1111    /// [`Self::repositorio`] (cc7332d), and [`Self::descricao`]
1112    /// (3f16e2f), the accessors that opened the "outer [`Caixa`]
1113    /// `Option<&str>` scalar" projection pattern this lift folds on.
1114    /// Same "one typed dispatch on the substrate primitive, thin
1115    /// projections at each consumer" discipline the peer per-`:placement`
1116    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
1117    /// [`crate::aplicacao::Placement::affinity`] (74ec2d3) /
1118    /// per-`:contratos` [`crate::aplicacao::WitContract::endpoint`]
1119    /// (7020470) / [`crate::aplicacao::WitContract::subject`] (90de675)
1120    /// / [`crate::aplicacao::WitContract::slot`] (ed22b66)
1121    /// `Option<&str>`-return accessors carry on the sibling M2 / M3
1122    /// typed-slot atom axes, extended here to close the outer top-level
1123    /// `Caixa` universal-axis surface's last unlifted `Option<String>`
1124    /// slot. Named `edicao()` to match the storage field's name; the
1125    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1126    /// vocabulary the slot's docstring already carries.
1127    #[must_use]
1128    pub const fn edicao(&self) -> Option<&str> {
1129        match &self.edicao {
1130            Some(s) => Some(s.as_str()),
1131            None => None,
1132        }
1133    }
1134
1135    /// Substrate-canonical per-`Caixa` `:nome` universal-axis DNS-1123-
1136    /// label caixa-identity scalar accessor every consumer of the top-
1137    /// level manifest's identity axis keys off — returns the author-
1138    /// declared `:nome` byte-string verbatim as an `&str`, borrowed from
1139    /// the typed slot's own `String` storage. Non-optional (`:nome` is
1140    /// a required-axis scalar every `defcaixa` form must supply; the
1141    /// [`Self::from_lisp`] derive rejects an omitted / non-string
1142    /// `:nome` at parse time, so a `Caixa` past parse definitionally
1143    /// carries a non-`None` `:nome`).
1144    ///
1145    /// The `:nome` slot carries the universal-axis DNS-1123-label
1146    /// caixa-identity every kind of caixa emits under (CAIXA-SDLC §I —
1147    /// the primary identity axis every `defcaixa` form supplies
1148    /// alongside `:versao` / `:kind`; the substrate-wide identity every
1149    /// other typed surface that names a caixa reaches through — `:deps`
1150    /// entries, `:membros` entries, `:children` entries, the
1151    /// `lareira-<nome>` Helm chart name every per-Servico renderer
1152    /// derives, the `pleme-program-<nome>` label every per-Aplicacao
1153    /// renderer emits) — the typed slot's `String` accept-set (empty
1154    /// rejected through [`ManifestError::NomeEmpty`], DNS-1123-shape-
1155    /// invalid rejected through [`ManifestError::NomeInvalid`] past
1156    /// the shared [`crate::render::require_valid_dns_1123_label`] gate
1157    /// the peer name axes each land on, joint-length-with-`lareira-`-
1158    /// prefix rejected through
1159    /// [`ManifestError::NomeChartNameBudgetExceeded`] past
1160    /// [`crate::render::is_lareira_chart_name_shape`]) maps onto every
1161    /// load-bearing downstream consumer the substrate carries — the
1162    /// two universal-axis validate gates at caixa-build time
1163    /// ([`Self::validate_nome`] + [`Self::validate_nome_chart_name_budget`]),
1164    /// [`crate::lareira_chart_name`]'s `lareira-<nome>` Helm chart-name
1165    /// derivation every per-Servico renderer keys off, the caixa-helm
1166    /// `Chart.yaml`'s `name:` axis, caixa-flux's `programs.yaml` entry
1167    /// `name:` axis, caixa-mesh's Cilium `CiliumNetworkPolicy` /
1168    /// `HTTPRoute` per-Aplicacao name axes at
1169    /// caixa-mesh/src/lib.rs:{2650, 2797, 2919, 2925},
1170    /// [`crate::pleme_program_selector`] /
1171    /// [`crate::pleme_program_in_aplicacao_selector`] label-selector
1172    /// derivations, and every future substrate renderer that emits an
1173    /// artifact keyed by the caixa's identity.
1174    ///
1175    /// Prior to this lift the `.nome` field was accessed inline at a
1176    /// dozen production sites across `caixa-core` (the two universal-
1177    /// axis validate gates + [`Dep::validate`]-adjacent duplicate
1178    /// tracking), `caixa-helm` (the `lareira_chart_name` fold, the
1179    /// `ChartYaml.name` / `ChartYaml.description` / `Chart.yaml`
1180    /// `keywords` fallback), `caixa-flux` (the `programs.yaml`
1181    /// entry `name:` fold, the `flux_kustomization_source_subtree`
1182    /// per-cluster subpath derivation), and `caixa-mesh` (the
1183    /// `pleme_program_in_aplicacao_selector` label-selector fold, the
1184    /// `cilium_network_policy_name` / `gateway_api_http_route_name`
1185    /// per-CR name derivations, the `LABEL_APLICACAO` labels-map
1186    /// insert) — a dozen open-coded field-accesses that expressed no
1187    /// compile-time link back to the typed slot. A future extension of
1188    /// the `:nome` axis to a richer author surface — a per-`:nome`
1189    /// structured `CaixaIdentity` newtype that carries the joint-
1190    /// length-with-prefix invariant [`Self::validate_nome_chart_name_budget`]
1191    /// enforces at the type level (rather than as a validate-time
1192    /// gate), a per-registry `:nome` namespacing overlay the M4 CR
1193    /// materializer resolves per-CR (the "`pleme-io/checkout` vs
1194    /// `partner-org/checkout` collision" arm the multi-tenant-registry
1195    /// story acknowledges), a promotion of the plain `String` byte-
1196    /// string to a richer `CaixaNome` newtype discriminated on
1197    /// namespace prefix — would have had to be threaded through every
1198    /// open-coded copy in lockstep or the two validate gates and the
1199    /// dozen emit paths would silently disagree on which identity a
1200    /// given [`Caixa`] resolves to (an author's `:nome "checkout"`
1201    /// would satisfy validate while one of the emit paths silently
1202    /// rendered a drifted other identity, or vice versa). Lifting the
1203    /// resolution to a typed method on the substrate primitive means
1204    /// every downstream consumer of the caixa's per-`Caixa` identity
1205    /// surface reaches for exactly one typed dispatch — the resolver's
1206    /// accept-set migrates as a unit on any future axis addition.
1207    ///
1208    /// First outer top-level [`Caixa`] `&str`-return required-scalar
1209    /// accessor — opens the "outer [`Caixa`] `&str` required-scalar"
1210    /// projection pattern the sibling per-`Caixa` `:versao` future lift
1211    /// folds on. Sibling in shape to the peer per-`:membros`
1212    /// [`crate::aplicacao::Membro::nome`] (4a32abf) / per-`:contratos`
1213    /// [`crate::aplicacao::WitContract::source`] /
1214    /// [`crate::aplicacao::WitContract::destination`] (7f0fd43),
1215    /// [`crate::aplicacao::WitContract::world_ref`] (0804823),
1216    /// [`crate::aplicacao::Membro::versao_requirement`] (a40b0e3),
1217    /// [`crate::aplicacao::Entrada::destination`] (6db982c),
1218    /// [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062),
1219    /// per-sub-struct required-axis accessors carry on the sibling M3
1220    /// mesh-slot-atom scalar-value axes, extended here to open the
1221    /// outer top-level [`Caixa`] `&str`-return required-scalar surface.
1222    /// Named `nome()` to match the storage field's name; the accessor's
1223    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1224    /// slot's docstring already carries.
1225    #[must_use]
1226    pub const fn nome(&self) -> &str {
1227        self.nome.as_str()
1228    }
1229
1230    /// Substrate-canonical per-`Caixa` `:versao` universal-axis SemVer-2
1231    /// pinned-version scalar accessor every consumer of the top-level
1232    /// manifest's version axis keys off — returns the author-declared
1233    /// `:versao` byte-string verbatim as an `&str`, borrowed from the
1234    /// typed slot's own `String` storage. Non-optional (`:versao` is a
1235    /// required-axis scalar every `defcaixa` form must supply alongside
1236    /// `:nome` / `:kind`; the [`Self::from_lisp`] derive rejects an
1237    /// omitted / non-string `:versao` at parse time, so a `Caixa` past
1238    /// parse definitionally carries a non-`None` `:versao`).
1239    ///
1240    /// The `:versao` slot carries the universal-axis SemVer-2
1241    /// concrete-version body every kind of caixa emits under
1242    /// (CAIXA-SDLC §I — the required-scalar every `defcaixa` form
1243    /// supplies alongside `:nome` / `:kind`; the substrate-wide
1244    /// pinned-version every downstream artifact-emitting consumer
1245    /// composes under — the `lareira-<nome>` Helm chart's `Chart.yaml`
1246    /// `version:` + `appVersion:` axes, the `feira publish` Zig-style
1247    /// `v<versao>` git tag the [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
1248    /// prefix composes on top of, the programs.yaml entry's `versao:`
1249    /// value the `lareira-fleet-programs` aggregator carries onto each
1250    /// rendered `ComputeUnit`, the OCI image's `:v<versao>` / `:latest`
1251    /// tags every substrate-side `skopeo push` writes, the lacre
1252    /// closure's pinned `concrete_versao`, and the `:upgrade-from :from`
1253    /// prior-version references peers in the exact same SemVer-2 shape).
1254    /// The typed slot's `String` accept-set (empty rejected through
1255    /// [`ManifestError::VersaoEmpty`], SemVer-2-shape-invalid rejected
1256    /// through [`ManifestError::VersaoInvalid`] past
1257    /// [`semver::Version::parse`]) maps onto every load-bearing
1258    /// downstream consumer the substrate carries — the [`Self::validate_versao`]
1259    /// universal-axis validate gate at caixa-build time, the
1260    /// [`crate::CaixaVersion::parse`] typed-wrapper resolver,
1261    /// [`caixa-helm`]'s `Chart.yaml` `version:` / `appVersion:` fold,
1262    /// [`caixa-flux`]'s `programs.yaml` entry `versao:` fold + the
1263    /// `cluster_bundle` `GitRepository` `ref: { tag: v<versao> }`
1264    /// derivation, [`caixa-mesh`]'s per-Aplicacao `programs.yaml` fan-
1265    /// out entry `versao:` fold, [`caixa-feira`]'s `feira publish` git-
1266    /// tag derivation (`format!("{prefix}{versao}")`), and every future
1267    /// substrate renderer that emits an artifact keyed by the caixa's
1268    /// pinned version.
1269    ///
1270    /// Prior to this lift the `.versao` field was accessed inline at a
1271    /// dozen production sites across `caixa-core` (the universal-axis
1272    /// [`Self::validate_versao`] gate + [`Dep::validate`]-adjacent
1273    /// version-shape gates), `caixa-helm` (the `ChartYaml.version` /
1274    /// `ChartYaml.app_version` folds), `caixa-flux` (the `programs.yaml`
1275    /// entry `versao:` fold, the `cluster_bundle` `GitRepository` `ref:
1276    /// { tag: v<versao> }` derivation), `caixa-mesh` (the per-Aplicacao
1277    /// `programs.yaml` fan-out entry `versao:` fold), and `caixa-feira`
1278    /// (the `feira publish` git-tag derivation + the `feira app graph` /
1279    /// `feira app deploy` diagnostic renderers) — a dozen open-coded
1280    /// field-accesses that expressed no compile-time link back to the
1281    /// typed slot. A future extension of the `:versao` axis to a richer
1282    /// author surface — a per-`:versao` structured `CaixaVersion` at the
1283    /// storage layer (the substrate already carries a `CaixaVersion`
1284    /// newtype at [`crate::version::CaixaVersion`], deferred until the
1285    /// serde-transparent-newtype-through-DeriveTataraDomain path lands),
1286    /// a per-registry `:versao` immutability overlay the M4 CR
1287    /// materializer enforces per-CR, a promotion of the plain `String`
1288    /// byte-string to a richer `PinnedVersao` newtype discriminated on
1289    /// SemVer-2 pre-release / build-metadata presence — would have had
1290    /// to be threaded through every open-coded copy in lockstep or the
1291    /// validate gate and the dozen emit paths would silently disagree
1292    /// on which version a given [`Caixa`] resolves to (an author's
1293    /// `:versao "0.1.0"` would satisfy validate while one of the emit
1294    /// paths silently rendered a drifted other version, or vice versa).
1295    /// Lifting the resolution to a typed method on the substrate
1296    /// primitive means every downstream consumer of the caixa's
1297    /// per-`Caixa` pinned-version surface reaches for exactly one typed
1298    /// dispatch — the resolver's accept-set migrates as a unit on any
1299    /// future axis addition.
1300    ///
1301    /// Second outer top-level [`Caixa`] `&str`-return required-scalar
1302    /// accessor — folds on the "outer [`Caixa`] `&str` required-scalar"
1303    /// projection pattern the sibling per-`Caixa` [`Self::nome`]
1304    /// (e6b7d97) opened. Sibling in shape to the peer per-`:membros`
1305    /// [`crate::aplicacao::Membro::versao_requirement`] (4127bb6) /
1306    /// per-`:children` [`crate::supervisor::ChildSpec::versao_requirement`]
1307    /// (2c053c8) / per-`:upgrade-from` [`crate::UpgradeFromEntry::prior_versao`]
1308    /// (75d27a8) per-sub-struct `:versao`-shaped `&str`-return accessors
1309    /// on the sibling per-typed-slot version-carrier axes, extended here
1310    /// to close the second outer top-level [`Caixa`] required-`&str`-
1311    /// carrying axis so the two universal-axis identity-carrying
1312    /// scalars every `defcaixa` form supplies (`:nome` + `:versao`)
1313    /// share the same "one typed dispatch per axis" discipline. Named
1314    /// `versao()` to match the storage field's name; the accessor's
1315    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
1316    /// slot's docstring already carries.
1317    #[must_use]
1318    pub const fn versao(&self) -> &str {
1319        self.versao.as_str()
1320    }
1321
1322    /// Substrate-canonical per-`Caixa` `:kind` universal-axis
1323    /// closed-set-enum discriminant accessor every consumer of the top-
1324    /// level manifest's kind axis keys off — returns the author-declared
1325    /// `:kind` variant verbatim as a [`CaixaKind`], `Copy`-projected
1326    /// from the typed slot's own [`CaixaKind`] storage. Non-optional
1327    /// (`:kind` is a required-axis discriminant every `defcaixa` form
1328    /// must supply alongside `:nome` / `:versao`; the [`Self::from_lisp`]
1329    /// derive rejects an omitted / non-symbol `:kind` at parse time, so
1330    /// a `Caixa` past parse definitionally carries a valid [`CaixaKind`]
1331    /// variant).
1332    ///
1333    /// The `:kind` slot carries the universal-axis closed-set typed-
1334    /// discriminant every substrate-side dispatch keys off (CAIXA-SDLC
1335    /// §I — the primary shape gate every renderer / verifier /
1336    /// operator branches on; the five variants `Biblioteca` /
1337    /// `Binario` / `Servico` / `Supervisor` / `Aplicacao` partition
1338    /// the caixa surface into disjoint runtime contracts) — the typed
1339    /// slot's [`CaixaKind`] accept-set (parse-time-rejected non-symbol
1340    /// values through the derive-macro's symbol-arm gate, exhaustively
1341    /// matched at every downstream dispatch site) maps onto every
1342    /// load-bearing downstream consumer the substrate carries:
1343    ///
1344    ///   - [`crate::render::require_kind`]'s per-renderer entry-gate
1345    ///     predicate — the canonical two-line
1346    ///     `require_kind(caixa, Servico)?` prelude every per-Servico
1347    ///     renderer (`caixa-helm`, `caixa-flux`, the future `caixa-otel`
1348    ///     / per-Servico OCI packager / M4 `wasm.pleme.io/v1alpha1/
1349    ///     ComputeUnit` CR materializer) runs at its entry-point,
1350    ///     alongside the [`crate::render::KindMismatch`] error carrier's
1351    ///     `actual:` field the diagnostic surfaces to name the offending
1352    ///     caixa's variant.
1353    ///   - [`Self::aplicacao_view`]'s + [`Self::supervisor_view`]'s
1354    ///     per-view kind-gate binding — the two `Option<TypedSpec>`
1355    ///     `_view` composers that fold the flat mesh-slot / supervisor-
1356    ///     slot columns into their typed sub-spec only when the kind
1357    ///     matches (returns `None` otherwise); the future per-Servico
1358    ///     M2-view composer (`servico_view`) will follow the same shape.
1359    ///   - [`Self::declared_foreign_code_slots`]'s per-slot kind-
1360    ///     coherence gate — the `!self.kind.requires_exe()` /
1361    ///     `!self.kind.requires_servicos()` predicates that fence
1362    ///     each code-surface slot from the wrong owning kind.
1363    ///   - [`crate::LayoutInvariants::verify`]'s kind ↔ code-surface
1364    ///     coherence gates — the six `caixa.kind == CaixaKind::X` /
1365    ///     `caixa.kind != CaixaKind::X` predicates and the four kind-
1366    ///     coherence error carriers (`SupervisorOwnsCode` /
1367    ///     `AplicacaoOwnsCode` / `MeshSlotsOnNonAplicacao` /
1368    ///     `SupervisorSlotsOnNonSupervisor` / `ServicoSlotsOnNonServico`
1369    ///     / `ForeignCodeSlot`) which each name the offending caixa's
1370    ///     variant in their `kind:` field.
1371    ///
1372    /// Prior to this lift the `.kind` field was accessed inline at
1373    /// twenty-plus production sites across `caixa-core` (the
1374    /// [`crate::render::require_kind`] entry-gate predicate + the
1375    /// [`crate::render::KindMismatch`] `actual:` field, the two `_view`
1376    /// composers, the `declared_foreign_code_slots` per-slot kind-
1377    /// coherence gate, and the six [`crate::LayoutInvariants::verify`]
1378    /// kind ↔ code-surface predicates + four error carriers) — a score
1379    /// of open-coded field-accesses that expressed no compile-time link
1380    /// back to the typed slot. A future extension of the `:kind` axis
1381    /// to a richer author surface — a per-`:kind` sub-variant discriminant
1382    /// (e.g. `Servico(ServicoRuntime)` splitting the current single
1383    /// variant across the wasm-component / legacy-container / native-
1384    /// binary runtime axes the M5 roadmap acknowledges), a per-cluster
1385    /// kind-overlay the M4 CR materializer resolves per-CR (the
1386    /// "cluster policy demotes `Aplicacao` to `Servico` on a single-
1387    /// tenant cluster" arm), a promotion of the plain [`CaixaKind`]
1388    /// enum to a richer `KindWithRuntime` discriminated on the
1389    /// component-model world axis — would have had to be threaded
1390    /// through every open-coded copy in lockstep or the entry gate,
1391    /// the view composers, and the layout invariants would silently
1392    /// disagree on which kind a given [`Caixa`] resolves to. Lifting
1393    /// the resolution to a typed method on the substrate primitive
1394    /// means every downstream consumer of the caixa's per-`Caixa`
1395    /// kind surface reaches for exactly one typed dispatch — the
1396    /// resolver's accept-set migrates as a unit on any future axis
1397    /// addition.
1398    ///
1399    /// First outer top-level [`Caixa`] `Copy`-return required-enum-
1400    /// discriminant accessor — opens the "outer [`Caixa`] `Copy`-return
1401    /// required-discriminant" projection pattern. Sibling in shape to
1402    /// the peer per-`:supervisor` [`crate::supervisor::SupervisorSpec::estrategia`]
1403    /// (eafb619), per-`:placement` [`crate::aplicacao::Placement::estrategia`]
1404    /// (921fe1b), and per-`:children` [`crate::supervisor::ChildSpec::restart`]
1405    /// (dfb4a81) `Copy`-return closed-set-enum discriminant accessors
1406    /// on the sibling nested-spec typed-slot discriminator axes,
1407    /// extended here to the outer top-level [`Caixa`] universal-axis
1408    /// surface. Named `kind()` to match the storage field's name;
1409    /// the accessor's identity maps onto the canonical CAIXA-SDLC §I
1410    /// vocabulary the slot's docstring already carries.
1411    #[must_use]
1412    pub const fn kind(&self) -> CaixaKind {
1413        self.kind
1414    }
1415
1416    /// Substrate-canonical per-`Caixa` `:autores` universal-axis
1417    /// maintainer-name-list slice-accessor every consumer of the top-
1418    /// level manifest's maintainer axis keys off — returns the author-
1419    /// declared `:autores` list verbatim as a `&[String]` slice-view over
1420    /// the same backing buffer the raw `self.autores.as_slice()` field
1421    /// access borrows from. Empty-list-carrying (`:autores` is a default-
1422    /// empty axis every `defcaixa` form supplies with an empty `()` when
1423    /// unset; the [`Self::from_lisp`] derive folds an omitted `:autores`
1424    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1425    /// parse definitionally carries a `Vec<String>` slot — possibly
1426    /// empty — and the returned `&[String]` degenerates to an empty
1427    /// slice on that arm without any silent `None` collapse).
1428    ///
1429    /// The `:autores` slot carries the universal-axis maintainer-name
1430    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
1431    /// facing surface every `defcaixa` form supplies alongside `:nome` /
1432    /// `:versao` / `:kind`; the substrate-wide contact-carrying axis
1433    /// every downstream registry-facing artifact emits under) — the
1434    /// typed slot's `Vec<String>` accept-set (empty-per-entry rejected
1435    /// through [`ManifestError::AutorEmpty`], non-chart-maintainer-shape
1436    /// rejected through [`ManifestError::AutorInvalid`], cross-entry
1437    /// duplicate rejected through [`ManifestError::AutorDuplicate`]) maps
1438    /// onto every load-bearing downstream consumer the substrate carries
1439    /// — the [`Self::validate_autores`] universal-axis empty-per-entry +
1440    /// shape + duplicate gate at caixa-core/src/manifest.rs, the
1441    /// caixa-helm `build_chart_yaml` `maintainers:` fold at
1442    /// caixa-helm/src/lib.rs that walks each entry into a `Maintainer {
1443    /// name, email: None }` record, every future per-`Caixa` registry-
1444    /// facing renderer the CAIXA-SDLC §I roadmap acknowledges (the
1445    /// future `artifacthub.io/maintainers` `Chart.yaml` annotation the
1446    /// caixa-helm docstring alludes to at [`Self::validate_licenca`],
1447    /// the future per-cluster author-notification overlay the M4 CR
1448    /// materializer resolves per-CR).
1449    ///
1450    /// Prior to this lift the `.autores` field was accessed inline at
1451    /// two production sites — [`Self::validate_autores`]'s `for autor
1452    /// in &self.autores` walk that gates every entry through
1453    /// [`ManifestError::AutorEmpty`] / `AutorInvalid` / `AutorDuplicate`,
1454    /// and the caixa-helm `build_chart_yaml` `caixa.autores.iter().map(|a|
1455    /// Maintainer { name: a.clone(), email: None }).collect()` fold that
1456    /// materializes every entry into a `Chart.yaml` `maintainers:` row —
1457    /// two open-coded field-accesses that expressed no compile-time link
1458    /// back to the typed slot. A future extension of the `:autores` axis
1459    /// to a richer author surface — a per-`:autores` structured
1460    /// `Maintainer { name, email, url }` at the storage layer once the
1461    /// substrate absorbs `artifacthub.io/maintainers`' name+email+url
1462    /// tuple, a per-registry `:autores` allowlist the M4 CR materializer
1463    /// enforces per-CR (the "cluster policy demands every author declare
1464    /// an on-file `mailto:` contact" arm), a promotion of the plain
1465    /// `Vec<String>` byte-string list to a richer
1466    /// `Vec<ChartMaintainer>` newtype discriminated on the RFC-5322
1467    /// `<name> [<email>]` grammar the `is_chart_maintainer_name_shape`
1468    /// predicate already resolves through — would have had to be
1469    /// threaded through both open-coded copies in lockstep or the
1470    /// validate gate and the caixa-helm emit path would silently
1471    /// disagree on which authors a given [`Caixa`] resolves to (an
1472    /// author's `:autores ("alice" "bob")` would satisfy validate while
1473    /// the caixa-helm emit path silently rendered a drifted other
1474    /// maintainer list, or vice versa). Lifting the resolution to a
1475    /// typed method on the substrate primitive means every downstream
1476    /// consumer of the caixa's per-`Caixa` maintainer surface reaches
1477    /// for exactly one typed dispatch — the resolver's accept-set
1478    /// migrates as a unit on any future axis addition.
1479    ///
1480    /// First outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1481    /// opens the "outer [`Caixa`] `&[T]` slice" projection pattern the
1482    /// sibling per-`Caixa` `:etiquetas` / `:deps` / `:deps-dev` / `:exe`
1483    /// / `:bibliotecas` / `:servicos` / `:upgrade-from` / `:children`
1484    /// future lifts fold on. Sibling in shape to the peer per-`:supervisor`
1485    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce), per-`:placement`
1486    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7), per-`:membros`
1487    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36), per-`:contratos`
1488    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1489    /// per-`:upgrade-from :instructions` [`crate::upgrade::UpgradeFromEntry::instructions`]
1490    /// (0137e5a) `&[T]`-return slice accessors on the sibling per-M2 /
1491    /// per-M3 typed-slot list axes, extended here to the outer top-level
1492    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1493    /// `&Vec<String>`) because every downstream consumer of the author
1494    /// list treats it as a read-only sequence — the slice-view is the
1495    /// narrowest borrow that supports every present + roadmapped consumer
1496    /// (`.iter()`, `.len()`, `.is_empty()`) without leaking the backing
1497    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
1498    /// reaches for (the storage-side `Vec` remains reachable through the
1499    /// `pub autores` field for the mutation-carrying serde round-trip and
1500    /// per-test fixture-mutation paths). Named `autores()` to match the
1501    /// storage field's name; the accessor's identity maps onto the
1502    /// canonical CAIXA-SDLC §I vocabulary the slot's docstring already
1503    /// carries.
1504    #[must_use]
1505    pub const fn autores(&self) -> &[String] {
1506        self.autores.as_slice()
1507    }
1508
1509    /// Substrate-canonical per-`Caixa` `:etiquetas` universal-axis
1510    /// registry-search-tag-list slice-accessor every consumer of the
1511    /// top-level manifest's topical-tag axis keys off — returns the
1512    /// author-declared `:etiquetas` list verbatim as a `&[String]`
1513    /// slice-view over the same backing buffer the raw
1514    /// `self.etiquetas.as_slice()` field access borrows from. Empty-
1515    /// list-carrying (`:etiquetas` is a default-empty axis every
1516    /// `defcaixa` form supplies with an empty `()` when unset; the
1517    /// [`Self::from_lisp`] derive folds an omitted `:etiquetas` through
1518    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1519    /// definitionally carries a `Vec<String>` slot — possibly empty —
1520    /// and the returned `&[String]` degenerates to an empty slice on
1521    /// that arm without any silent `None` collapse).
1522    ///
1523    /// The `:etiquetas` slot carries the universal-axis topical-tag
1524    /// list every kind of caixa emits under (CAIXA-SDLC §I — the
1525    /// author-facing surface every `defcaixa` form supplies alongside
1526    /// `:nome` / `:versao` / `:kind`; the substrate-wide registry-
1527    /// search-facing axis every downstream registry-facing artifact
1528    /// emits under) — the typed slot's `Vec<String>` accept-set
1529    /// (empty-per-entry rejected through [`ManifestError::EtiquetaEmpty`],
1530    /// non-chart-keyword-shape rejected through
1531    /// [`ManifestError::EtiquetaInvalid`], cross-entry duplicate
1532    /// rejected through [`ManifestError::EtiquetaDuplicate`]) maps onto
1533    /// every load-bearing downstream consumer the substrate carries —
1534    /// the [`Self::validate_etiquetas`] universal-axis empty-per-entry
1535    /// + shape + duplicate gate at caixa-core/src/manifest.rs, the
1536    /// caixa-helm `build_chart_yaml` `keywords:` fold at
1537    /// caixa-helm/src/lib.rs that walks each entry into the rendered
1538    /// `Chart.yaml` `keywords:` array (chained with the
1539    /// [`crate::LAREIRA_CHART_KEYWORDS`] substrate-wide floor set and
1540    /// dedup'd through a `BTreeSet` at emit time), every future per-
1541    /// `Caixa` registry-facing renderer the CAIXA-SDLC §I roadmap
1542    /// acknowledges (the future `artifacthub.io/keywords` `Chart.yaml`
1543    /// annotation, the future per-cluster tag-notification overlay the
1544    /// M4 CR materializer resolves per-CR).
1545    ///
1546    /// Prior to this lift the `.etiquetas` field was accessed inline at
1547    /// two production sites — [`Self::validate_etiquetas`]'s `for
1548    /// etiqueta in &self.etiquetas` walk that gates every entry through
1549    /// [`ManifestError::EtiquetaEmpty`] / `EtiquetaInvalid` /
1550    /// `EtiquetaDuplicate`, and the caixa-helm `build_chart_yaml`
1551    /// `caixa.etiquetas.iter().cloned().chain(...)` fold that
1552    /// materializes every entry into a `Chart.yaml` `keywords:` row —
1553    /// two open-coded field-accesses that expressed no compile-time
1554    /// link back to the typed slot. A future extension of the
1555    /// `:etiquetas` axis to a richer tag surface — a per-`:etiquetas`
1556    /// structured `ChartKeyword { name, uri, category }` at the storage
1557    /// layer once the substrate absorbs `artifacthub.io/keywords`
1558    /// richer tag tuple, a per-registry `:etiquetas` allowlist the M4
1559    /// CR materializer enforces per-CR (the "cluster policy demands
1560    /// every tag come from a substrate-approved taxonomy" arm), a
1561    /// promotion of the plain `Vec<String>` byte-string list to a
1562    /// richer `Vec<ChartKeyword>` newtype discriminated on the DNS-
1563    /// 1123-label-shaped grammar the `is_chart_keyword_shape` predicate
1564    /// already resolves through — would have had to be threaded through
1565    /// both open-coded copies in lockstep or the validate gate and the
1566    /// caixa-helm emit path would silently disagree on which tags a
1567    /// given [`Caixa`] resolves to (an author's `:etiquetas ("demo"
1568    /// "aplicacao")` would satisfy validate while the caixa-helm emit
1569    /// path silently rendered a drifted other keyword list, or vice
1570    /// versa). Lifting the resolution to a typed method on the
1571    /// substrate primitive means every downstream consumer of the
1572    /// caixa's per-`Caixa` topical-tag surface reaches for exactly one
1573    /// typed dispatch — the resolver's accept-set migrates as a unit
1574    /// on any future axis addition.
1575    ///
1576    /// Second outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1577    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1578    /// [`Self::autores`] (b5d813f) opened, sibling in shape and
1579    /// idiom. The remaining unlifted outer-`Caixa` slice-carrying axes
1580    /// (`:deps` / `:deps-dev` / `:exe` / `:bibliotecas` / `:servicos`
1581    /// / `:upgrade-from` / `:children` / `:membros` / `:contratos`)
1582    /// fold onto the same pattern in future lifts. Sibling in shape to
1583    /// the peer per-`:supervisor`
1584    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1585    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1586    /// (a6e18d7), per-`:membros`
1587    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1588    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1589    /// (0dcc926), and per-`:upgrade-from :instructions`
1590    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1591    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1592    /// typed-slot list axes, extended here to the outer top-level
1593    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1594    /// `&Vec<String>`) because every downstream consumer of the tag
1595    /// list treats it as a read-only sequence — the slice-view is the
1596    /// narrowest borrow that supports every present + roadmapped
1597    /// consumer (`.iter()`, `.len()`, `.is_empty()`) without leaking
1598    /// the backing `Vec`'s grow/push/reserve surface no consumer of
1599    /// the typed view reaches for (the storage-side `Vec` remains
1600    /// reachable through the `pub etiquetas` field for the mutation-
1601    /// carrying serde round-trip and per-test fixture-mutation paths).
1602    /// Named `etiquetas()` to match the storage field's name; the
1603    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1604    /// vocabulary the slot's docstring already carries.
1605    #[must_use]
1606    pub const fn etiquetas(&self) -> &[String] {
1607        self.etiquetas.as_slice()
1608    }
1609
1610    /// Substrate-canonical per-`Caixa` `:bibliotecas` universal-axis
1611    /// library-source-path-list slice-accessor every consumer of the
1612    /// top-level manifest's Biblioteca-source axis keys off — returns
1613    /// the author-declared `:bibliotecas` list verbatim as a
1614    /// `&[String]` slice-view over the same backing buffer the raw
1615    /// `self.bibliotecas.as_slice()` field access borrows from. Empty-
1616    /// list-carrying (`:bibliotecas` is a default-empty axis every
1617    /// `defcaixa` form supplies with an empty `()` when unset; the
1618    /// [`Self::from_lisp`] derive folds an omitted `:bibliotecas`
1619    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
1620    /// parse definitionally carries a `Vec<String>` slot — possibly
1621    /// empty — and the returned `&[String]` degenerates to an empty
1622    /// slice on that arm without any silent `None` collapse).
1623    ///
1624    /// The `:bibliotecas` slot carries the universal-axis lisp-library
1625    /// entry-path list every `:kind Biblioteca` caixa emits under
1626    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1627    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1628    /// substrate-wide library-carrier axis every downstream
1629    /// authoring-facing consumer keys off) — the typed slot's
1630    /// `Vec<String>` accept-set (empty-per-entry rejected through
1631    /// [`ManifestError::CodePathEmpty { slot: ":bibliotecas" }`],
1632    /// non-sandboxed-relative-shape rejected through
1633    /// [`ManifestError::CodePathShape`], non-`.lisp`-extension rejected
1634    /// through [`ManifestError::CodePathNonLispExtension`], cross-entry
1635    /// duplicate rejected through [`ManifestError::CodePathDuplicate`])
1636    /// maps onto every load-bearing downstream consumer the substrate
1637    /// carries — the [`crate::LayoutInvariants`] Biblioteca-arm
1638    /// empty-check + per-entry file-exists loop at
1639    /// caixa-core/src/layout.rs that gates each entry through
1640    /// [`crate::LayoutError::MissingLib`] / `MissingEntry`, the
1641    /// [`Self::validate_code_paths`] per-slot shape gate at
1642    /// caixa-core/src/manifest.rs that walks each entry through the
1643    /// sandbox-relative / `.lisp`-extension / cross-entry duplicate
1644    /// gates, the `feira build` per-entry `tatara_lisp::read` parse
1645    /// walk at caixa-feira/src/cmd/build.rs that phase-1-checks each
1646    /// declared library file for lexical / structural errors before
1647    /// downstream `importar` resolution, every future per-`Caixa`
1648    /// library-facing renderer the CAIXA-SDLC §I roadmap acknowledges
1649    /// (the future `tatara-lispc` compilation entry the docstring at
1650    /// caixa-feira/src/cmd/build.rs alludes to, the future per-cluster
1651    /// bytecode-caching overlay the M4 CR materializer resolves per-CR,
1652    /// the future `caixa-lsp` per-library semantic-token stream the
1653    /// caixa-lsp docstring roadmaps).
1654    ///
1655    /// Prior to this lift the `.bibliotecas` field was accessed inline
1656    /// at three production sites — [`crate::LayoutInvariants`]'s
1657    /// `caixa.bibliotecas.is_empty()` `MissingLib`-arm gate + `for p
1658    /// in &caixa.bibliotecas` `MissingEntry` walk that gates each
1659    /// declared library path through the on-disk-existence check,
1660    /// the compound-code-path `has_code = !caixa.bibliotecas.is_empty()
1661    /// || !caixa.exe.is_empty() || !caixa.servicos.is_empty()` OR-fold
1662    /// on the [`crate::LayoutError::SupervisorOwnsCode`] /
1663    /// `AplicacaoOwnsCode` kind-coherence gate, and the `feira build`
1664    /// per-entry `for entry in &caixa.bibliotecas` + `caixa.bibliotecas.
1665    /// len()` phase-1 tatara-lispc-precursor parse walk — three open-
1666    /// coded field-accesses that expressed no compile-time link back
1667    /// to the typed slot. A future extension of the `:bibliotecas`
1668    /// axis to a richer library surface — a per-`:bibliotecas`
1669    /// structured `BibliotecaEntry { path, edition, exports }` at the
1670    /// storage layer once the substrate absorbs the per-library
1671    /// language-edition + explicit-exports tuple the tatara-lisp
1672    /// module-system roadmap acknowledges, a per-registry
1673    /// `:bibliotecas` allowlist the M4 CR materializer enforces
1674    /// per-CR (the "cluster policy demands every biblioteca declare
1675    /// its own :edicao" arm), a promotion of the plain `Vec<String>`
1676    /// byte-string list to a richer `Vec<LibraryPath>` newtype
1677    /// discriminated on the `lib/<nome>.lisp`-shape grammar the
1678    /// [`crate::render::is_sandboxed_relative_path`] +
1679    /// [`crate::render::is_lisp_extension`] predicates already resolve
1680    /// through — would have had to be threaded through all three
1681    /// open-coded copies in lockstep or the layout gate, the shape
1682    /// validator, and the `feira build` phase-1 parse walk would
1683    /// silently disagree on which library paths a given [`Caixa`]
1684    /// resolves to (an author's `:bibliotecas ("lib/foo.lisp"
1685    /// "lib/bar.lisp")` would satisfy layout while `feira build`
1686    /// silently parsed a drifted other list, or vice versa). Lifting
1687    /// the resolution to a typed method on the substrate primitive
1688    /// means every downstream consumer of the caixa's per-`Caixa`
1689    /// library-source surface reaches for exactly one typed dispatch
1690    /// — the resolver's accept-set migrates as a unit on any future
1691    /// axis addition.
1692    ///
1693    /// Third outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1694    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1695    /// [`Self::autores`] (b5d813f) opened and [`Self::etiquetas`]
1696    /// (78c7d3c) folded on, sibling in shape and idiom. The remaining
1697    /// unlifted outer-`Caixa` slice-carrying axes (`:deps` /
1698    /// `:deps-dev` / `:exe` / `:servicos` / `:upgrade-from` /
1699    /// `:children` / `:membros` / `:contratos`) fold onto the same
1700    /// pattern in future lifts. Sibling in shape to the peer
1701    /// per-`:supervisor`
1702    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1703    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1704    /// (a6e18d7), per-`:membros`
1705    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1706    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
1707    /// (0dcc926), and per-`:upgrade-from :instructions`
1708    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1709    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1710    /// typed-slot list axes, extended here to the outer top-level
1711    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1712    /// `&Vec<String>`) because every downstream consumer of the
1713    /// library-source list treats it as a read-only sequence — the
1714    /// slice-view is the narrowest borrow that supports every
1715    /// present + roadmapped consumer (`.iter()`, `.len()`,
1716    /// `.is_empty()`) without leaking the backing `Vec`'s
1717    /// grow/push/reserve surface no consumer of the typed view
1718    /// reaches for (the storage-side `Vec` remains reachable through
1719    /// the `pub bibliotecas` field for the mutation-carrying serde
1720    /// round-trip and per-test fixture-mutation paths). Named
1721    /// `bibliotecas()` to match the storage field's name; the
1722    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
1723    /// vocabulary the slot's docstring already carries.
1724    #[must_use]
1725    pub const fn bibliotecas(&self) -> &[String] {
1726        self.bibliotecas.as_slice()
1727    }
1728
1729    /// Substrate-canonical per-`Caixa` `:exe` universal-axis
1730    /// nix-built-executable-entry-path-list slice-accessor every consumer
1731    /// of the top-level manifest's Binario-executable axis keys off —
1732    /// returns the author-declared `:exe` list verbatim as a `&[String]`
1733    /// slice-view over the same backing buffer the raw
1734    /// `self.exe.as_slice()` field access borrows from. Empty-list-
1735    /// carrying (`:exe` is a default-empty axis every `defcaixa` form
1736    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
1737    /// derive folds an omitted `:exe` through `#[serde(default)]` to
1738    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
1739    /// `Vec<String>` slot — possibly empty — and the returned `&[String]`
1740    /// degenerates to an empty slice on that arm without any silent
1741    /// `None` collapse).
1742    ///
1743    /// The `:exe` slot carries the universal-axis nix-built executable
1744    /// entry-path list every `:kind Binario` caixa emits under
1745    /// (CAIXA-SDLC §I — the author-facing surface every `defcaixa`
1746    /// form supplies alongside `:nome` / `:versao` / `:kind`; the
1747    /// substrate-wide `exe/`-directory-fenced entry-carrier axis every
1748    /// downstream flake-build-facing consumer keys off) — the typed
1749    /// slot's `Vec<String>` accept-set (empty-per-entry rejected
1750    /// through [`ManifestError::CodePathEmpty { slot: ":exe" }`],
1751    /// non-sandboxed-relative-shape rejected through
1752    /// [`ManifestError::CodePathShape`], cross-entry duplicate rejected
1753    /// through [`ManifestError::CodePathDuplicate`], out-of-`exe/`-
1754    /// directory paths rejected past the layout's
1755    /// [`crate::LayoutError::ExeOutsideDir`] `starts_with` fence) maps
1756    /// onto every load-bearing downstream consumer the substrate carries
1757    /// — the [`crate::LayoutInvariants`] Binario-arm empty-check +
1758    /// per-entry file-exists + `exe/`-directory-fence loop at
1759    /// caixa-core/src/layout.rs that gates each entry through
1760    /// [`crate::LayoutError::BinarioWithoutExe`] / `MissingEntry` /
1761    /// `ExeOutsideDir`, the compound `has_code` OR-fold on the
1762    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1763    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1764    /// that fences code-surface slots off from the two no-code kinds,
1765    /// [`Self::declared_foreign_code_slots`]'s `!self.exe.is_empty()`
1766    /// arm on the [`crate::LayoutError::ForeignCodeSlot`] gate that
1767    /// fences the `:exe` code surface off from every non-Binario code-
1768    /// running kind, [`Self::validate_code_paths`]'s per-slot shape gate
1769    /// that walks each entry through the sandbox-relative / cross-entry
1770    /// duplicate gates, every future per-`Caixa` executable-facing
1771    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1772    /// `caixa-flake` per-Binario `packages.<system>.<nome>` derivation
1773    /// entry the caixa-flake docstring roadmaps, the future per-cluster
1774    /// `nix-store` overlay the M4 CR materializer resolves per-CR, the
1775    /// future `feira nix` per-executable Binario-target emit path).
1776    ///
1777    /// Prior to this lift the `.exe` field was accessed inline at three
1778    /// production sites — the compound-code-path `has_code =
1779    /// !caixa.bibliotecas().is_empty() || !caixa.exe.is_empty() ||
1780    /// !caixa.servicos.is_empty()` OR-fold on the
1781    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1782    /// `AplicacaoOwnsCode` kind-coherence gate, the Binario-arm
1783    /// `caixa.exe.is_empty()` [`crate::LayoutError::BinarioWithoutExe`]
1784    /// gate, the per-entry `for p in &caixa.exe`
1785    /// `MissingEntry`/`ExeOutsideDir` walk, and the
1786    /// [`Self::declared_foreign_code_slots`]'s
1787    /// `!self.exe.is_empty()` arm on the `ForeignCodeSlot` gate — four
1788    /// open-coded field-accesses that expressed no compile-time link
1789    /// back to the typed slot. A future extension of the `:exe` axis
1790    /// to a richer executable surface — a per-`:exe` structured
1791    /// `BinarioEntry { path, wrapper, capabilities }` at the storage
1792    /// layer once the substrate absorbs the per-executable
1793    /// nix-wrapper + linux-capabilities tuple the CAIXA-SDLC §I
1794    /// executable roadmap acknowledges, a per-registry `:exe` allowlist
1795    /// the M4 CR materializer enforces per-CR (the "cluster policy
1796    /// demands every Binario declare an explicit `:wrapper`" arm), a
1797    /// promotion of the plain `Vec<String>` byte-string list to a
1798    /// richer `Vec<ExecutablePath>` newtype discriminated on the
1799    /// `exe/<nome>`-shape grammar the layout's `starts_with(exe_dir)`
1800    /// fence already resolves through — would have had to be threaded
1801    /// through all four open-coded copies in lockstep or the layout
1802    /// gate, the shape validator, and the `feira nix` emit path would
1803    /// silently disagree on which executable paths a given [`Caixa`]
1804    /// resolves to (an author's `:exe ("exe/cli" "exe/serve")` would
1805    /// satisfy layout while `feira nix` silently packaged a drifted
1806    /// other list, or vice versa). Lifting the resolution to a typed
1807    /// method on the substrate primitive means every downstream
1808    /// consumer of the caixa's per-`Caixa` executable-source surface
1809    /// reaches for exactly one typed dispatch — the resolver's accept-
1810    /// set migrates as a unit on any future axis addition.
1811    ///
1812    /// Fourth outer top-level [`Caixa`] `&[T]`-return slice-accessor —
1813    /// folds on the "outer [`Caixa`] `&[T]` slice" projection pattern
1814    /// [`Self::autores`] (b5d813f) opened, [`Self::etiquetas`]
1815    /// (78c7d3c) folded on, and [`Self::bibliotecas`] (8a36c23) closed
1816    /// the universal-axis text-tag family of. Opens the outer-`Caixa`
1817    /// foreign-code-slot `&[T]` sub-family the sibling `:servicos`
1818    /// future lift closes onto (per the trio of code-surface list slots
1819    /// the [`Self::validate_code_paths`] per-slot dispatch tuple
1820    /// already carries — `:bibliotecas` + `:exe` + `:servicos`, of which
1821    /// `:bibliotecas` landed at 8a36c23 and `:servicos` remains as the
1822    /// last unlifted code-surface slot). Sibling in shape to the peer
1823    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
1824    /// (bc92bce), per-`:placement`
1825    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
1826    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
1827    /// (6c77e36), per-`:contratos`
1828    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1829    /// per-`:upgrade-from :instructions`
1830    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1831    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1832    /// typed-slot list axes, extended here to the outer top-level
1833    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1834    /// `&Vec<String>`) because every downstream consumer of the
1835    /// executable-source list treats it as a read-only sequence — the
1836    /// slice-view is the narrowest borrow that supports every
1837    /// present + roadmapped consumer (`.iter()`, `.len()`,
1838    /// `.is_empty()`) without leaking the backing `Vec`'s
1839    /// grow/push/reserve surface no consumer of the typed view
1840    /// reaches for (the storage-side `Vec` remains reachable through
1841    /// the `pub exe` field for the mutation-carrying serde
1842    /// round-trip and per-test fixture-mutation paths). Named `exe()`
1843    /// to match the storage field's name; the accessor's identity
1844    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
1845    /// docstring already carries.
1846    #[must_use]
1847    pub const fn exe(&self) -> &[String] {
1848        self.exe.as_slice()
1849    }
1850
1851    /// Substrate-canonical per-`Caixa` `:servicos` universal-axis
1852    /// ComputeUnit-CR-YAML-entry-path-list slice-accessor every consumer
1853    /// of the top-level manifest's Servico-component axis keys off —
1854    /// returns the author-declared `:servicos` list verbatim as a
1855    /// `&[String]` slice-view over the same backing buffer the raw
1856    /// `self.servicos.as_slice()` field access borrows from. Empty-list-
1857    /// carrying (`:servicos` is a default-empty axis every `defcaixa`
1858    /// form supplies with an empty `()` when unset; the
1859    /// [`Self::from_lisp`] derive folds an omitted `:servicos` through
1860    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
1861    /// definitionally carries a `Vec<String>` slot — possibly empty —
1862    /// and the returned `&[String]` degenerates to an empty slice on
1863    /// that arm without any silent `None` collapse).
1864    ///
1865    /// The `:servicos` slot carries the universal-axis
1866    /// `.computeunit.yaml` ComputeUnit-CR entry-path list every
1867    /// `:kind Servico` caixa emits under (CAIXA-SDLC §I — the
1868    /// author-facing surface every `defcaixa` form supplies alongside
1869    /// `:nome` / `:versao` / `:kind`; the substrate-wide
1870    /// `servicos/`-directory-fenced entry-carrier axis every downstream
1871    /// Servico-facing renderer keys off) — the typed slot's
1872    /// `Vec<String>` accept-set (empty-per-entry rejected through
1873    /// [`ManifestError::CodePathEmpty { slot: ":servicos" }`],
1874    /// non-sandboxed-relative-shape rejected through
1875    /// [`ManifestError::CodePathShape`], non-`.computeunit.yaml`
1876    /// extension rejected through
1877    /// [`ManifestError::CodePathNonComputeUnitYamlExtension`], cross-
1878    /// entry duplicate rejected through
1879    /// [`ManifestError::CodePathDuplicate`], `len != 1` rejected by the
1880    /// V0 [`crate::ServicoCountMismatch`] gate on the per-Servico
1881    /// renderer entry-points, out-of-`servicos/`-directory paths
1882    /// rejected past the layout's [`crate::LayoutError::ServicoOutsideDir`]
1883    /// `starts_with` fence) maps onto every load-bearing downstream
1884    /// consumer the substrate carries — the [`crate::LayoutInvariants`]
1885    /// Servico-arm empty-check + per-entry file-exists + `servicos/`-
1886    /// directory-fence loop at caixa-core/src/layout.rs that gates each
1887    /// entry through [`crate::LayoutError::ServicoWithoutServicos`] /
1888    /// `MissingEntry` / `ServicoOutsideDir`, the compound `has_code`
1889    /// OR-fold on the [`crate::LayoutError::SupervisorOwnsCode`] /
1890    /// [`crate::LayoutError::AplicacaoOwnsCode`] kind-coherence gate
1891    /// that fences code-surface slots off from the two no-code kinds,
1892    /// [`Self::declared_foreign_code_slots`]'s
1893    /// `!self.servicos.is_empty()` arm on the
1894    /// [`crate::LayoutError::ForeignCodeSlot`] gate that fences the
1895    /// `:servicos` code surface off from every non-Servico code-running
1896    /// kind, [`Self::validate_code_paths`]'s per-slot shape gate that
1897    /// walks each entry through the sandbox-relative / `.computeunit.
1898    /// yaml`-extension / cross-entry duplicate gates, the
1899    /// [`crate::require_single_servico`] V0 singularity gate every
1900    /// per-Servico renderer entry-point runs through
1901    /// [`crate::require_v0_servico_shape`], the `feira chart` /
1902    /// `feira deploy` per-verb `first_servico_path` walk at
1903    /// caixa-feira/src/cmd/chart.rs that resolves the singleton
1904    /// ComputeUnit-CR file, every future per-`Caixa` Servico-facing
1905    /// renderer the CAIXA-SDLC §I roadmap acknowledges (the future
1906    /// per-Servico OCI packager, the future M4
1907    /// `wasm.pleme.io/v1alpha1/ComputeUnit` CR materializer, the future
1908    /// per-Servico OTel collector-config emit).
1909    ///
1910    /// Prior to this lift the `.servicos` field was accessed inline at
1911    /// five production sites — the compound-code-path `has_code =
1912    /// !caixa.bibliotecas().is_empty() || !caixa.exe().is_empty() ||
1913    /// !caixa.servicos.is_empty()` OR-fold on the
1914    /// [`crate::LayoutError::SupervisorOwnsCode`] /
1915    /// `AplicacaoOwnsCode` kind-coherence gate, the Servico-arm
1916    /// `caixa.servicos.is_empty()`
1917    /// [`crate::LayoutError::ServicoWithoutServicos`] gate, the
1918    /// per-entry `for p in &caixa.servicos`
1919    /// `MissingEntry`/`ServicoOutsideDir` walk, the
1920    /// [`Self::declared_foreign_code_slots`]'s
1921    /// `!self.servicos.is_empty()` arm on the `ForeignCodeSlot` gate,
1922    /// and the [`crate::require_single_servico`] V0 count gate's
1923    /// `caixa.servicos.len() == 1` / `caixa.servicos.len()` count
1924    /// projection (both the accept-arm predicate and the
1925    /// diagnostic-carrying `ServicoCountMismatch { count }`
1926    /// projection) — five open-coded field-accesses across three
1927    /// crates that expressed no compile-time link back to the typed
1928    /// slot. A future extension of the `:servicos` axis to a richer
1929    /// component surface — a per-`:servicos` structured
1930    /// `ServicoEntry { path, world, capabilities }` at the storage
1931    /// layer once the substrate absorbs the per-component WIT-world +
1932    /// capability-set tuple the CAIXA-SDLC §I Servico roadmap
1933    /// acknowledges, a per-registry `:servicos` allowlist the M4 CR
1934    /// materializer enforces per-CR (the "cluster policy demands every
1935    /// Servico declare an explicit `:world`" arm), a promotion of the
1936    /// plain `Vec<String>` byte-string list to a richer
1937    /// `Vec<ComputeUnitPath>` newtype discriminated on the
1938    /// `servicos/<nome>.computeunit.yaml`-shape grammar the layout's
1939    /// `starts_with(servicos_dir)` fence and the
1940    /// [`crate::render::is_computeunit_yaml_extension`] predicate
1941    /// already resolve through, a promotion of the V0 singleton
1942    /// contract to a multi-component `Vec<ComputeUnitPath>` past the M5
1943    /// component-model multi-world boundary — would have had to be
1944    /// threaded through all five open-coded copies in lockstep or the
1945    /// layout gate, the shape validator, the V0 count gate, and the
1946    /// `feira chart` / `feira deploy` entry-point walks would silently
1947    /// disagree on which ComputeUnit-CR paths a given [`Caixa`]
1948    /// resolves to (an author's `:servicos ("servicos/foo.computeunit.
1949    /// yaml")` would satisfy layout while `feira chart` silently
1950    /// packaged a drifted other list, or vice versa). Lifting the
1951    /// resolution to a typed method on the substrate primitive means
1952    /// every downstream consumer of the caixa's per-`Caixa`
1953    /// ComputeUnit-CR-source surface reaches for exactly one typed
1954    /// dispatch — the resolver's accept-set migrates as a unit on any
1955    /// future axis addition.
1956    ///
1957    /// Fifth and final outer top-level [`Caixa`] `&[T]`-return slice-
1958    /// accessor — folds on the "outer [`Caixa`] `&[T]` slice"
1959    /// projection pattern [`Self::autores`] (b5d813f) opened,
1960    /// [`Self::etiquetas`] (78c7d3c) folded on, [`Self::bibliotecas`]
1961    /// (8a36c23) closed the universal-axis text-tag family of, and
1962    /// [`Self::exe`] (65d9527) opened the foreign-code-slot sub-family
1963    /// of. Closes the outer-`Caixa` foreign-code-slot `&[T]` sub-family
1964    /// — with `:bibliotecas`, `:exe`, and `:servicos` now each carrying
1965    /// a substrate-canonical slice accessor, the trio of code-surface
1966    /// list slots the [`Self::validate_code_paths`] per-slot dispatch
1967    /// tuple carries is complete on the typed dispatch surface (the
1968    /// internal `[(":bibliotecas", &self.bibliotecas, ..), (":exe",
1969    /// &self.exe, ..), (":servicos", &self.servicos, ..)]` per-slot
1970    /// dispatch tuple's homogeneous `&Vec<String>`-typed shape blocks a
1971    /// per-element accessor swap in isolation — a future companion lift
1972    /// promotes the tuple's element type to `&[String]` and threads the
1973    /// triple of typed dispatches through as a unit). Sibling in shape
1974    /// to the peer per-`:supervisor`
1975    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
1976    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
1977    /// (a6e18d7), per-`:membros`
1978    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
1979    /// per-`:contratos`
1980    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
1981    /// per-`:upgrade-from :instructions`
1982    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
1983    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
1984    /// typed-slot list axes, extended here to the outer top-level
1985    /// [`Caixa`] universal-axis surface. Returns `&[String]` (not
1986    /// `&Vec<String>`) because every downstream consumer of the
1987    /// ComputeUnit-CR-source list treats it as a read-only sequence —
1988    /// the slice-view is the narrowest borrow that supports every
1989    /// present + roadmapped consumer (`.iter()`, `.len()`,
1990    /// `.is_empty()`, `.first()`) without leaking the backing `Vec`'s
1991    /// grow/push/reserve surface no consumer of the typed view reaches
1992    /// for (the storage-side `Vec` remains reachable through the
1993    /// `pub servicos` field for the mutation-carrying serde round-trip
1994    /// and per-test fixture-mutation paths, and for the
1995    /// [`Self::validate_code_paths`] per-slot dispatch tuple whose
1996    /// homogeneous-element-type shape carries the raw field access
1997    /// until the trio-closure lift promotes the tuple as a unit).
1998    /// Named `servicos()` to match the storage field's name; the
1999    /// accessor's identity maps onto the canonical CAIXA-SDLC §I
2000    /// vocabulary the slot's docstring already carries.
2001    #[must_use]
2002    pub const fn servicos(&self) -> &[String] {
2003        self.servicos.as_slice()
2004    }
2005
2006    /// Substrate-canonical per-`Caixa` `:deps` universal-axis
2007    /// runtime-dependency-declaration-list slice-accessor every consumer
2008    /// of the top-level manifest's runtime-dep-graph axis keys off —
2009    /// returns the author-declared `:deps` list verbatim as a `&[Dep]`
2010    /// slice-view over the same backing buffer the raw
2011    /// `self.deps.as_slice()` field access borrows from. Empty-list-
2012    /// carrying (`:deps` is a default-empty axis every `defcaixa` form
2013    /// supplies with an empty `()` when unset; the [`Self::from_lisp`]
2014    /// derive folds an omitted `:deps` through `#[serde(default)]` to
2015    /// `Vec::new()`, so a `Caixa` past parse definitionally carries a
2016    /// `Vec<Dep>` slot — possibly empty — and the returned `&[Dep]`
2017    /// degenerates to an empty slice on that arm without any silent
2018    /// `None` collapse).
2019    ///
2020    /// The `:deps` slot carries the universal-axis runtime dependency
2021    /// list every kind of caixa emits under (CAIXA-SDLC §I — the author-
2022    /// facing surface every `defcaixa` form supplies alongside `:nome` /
2023    /// `:versao` / `:kind`; the substrate-wide runtime-closure-input axis
2024    /// every downstream resolver-facing artifact emits under) — the
2025    /// typed slot's `Vec<Dep>` accept-set (empty-`:nome` rejected through
2026    /// [`DepError::NomeEmpty`], non-DNS-1123-label `:nome` rejected
2027    /// through [`DepError::NomeInvalid`], malformed `:versao` rejected
2028    /// through [`DepError::VersaoInvalid`], empty `:fonte.repo` rejected
2029    /// through [`DepError::FonteRepoEmpty`], within-list duplicate `:nome`
2030    /// rejected through [`DepError::DuplicateNome { list: ":deps" }`])
2031    /// maps onto every load-bearing downstream consumer the substrate
2032    /// carries — the [`Self::validate_deps`] per-entry
2033    /// [`Dep::validate`] + within-list dedup walk at
2034    /// caixa-core/src/manifest.rs, the [`crate::dep::validate_no_self_dep`]
2035    /// cross-list self-reference gate at caixa-core/src/layout.rs that
2036    /// checks each entry against the caixa's own `:nome`, the
2037    /// caixa-resolver `for dep in &root.deps` closure walk at
2038    /// caixa-resolver/src/resolve.rs that seeds every git-clone target
2039    /// through the resolver's [`crate::Dep`]-keyed pipeline, the
2040    /// caixa-crd `caixa.deps.iter().map(dep_into_ref).collect()` fold at
2041    /// caixa-crd/src/conversion.rs that materializes each entry into the
2042    /// K8s `Caixa` CR's `spec.deps` field, every future per-`Caixa`
2043    /// resolver-facing renderer the CAIXA-SDLC §I roadmap acknowledges
2044    /// (the future per-cluster runtime-closure-audit overlay the M4 CR
2045    /// materializer resolves per-CR, the future `lacre.lisp` BLAKE3-
2046    /// closure emit walk the caixa-resolver docstring roadmaps).
2047    ///
2048    /// First outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2049    /// opens the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2050    /// sibling `:deps-dev` future lift closes on. Peer of the closed
2051    /// outer-`Caixa` foreign-code-slot `&[String]` sub-family
2052    /// ([`Self::bibliotecas`] 8a36c23, [`Self::exe`] 65d9527,
2053    /// [`Self::servicos`] 611f78b) and the outer-`Caixa` universal-axis
2054    /// text-tag family ([`Self::autores`] b5d813f, [`Self::etiquetas`]
2055    /// 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice" projection
2056    /// pattern onto a novel element-type axis (`Dep` composite vs the
2057    /// prior sibling family's `String` scalar). Sibling in shape to the
2058    /// peer per-`:supervisor`
2059    /// [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
2060    /// per-`:placement` [`crate::aplicacao::Placement::clusters`]
2061    /// (a6e18d7), per-`:membros`
2062    /// [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
2063    /// per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
2064    /// (0dcc926), and per-`:upgrade-from :instructions`
2065    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2066    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2067    /// typed-slot list axes, extended here to the outer top-level
2068    /// [`Caixa`] universal-axis dep-graph surface. Returns `&[Dep]`
2069    /// (not `&Vec<Dep>`) because every downstream consumer of the
2070    /// runtime-dep list treats it as a read-only sequence — the slice-
2071    /// view is the narrowest borrow that supports every present +
2072    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2073    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2074    /// of the typed view reaches for (the storage-side `Vec` remains
2075    /// reachable through the `pub deps` field for the mutation-carrying
2076    /// serde round-trip and per-test fixture-mutation paths). Named
2077    /// `deps()` to match the storage field's name; the accessor's
2078    /// identity maps onto the canonical CAIXA-SDLC §I vocabulary the
2079    /// slot's docstring already carries.
2080    #[must_use]
2081    pub const fn deps(&self) -> &[Dep] {
2082        self.deps.as_slice()
2083    }
2084
2085    /// Substrate-canonical per-`Caixa` `:deps-dev` universal-axis
2086    /// development-only-dependency-declaration-list slice-accessor every
2087    /// consumer of the top-level manifest's dev-dep-graph axis keys off —
2088    /// returns the author-declared `:deps-dev` list verbatim as a `&[Dep]`
2089    /// slice-view over the same backing buffer the raw
2090    /// `self.deps_dev.as_slice()` field access borrows from. Empty-list-
2091    /// carrying (`:deps-dev` is a default-empty axis every `defcaixa`
2092    /// form supplies with an empty `()` when unset; the
2093    /// [`Self::from_lisp`] derive folds an omitted `:deps-dev` through
2094    /// `#[serde(default)]` to `Vec::new()`, so a `Caixa` past parse
2095    /// definitionally carries a `Vec<Dep>` slot — possibly empty — and
2096    /// the returned `&[Dep]` degenerates to an empty slice on that arm
2097    /// without any silent `None` collapse).
2098    ///
2099    /// The `:deps-dev` slot carries the universal-axis dev-only
2100    /// dependency list every kind of caixa emits under (CAIXA-SDLC §I —
2101    /// the author-facing sibling of `:deps` that every `defcaixa` form
2102    /// supplies to declare tests / lint / bench closures the runtime
2103    /// `:deps` axis does not carry; the substrate-wide dev-closure-input
2104    /// axis every downstream test-facing artifact emits under, matching
2105    /// Cargo's `[dev-dependencies]` table's dev-time-only visibility
2106    /// contract) — the typed slot's `Vec<Dep>` accept-set (empty-`:nome`
2107    /// rejected through [`DepError::NomeEmpty`], non-DNS-1123-label
2108    /// `:nome` rejected through [`DepError::NomeInvalid`], malformed
2109    /// `:versao` rejected through [`DepError::VersaoInvalid`], empty
2110    /// `:fonte.repo` rejected through [`DepError::FonteRepoEmpty`],
2111    /// within-list duplicate `:nome` rejected through
2112    /// [`DepError::DuplicateNome { list: ":deps-dev" }`]) maps onto every
2113    /// load-bearing downstream consumer the substrate carries — the
2114    /// [`Self::validate_deps`] per-entry [`Dep::validate`] + within-list
2115    /// dedup walk at caixa-core/src/manifest.rs, the
2116    /// [`crate::dep::validate_no_self_dep`] cross-list self-reference
2117    /// gate at caixa-core/src/layout.rs that checks each entry against
2118    /// the caixa's own `:nome`, the caixa-resolver
2119    /// `for dep in &root.deps_dev` closure walk at
2120    /// caixa-resolver/src/resolve.rs that seeds every dev-only git-clone
2121    /// target through the resolver's [`crate::Dep`]-keyed pipeline, and
2122    /// every future per-`Caixa` resolver-facing renderer the CAIXA-SDLC
2123    /// §I roadmap acknowledges (the future per-cluster dev-closure-audit
2124    /// overlay the M4 CR materializer resolves per-CR, the future
2125    /// `lacre.lisp` BLAKE3-closure emit walk the caixa-resolver docstring
2126    /// roadmaps).
2127    ///
2128    /// Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor —
2129    /// closes the outer-`Caixa` dependency-slot `&[Dep]` sub-family the
2130    /// sibling [`Self::deps`] (ad34b4e) opened on. The two accessors
2131    /// jointly close the two-list dep-graph surface every downstream
2132    /// resolver-facing consumer keys off (runtime `:deps` +
2133    /// dev-only `:deps-dev`, the canonical Cargo-shaped dependency-table
2134    /// pair the [`Self::validate_deps`] gate already walks in canonical
2135    /// order). Peer of the closed outer-`Caixa` foreign-code-slot
2136    /// `&[String]` sub-family ([`Self::bibliotecas`] 8a36c23,
2137    /// [`Self::exe`] 65d9527, [`Self::servicos`] 611f78b) and the outer-
2138    /// `Caixa` universal-axis text-tag family ([`Self::autores`]
2139    /// b5d813f, [`Self::etiquetas`] 78c7d3c) — folds the "outer
2140    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
2141    /// dev-dep composite-element axis (`Dep` composite, matching the
2142    /// [`Self::deps`] element type). Sibling in shape to the peer
2143    /// per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
2144    /// (bc92bce), per-`:placement`
2145    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7),
2146    /// per-`:membros` [`crate::aplicacao::AplicacaoSpec::membros`]
2147    /// (6c77e36), per-`:contratos`
2148    /// [`crate::aplicacao::AplicacaoSpec::contratos`] (0dcc926), and
2149    /// per-`:upgrade-from :instructions`
2150    /// [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
2151    /// `&[T]`-return slice accessors on the sibling per-M2 / per-M3
2152    /// typed-slot list axes, folded here to the outer top-level
2153    /// [`Caixa`] universal-axis dev-dep-graph surface. Returns `&[Dep]`
2154    /// (not `&Vec<Dep>`) because every downstream consumer of the
2155    /// dev-dep list treats it as a read-only sequence — the slice-view
2156    /// is the narrowest borrow that supports every present +
2157    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`) without
2158    /// leaking the backing `Vec`'s grow/push/reserve surface no consumer
2159    /// of the typed view reaches for (the storage-side `Vec` remains
2160    /// reachable through the `pub deps_dev` field for the mutation-
2161    /// carrying serde round-trip and per-test fixture-mutation paths).
2162    /// Named `deps_dev()` to match the storage field's `snake_case` name;
2163    /// the kebab-case author-surface tag `:deps-dev` is the same axis
2164    /// after tatara-lisp's kebab↔snake fold and the accessor's identity
2165    /// maps onto the canonical CAIXA-SDLC §I vocabulary the slot's
2166    /// docstring already carries.
2167    #[must_use]
2168    pub const fn deps_dev(&self) -> &[Dep] {
2169        self.deps_dev.as_slice()
2170    }
2171
2172    /// Substrate-canonical per-[`Caixa`] typed-dispatch read accessor
2173    /// every consumer that walks one of the two dep-list axes keyed on a
2174    /// [`crate::dep::DepList`] discriminant reaches for — routes the
2175    /// `(list: DepList) -> &[Dep]` projection through one typed method on
2176    /// the substrate primitive rather than the prior open-coded
2177    /// `match list { Prod => caixa.deps(), Dev => caixa.deps_dev() }`
2178    /// inline dispatch every per-axis walker would otherwise carry.
2179    /// Returns the author-declared per-list `Vec<Dep>` verbatim as a
2180    /// `&[Dep]` slice-view over the same backing buffer the sibling
2181    /// [`Self::deps`] (`Prod`) / [`Self::deps_dev`] (`Dev`) per-slot
2182    /// accessors borrow from, preserving the empty-list-carrying invariant
2183    /// each per-slot accessor already establishes (`:deps` / `:deps-dev`
2184    /// are default-empty axes every `defcaixa` form supplies with an empty
2185    /// `()` when unset; the [`Self::from_lisp`] derive folds an omitted
2186    /// list through `#[serde(default)]` to `Vec::new()`, so both arms
2187    /// definitionally carry a `Vec<Dep>` slot — possibly empty — and the
2188    /// returned `&[Dep]` degenerates to an empty slice on either arm
2189    /// without any silent `None` collapse).
2190    ///
2191    /// The [`crate::dep::DepList`] closed-set typed enum is the
2192    /// substrate's canonical discriminator for the "runtime-closure
2193    /// `:deps` vs dev-only-closure `:deps-dev`" axis every dep-list
2194    /// consumer dispatches on — the compiler-checked exhaustiveness on
2195    /// the enum's `match` arms is the build-time guarantee that no future
2196    /// per-list read-site regresses to a bare-`bool`-flag inline dispatch
2197    /// that a future third dep-list axis (a `:deps-build` build-only
2198    /// closure once the substrate grows cross-artifact heterogeneous
2199    /// dep-graphs, per CAIXA-SDLC §I) would silently split at every
2200    /// consumer. Prior to this the read side carried two per-slot
2201    /// accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`]) and no
2202    /// typed dispatch that a per-axis walker could parametrise on, so
2203    /// every per-list walker (the [`Self::validate_deps`] per-list
2204    /// [`crate::render::insert_first_seen`] dedup walk, a future
2205    /// `feira app graph` per-list dep summary, a future M4 per-cluster
2206    /// dev-closure-audit overlay the CR materializer resolves per-CR)
2207    /// open-coded the same two-block "run over `:deps`, then run over
2208    /// `:deps-dev`" pattern — a silent duplication that a future third
2209    /// dep-list axis would have had to grow a third block at every site.
2210    ///
2211    /// Peer of the sibling [`Self::push_dep`] typed-mutation dispatch
2212    /// (359fba5) — closes the two-side dispatch symmetry on the outer
2213    /// [`Caixa`] two-list dep-graph surface: `push_dep` on the mutation
2214    /// side, `deps_of` on the read side, both keyed on the same
2215    /// [`crate::dep::DepList`] discriminator. Same "one typed dispatch on
2216    /// the substrate primitive, thin projections at each consumer"
2217    /// discipline the sibling per-slot read accessors ([`Self::nome`]
2218    /// e6b7d97, [`Self::versao`], [`Self::kind`]) carry — extended onto
2219    /// the outer-[`Caixa`] typed-dispatch read surface.
2220    ///
2221    /// Declared `pub const fn` — every operator in the body is already
2222    /// `const`-callable (the [`crate::dep::DepList`] enum is a plain
2223    /// closed-set `#[derive(Copy)]` discriminator so the `match` arms
2224    /// are const-evaluable, and each arm forwards through the sibling
2225    /// `pub const fn` [`Self::deps`] / [`Self::deps_dev`] per-slot
2226    /// slice accessor). Pinned load-bearing by the paired
2227    /// [`caixa_deps_of_is_const_fn`][pin] wrapper test (a
2228    /// `const fn deps_of_via_const_fn(c: &Caixa, l: DepList) -> &[Dep]`
2229    /// that forwards through this accessor) — any future accidental
2230    /// downgrade to non-`const` fails the wrapper at caixa-core build
2231    /// time with E0015 (`cannot call non-const method`), strictly
2232    /// stronger than a runtime `assert!` and side-stepping the
2233    /// destructor-in-const restriction the `Caixa` fixture's owning
2234    /// carriers rule out on the direct-`const _: () = assert!(…)`
2235    /// residence. Peer of the sibling per-`Dep` outer-accessor
2236    /// family's parallel `const`-eval-surface pass and of the outer-
2237    /// `Caixa` slice-return accessor family's earlier pass (231a968)
2238    /// — same "one canonical dispatch per axis, `const`-eval posture
2239    /// pinned at the substrate primitive, thin projections at each
2240    /// consumer" discipline extended onto the outer-`Caixa`
2241    /// typed-dispatch read surface on the [`DepList`]-keyed dep-list
2242    /// axis.
2243    ///
2244    /// [DepList]: crate::dep::DepList
2245    /// [pin]: tests::caixa_deps_of_is_const_fn
2246    #[must_use]
2247    pub const fn deps_of(&self, list: crate::dep::DepList) -> &[Dep] {
2248        match list {
2249            crate::dep::DepList::Prod => self.deps(),
2250            crate::dep::DepList::Dev => self.deps_dev(),
2251        }
2252    }
2253
2254    /// Substrate-canonical per-[`Caixa`] typed-mutation dispatch every
2255    /// consumer that appends to one of the two dep-list axes keys off
2256    /// — routes the `(list: DepList, dep: Dep)` tuple through one typed
2257    /// method on the substrate primitive rather than the prior
2258    /// `feira add`-side open-coded `if self.dev { &mut caixa.deps_dev }
2259    /// else { &mut caixa.deps }` inline dispatch + open-coded
2260    /// `.iter().any(|d| d.nome == …)` dup-check cascade. Refuses the
2261    /// mutation with the canonical typed [`DepError::DuplicateNome`] on
2262    /// a within-list name collision — the same `list: &'static str`
2263    /// diagnostic shape [`Self::validate_deps`]'s per-list
2264    /// [`crate::render::insert_first_seen`] walk raises on the peer
2265    /// parse-time within-list dedup axis, so a future author reading a
2266    /// `feira add` refusal and a `feira build` refusal reaches for the
2267    /// same corrective surface without switching diagnostic idioms.
2268    ///
2269    /// The two-arm [`crate::dep::DepList`] enum is the substrate's
2270    /// closed-set typed carrier for the "runtime-closure `:deps` vs
2271    /// dev-only-closure `:deps-dev`" axis every dep-list consumer
2272    /// dispatches on — the compiler-checked exhaustiveness on the
2273    /// enum's `match` arms is the build-time guarantee that no future
2274    /// per-list mutation-site regresses to a bare-`bool`-flag
2275    /// (`is_dev: bool`) inline dispatch that a future third
2276    /// dep-list axis (a `:deps-build` build-only closure once the
2277    /// substrate grows cross-artifact heterogeneous dep-graphs, per
2278    /// CAIXA-SDLC §I) would silently split at every consumer.
2279    ///
2280    /// Same "one typed dispatch on the substrate primitive, thin
2281    /// projections at each consumer" discipline the sibling per-slot
2282    /// read accessors ([`Self::deps`] ad34b4e, [`Self::deps_dev`],
2283    /// [`Self::nome`] e6b7d97, [`Self::versao`], [`Self::kind`])
2284    /// carry — extended onto the outer-[`Caixa`] typed-mutation surface,
2285    /// the substrate's first typed-mutation dispatch on the top-level
2286    /// manifest. The prior `feira add` open-coded `&mut caixa.deps` /
2287    /// `&mut caixa.deps_dev` inline field-access + `bail!` string-
2288    /// diagnostic path routed no through-line back to the typed slot,
2289    /// so a future extension of either dep-list axis to a richer author
2290    /// surface (a per-cluster override the operator pins through a
2291    /// future `:placement`-scoped dep-list slot the CAIXA-SDLC §I
2292    /// roadmap acknowledges, an M4
2293    /// `mesh.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
2294    /// admission-webhook that normalized the list at admission time)
2295    /// would have had to be threaded through the `feira add` mutation
2296    /// site in lockstep with every read consumer or one path would
2297    /// silently disagree with the other on which list a given dep lands
2298    /// in. Lifting the resolution rule to a typed method on the
2299    /// substrate primitive means every downstream dep-list-mutating
2300    /// consumer of the top-level manifest reaches for exactly one typed
2301    /// dispatch — the resolver's accept-set migrates as a unit on any
2302    /// future axis addition.
2303    ///
2304    /// # Errors
2305    ///
2306    /// Returns [`DepError::DuplicateNome`] with `list = list.as_str()`
2307    /// when another entry in the same list already carries the same
2308    /// `:nome` — the mutation is refused and the caller can surface the
2309    /// typed diagnostic to the author (the `feira add` verb routes the
2310    /// error through `anyhow::Error::from`, which preserves the
2311    /// canonical `#[error(...)]`-templated diagnostic body).
2312    pub fn push_dep(&mut self, list: crate::dep::DepList, dep: Dep) -> Result<(), DepError> {
2313        let target = match list {
2314            crate::dep::DepList::Prod => &mut self.deps,
2315            crate::dep::DepList::Dev => &mut self.deps_dev,
2316        };
2317        if target.iter().any(|d| d.nome() == dep.nome()) {
2318            return Err(DepError::duplicate_nome(dep.nome(), list.as_str()));
2319        }
2320        target.push(dep);
2321        Ok(())
2322    }
2323
2324    /// Substrate-canonical per-`Caixa` `:limits` M2 typed-slot outer-
2325    /// composite Lunatic-per-process wasm32-sandboxing-composite optional-
2326    /// composite-reference accessor every consumer of the top-level
2327    /// manifest's per-Servico [`LimitsSpec`] outer-composite reader keys
2328    /// off — returns the author-declared `:limits` typed composite
2329    /// verbatim as an `Option<&LimitsSpec>` reference over the same
2330    /// backing storage the raw `self.limits.as_ref()` field access
2331    /// borrows from, with `None` naming the "no `:limits` block
2332    /// authored — every per-axis Lunatic-sandbox cap defers to the
2333    /// wasm-engine-default arm named on the per-axis
2334    /// [`LimitsSpec::memory`] / [`LimitsSpec::fuel`] /
2335    /// [`LimitsSpec::wall_clock`] / [`LimitsSpec::cpu`] scalar-accessor
2336    /// docstrings" partition every downstream Servico-M2-overlay
2337    /// emitter treats as "emit nothing" and the sibling
2338    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate
2339    /// treats as "skip the per-axis
2340    /// [`crate::LimitsError::MemoryZero`] / `MemoryBelowWasm32Page` /
2341    /// `FuelZero` / `WallClockZero` / `CpuZero` refusal cascade".
2342    ///
2343    /// The outer `:limits` slot carries the M2 Servico-runtime typed
2344    /// composite — the load-bearing container of every Lunatic-shaped
2345    /// per-process wasm32-sandbox cap axis every long-running wasm
2346    /// component's runtime dispatches on (INSPIRATIONS §III.1 —
2347    /// Lunatic per-process linear-memory / fuel / wall-clock /
2348    /// millicore cap primitives translated onto pleme-io's typed
2349    /// `:limits :memory` / `:limits :fuel` / `:limits :wall-clock` /
2350    /// `:limits :cpu` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2351    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2352    /// chart both fan on). Every per-`:limits` axis threads through a
2353    /// lifted per-slot accessor on the [`LimitsSpec`] type: the
2354    /// [`LimitsSpec::memory`] wasm32 linear-memory byte-cap scalar
2355    /// accessor, the [`LimitsSpec::fuel`] wasmtime fuel-cap scalar
2356    /// accessor, the [`LimitsSpec::wall_clock`] per-call wall-clock
2357    /// deadline scalar accessor, and the [`LimitsSpec::cpu`]
2358    /// K8s-millicore soft-CPU-share scalar accessor. Every downstream
2359    /// consumer that reaches for a limits axis first passes through
2360    /// this outer accessor onto the composite and then dispatches
2361    /// onto the per-axis accessor — the two-level dispatch means
2362    /// every per-`:limits` reader now routes through a typed dispatch
2363    /// on the substrate primitive at both altitudes.
2364    ///
2365    /// Prior to this lift the `.limits` `Option<LimitsSpec>` composite
2366    /// was accessed inline at three production sites — the
2367    /// [`crate::StandardLayout::verify`] per-`:limits` shape gate's
2368    /// `if let Some(l) = &caixa.limits { … }` traversal head
2369    /// (caixa-core/src/layout.rs:882, which drives the per-axis
2370    /// refusal cascade on the composite: the `LimitsError::MemoryZero`
2371    /// / `MemoryBelowWasm32Page` / `MemoryExceedsWasm32Max` /
2372    /// `FuelZero` / `FuelExceedsMax` / `WallClockZero` /
2373    /// `WallClockExceedsMax` / `CpuZero` / `CpuExceedsMax` refusals
2374    /// [`LimitsSpec::validate`] fans onto), the
2375    /// [`crate::render::servico_m2_overlay`] per-Servico M2 overlay
2376    /// emitter's `if let Some(limits) = &caixa.limits { … }` traversal
2377    /// head (caixa-core/src/render.rs:18504, which drives the
2378    /// `M2_KEY_LIMITS`-keyed `limits.is_empty()`-gated `serde_yaml`
2379    /// projection every `caixa-helm` / `caixa-flux` Servico values-
2380    /// block emitter fans on), and the
2381    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2382    /// set enumerator's `self.limits.is_some()` presence probe
2383    /// (caixa-core/src/manifest.rs:1788, which drives the
2384    /// `M2_AUTHOR_KEY_LIMITS` kebab-case author-label push every
2385    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2386    /// gate reads) — three open-coded outer-field accesses that
2387    /// expressed no compile-time link back to the typed slot at the
2388    /// [`Caixa`] altitude. A future extension of the `:limits` outer
2389    /// axis to a richer author surface (a multi-`:limits` list the M4
2390    /// CR materializer resolves per-CR at admission time so a Servico
2391    /// can expose a compute-heavy + IO-heavy limits pair, a per-
2392    /// cluster `:limits-overrides` slot the operator pins so a
2393    /// cluster-specific policy can tighten a caixa-declared cap
2394    /// without re-authoring the `caixa.lisp`, a promotion of the
2395    /// plain `Option<LimitsSpec>` to a richer
2396    /// `{static, dynamic}` partition once the wasm-engine's runtime-
2397    /// resolved dynamic-cap surface lands) would have had to be
2398    /// threaded through all three open-coded copies in lockstep or
2399    /// one consumer would silently disagree with the peers on which
2400    /// limits composite a given Caixa resolves to — the layout gate's
2401    /// per-axis bracket-dispatch seed reading the raw slot while the
2402    /// peer `servico_m2_overlay` emitter read an operator-resolved
2403    /// slot would silently split the build-time sandbox-shape gate
2404    /// from the runtime `ComputeUnit` CR emission gate, a three-
2405    /// consumer split at the layout gate, the M2 overlay emitter, and
2406    /// the declared-slot enumerator far from the source `caixa.lisp`
2407    /// with no field naming the limits-drift root cause. Lifting the
2408    /// resolution rule to a typed method on the substrate primitive
2409    /// means every downstream consumer of the caixa's per-`Caixa`
2410    /// Lunatic-sandboxing outer-composite surface reaches for exactly
2411    /// one typed dispatch — the resolver's accept-set migrates as a
2412    /// unit on any future axis addition.
2413    ///
2414    /// First outer top-level [`Caixa`] `Option<&Composite>`-return
2415    /// composite-reference accessor — opens the outer-`Caixa`
2416    /// `Option<&Composite>` composite-reference projection pattern the
2417    /// sibling per-`Caixa` `:behavior` [`crate::BehaviorSpec`] /
2418    /// `:politicas` [`crate::aplicacao::MeshPolicy`] / `:placement`
2419    /// [`crate::aplicacao::Placement`] / `:entrada`
2420    /// [`crate::aplicacao::Entrada`] future outer-composite lifts
2421    /// fold on. Peer of the M3 mesh-slot outer-composite family the
2422    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2423    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2424    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2425    /// accessors already close on the outer [`crate::AplicacaoSpec`]
2426    /// altitude — extends that "one typed dispatch on the substrate
2427    /// primitive, thin projections at each consumer" discipline onto
2428    /// the outer top-level [`Caixa`] altitude, opening the M2 Servico-
2429    /// runtime slot family's outer-composite axis. Returns
2430    /// `Option<&LimitsSpec>` (not the owning composite by copy or
2431    /// clone) because every downstream consumer of the limits
2432    /// composite treats it as a read-only per-axis dispatch source —
2433    /// the reference-view is the narrowest borrow that supports every
2434    /// present + roadmapped consumer (per-axis accessor dispatch,
2435    /// `.is_empty()`-gated overlay projection, presence-probe early
2436    /// return on the "author-omitted `:limits` ⇒ engine-default
2437    /// applies" partition) without cloning the composite through
2438    /// every consumer's fast path. The `Option` half of the return-
2439    /// type preserves the load-bearing "author-omitted `:limits` ⇒
2440    /// engine-default applies" partition (not a default composite the
2441    /// downstream must reject on emptiness) — the accessor projects
2442    /// the raw `Option<LimitsSpec>` slot's presence bit through the
2443    /// reference-return unchanged. Named `limits()` to match the
2444    /// storage field's name verbatim and the tatara-lisp author-
2445    /// surface term (`:limits`) the field's own docstring already
2446    /// carries.
2447    #[must_use]
2448    pub const fn limits(&self) -> Option<&LimitsSpec> {
2449        self.limits.as_ref()
2450    }
2451
2452    /// Substrate-canonical per-`Caixa` `:behavior` M2 typed-slot outer-
2453    /// composite OTP-`gen_server`-shaped callback-table optional-
2454    /// composite-reference accessor every consumer of the top-level
2455    /// manifest's per-Servico [`BehaviorSpec`] outer-composite reader
2456    /// keys off — returns the author-declared `:behavior` typed
2457    /// composite verbatim as an `Option<&BehaviorSpec>` reference over
2458    /// the same backing storage the raw `self.behavior.as_ref()` field
2459    /// access borrows from, with `None` naming the "no `:behavior`
2460    /// block authored — every per-callback OTP-shaped hook defers to
2461    /// the wasm-engine's runtime default arm named on the per-axis
2462    /// [`BehaviorSpec::on_init`] / [`BehaviorSpec::on_call`] /
2463    /// [`BehaviorSpec::on_cast`] / [`BehaviorSpec::on_info`] /
2464    /// [`BehaviorSpec::on_state_change`] /
2465    /// [`BehaviorSpec::on_terminate`] scalar-accessor docstrings"
2466    /// partition every downstream Servico-M2-overlay emitter treats as
2467    /// "emit nothing" and the sibling [`crate::StandardLayout::verify`]
2468    /// per-`:behavior` shape gate treats as "skip the per-arm
2469    /// [`crate::behavior::BehaviorError`] refusal cascade + the
2470    /// per-callback on-disk `MissingEntry` existence check".
2471    ///
2472    /// The outer `:behavior` slot carries the M2 Servico-runtime typed
2473    /// composite — the load-bearing container of every OTP-shaped
2474    /// per-Servico lifecycle-callback path axis every long-running wasm
2475    /// component's runtime dispatches on (INSPIRATIONS §II.3 — Erlang/
2476    /// OTP `gen_server:init/1` / `handle_call/3` / `handle_cast/2` /
2477    /// `handle_info/2` / `code_change/3` / `terminate/2` primitives
2478    /// translated onto pleme-io's typed `:behavior :on-init` /
2479    /// `:on-call` / `:on-cast` / `:on-info` / `:on-state-change` /
2480    /// `:on-terminate` sub-slot axes; CAIXA-SDLC §II — the typed-M2
2481    /// slot algebra the wasm-engine + `pleme-computeunit` Helm-library
2482    /// chart both fan on). Every per-`:behavior` axis threads through a
2483    /// lifted per-callback accessor on the [`BehaviorSpec`] type
2484    /// (9b4ecde / d66c702 / 156ddbe / 99616ac / 4846cef / 701add7).
2485    /// Every downstream consumer that reaches for a behavior axis
2486    /// first passes through this outer accessor onto the composite
2487    /// and then dispatches onto the per-callback accessor — the
2488    /// two-level dispatch means every per-`:behavior` reader now
2489    /// routes through a typed dispatch on the substrate primitive at
2490    /// both altitudes.
2491    ///
2492    /// Composes cross-slot with the M2 `:upgrade-from` gate: the
2493    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
2494    /// cross-slot composition gate at [`crate::StandardLayout::verify`]
2495    /// keys the "per-version `:state-change` instruction must have a
2496    /// `:on-state-change` callback" precondition off this accessor's
2497    /// composite (the callback-side counterpart to the
2498    /// `:upgrade-from :instructions :state-change :script` refusal at
2499    /// the appup-side). Threading that gate's traversal input through
2500    /// this accessor closes the cross-slot invariant on the substrate
2501    /// primitive, not on the raw field.
2502    ///
2503    /// Prior to this lift the `.behavior` `Option<BehaviorSpec>`
2504    /// composite was accessed inline at four production sites — the
2505    /// [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
2506    /// `if let Some(b) = &caixa.behavior { … }` traversal head
2507    /// (caixa-core/src/layout.rs:896, which drives the per-arm
2508    /// `BehaviorError` refusal cascade + the per-callback on-disk
2509    /// [`crate::LayoutError::MissingEntry`] existence check under
2510    /// [`crate::render::LAYOUT_MISSING_ENTRY_KIND_BEHAVIOR_CALLBACK`]),
2511    /// the [`crate::upgrade::validate_upgrade_from_against_behavior`]
2512    /// cross-slot composition gate's `caixa.behavior.as_ref()`
2513    /// traversal-input feed (caixa-core/src/layout.rs:1008, which
2514    /// drives the `:state-change` ↔ `:on-state-change` precondition
2515    /// refusal), the [`crate::render::servico_m2_overlay`] per-Servico
2516    /// M2 overlay emitter's `if let Some(behavior) = &caixa.behavior
2517    /// { … }` traversal head (caixa-core/src/render.rs:18513, which
2518    /// drives the `M2_KEY_BEHAVIOR`-keyed `behavior.is_empty()`-gated
2519    /// `serde_yaml` projection every `caixa-helm` / `caixa-flux`
2520    /// Servico values-block emitter fans on), and the
2521    /// [`Self::declared_servico_slots`] per-Servico M2 declared-slot-
2522    /// set enumerator's `self.behavior.is_some()` presence probe
2523    /// (caixa-core/src/manifest.rs:1919, which drives the
2524    /// `M2_AUTHOR_KEY_BEHAVIOR` kebab-case author-label push every
2525    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
2526    /// gate reads) — four open-coded outer-field accesses that
2527    /// expressed no compile-time link back to the typed slot at the
2528    /// [`Caixa`] altitude. A future extension of the `:behavior`
2529    /// outer axis to a richer author surface (a per-callback overlay
2530    /// resolver the operator materializes at admission time so a
2531    /// cluster-specific policy can inject a per-callback tracing
2532    /// interceptor without re-authoring the `caixa.lisp`, a promotion
2533    /// of the plain `Option<BehaviorSpec>` to a richer `{static,
2534    /// dynamic}` partition once a runtime-resolved behavior-swap
2535    /// surface lands, the M4 per-callback middleware chain the
2536    /// caixa-operator's per-Servico admission webhook keys off) would
2537    /// have had to be threaded through all four open-coded copies in
2538    /// lockstep or one consumer would silently disagree with the
2539    /// peers on which behavior composite a given Caixa resolves to —
2540    /// the layout gate's per-callback existence-check seed reading
2541    /// the raw slot while the peer `servico_m2_overlay` emitter read
2542    /// an operator-resolved slot would silently split the build-time
2543    /// callback-shape gate from the runtime `ComputeUnit` CR emission
2544    /// gate from the cross-slot `:state-change` composition gate from
2545    /// the M2 declared-slot enumerator, a four-consumer split far
2546    /// from the source `caixa.lisp` with no field naming the
2547    /// behavior-drift root cause. Lifting the resolution rule to a
2548    /// typed method on the substrate primitive means every downstream
2549    /// consumer of the caixa's per-`Caixa` OTP-callback-table outer-
2550    /// composite surface reaches for exactly one typed dispatch — the
2551    /// resolver's accept-set migrates as a unit on any future axis
2552    /// addition.
2553    ///
2554    /// Second outer top-level [`Caixa`] `Option<&Composite>`-return
2555    /// composite-reference accessor — sibling to the opening
2556    /// [`Self::limits`] (b2bd9d7) accessor on the outer-`Caixa`
2557    /// `Option<&Composite>` composite-reference sub-family, extends
2558    /// the "one typed dispatch on the substrate primitive, thin
2559    /// projections at each consumer" discipline onto the second of
2560    /// the three M2 Servico-runtime slots. The remaining
2561    /// `Option<&Composite>` axes at the outer top-level [`Caixa`]
2562    /// altitude — the M3 mesh-slot family (`:politicas`,
2563    /// `:placement`, `:entrada` — already closed on the inner
2564    /// [`crate::AplicacaoSpec`] altitude via 534dc21 / 9abb8f0 /
2565    /// d32111c) — remain the future sibling lifts on the outer
2566    /// top-level projection. Returns `Option<&BehaviorSpec>` (not
2567    /// the owning composite by copy or clone) because every
2568    /// downstream consumer of the behavior composite treats it as a
2569    /// read-only per-callback dispatch source — the reference-view is
2570    /// the narrowest borrow that supports every present + roadmapped
2571    /// consumer (per-callback accessor dispatch, `.is_empty()`-gated
2572    /// overlay projection, presence-probe early return on the
2573    /// "author-omitted `:behavior` ⇒ runtime-default applies"
2574    /// partition, cross-slot `:state-change` composition input)
2575    /// without cloning the composite through every consumer's fast
2576    /// path. The `Option` half of the return-type preserves the
2577    /// load-bearing "author-omitted `:behavior` ⇒ runtime-default
2578    /// applies" partition (not a default composite the downstream
2579    /// must reject on emptiness) — the accessor projects the raw
2580    /// `Option<BehaviorSpec>` slot's presence bit through the
2581    /// reference-return unchanged. Named `behavior()` to match the
2582    /// storage field's name verbatim and the tatara-lisp author-
2583    /// surface term (`:behavior`) the field's own docstring already
2584    /// carries.
2585    #[must_use]
2586    pub const fn behavior(&self) -> Option<&crate::BehaviorSpec> {
2587        self.behavior.as_ref()
2588    }
2589
2590    /// Substrate-canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
2591    /// composite MESH-COMPOSITION-shaped mesh-policy optional-composite-
2592    /// reference accessor every consumer of the top-level manifest's
2593    /// per-Aplicacao [`crate::aplicacao::MeshPolicy`] outer-composite
2594    /// reader keys off — returns the author-declared `:politicas` typed
2595    /// composite verbatim as an `Option<&MeshPolicy>` reference over the
2596    /// same backing storage the raw `self.politicas.as_ref()` field
2597    /// access borrows from, with `None` naming the "no `:politicas`
2598    /// block authored — every per-axis mesh-policy scalar defers to the
2599    /// cluster-default arm named on the per-axis
2600    /// [`crate::aplicacao::MeshPolicy::timeout`] /
2601    /// [`crate::aplicacao::MeshPolicy::retries`] /
2602    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] /
2603    /// [`crate::aplicacao::MeshPolicy::mtls_required`] /
2604    /// [`crate::aplicacao::MeshPolicy::rate_limit`] scalar-accessor
2605    /// docstrings" partition every downstream caixa-mesh /
2606    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2607    /// "emit no per-`:politicas` overlay" and the sibling
2608    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2609    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2610    /// arm.
2611    ///
2612    /// The outer `:politicas` slot carries the M3 mesh-slot per-
2613    /// Aplicacao typed composite — the load-bearing container of every
2614    /// mesh-level policy axis every Cilium NetworkPolicy / Gateway API
2615    /// v1.x HTTPRoute / future M4 per-edge policy overlay emitter fans
2616    /// on (MESH-COMPOSITION §III.2 — the Aplicacao's typed mesh-policy
2617    /// composite; §V — the "no infinite blocking" per-call deadline +
2618    /// "sandboxing-by-default" mTLS-enforcement CSE invariants; §III.3
2619    /// — the typed inter-Servico contrato-edge overlay the per-`(:de,
2620    /// :para)` mesh renderer keys off). Every per-`:politicas` axis
2621    /// threads through a lifted per-slot accessor on the
2622    /// [`crate::aplicacao::MeshPolicy`] type: the
2623    /// [`crate::aplicacao::MeshPolicy::mtls_required`] (c0110f1) Cilium
2624    /// mTLS-enforcement toggle, the
2625    /// [`crate::aplicacao::MeshPolicy::retries`] (bdfb399) transient-
2626    /// failure retry budget, the [`crate::aplicacao::MeshPolicy::timeout`]
2627    /// (7073d0f) Gateway-API per-call deadline, the
2628    /// [`crate::aplicacao::MeshPolicy::circuit_breaker`] (b0e741a)
2629    /// Envoy-outlier-detection composite. Every downstream consumer
2630    /// that reaches for a mesh-policy axis first passes through this
2631    /// outer accessor onto the composite and then dispatches onto the
2632    /// per-axis accessor — the two-level dispatch means every per-
2633    /// `:politicas` reader now routes through a typed dispatch on the
2634    /// substrate primitive at both altitudes.
2635    ///
2636    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2637    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2638    /// author-omitted arm onto the [`crate::aplicacao::MeshPolicy::default`]
2639    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::politicas`]
2640    /// (534dc21) `&MeshPolicy`-return accessor observes a typed
2641    /// composite whether or not the author declared the outer slot.
2642    /// The outer accessor preserves the "author-omitted vs authored-
2643    /// empty" partition the inner accessor's `is_empty()`-gated
2644    /// renderer overlay collapses — routing the presence bit through
2645    /// this accessor keeps the [`Self::declared_mesh_slots`] M3 kind-
2646    /// coherence enumerator's `M3_AUTHOR_KEY_POLITICAS` push separate
2647    /// from the inner `MeshPolicy::is_empty()`-gated overlay elision.
2648    ///
2649    /// Prior to this lift the `.politicas` `Option<MeshPolicy>`
2650    /// composite was accessed inline at two production sites — the
2651    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2652    /// `self.politicas.clone().unwrap_or_default()` traversal head
2653    /// (caixa-core/src/manifest.rs:1899, which drives the fold onto
2654    /// the [`crate::aplicacao::MeshPolicy::default`] cluster-default
2655    /// arm the inner [`crate::AplicacaoSpec::politicas`] accessor
2656    /// then observes), and the [`Self::declared_mesh_slots`] M3
2657    /// declared-slot-set enumerator's `self.politicas.is_some()`
2658    /// presence probe (caixa-core/src/manifest.rs:1961, which drives
2659    /// the `M3_AUTHOR_KEY_POLITICAS` kebab-case author-label push
2660    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2661    /// coherence gate reads) — two open-coded outer-field accesses
2662    /// that expressed no compile-time link back to the typed slot at
2663    /// the [`Caixa`] altitude. A future extension of the `:politicas`
2664    /// outer axis to a richer author surface (a per-cluster
2665    /// `:politicas-overrides` slot the operator materializes at
2666    /// admission time so a cluster-specific policy can tighten the
2667    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2668    /// promotion of the plain `Option<MeshPolicy>` to a richer
2669    /// `{static, dynamic}` partition once the M4 per-edge
2670    /// contrato-scoped policy-override surface lands, the M5 traffic-
2671    /// shaping composition the caixa-operator's per-Aplicacao mesh
2672    /// admission webhook keys off) would have had to be threaded
2673    /// through both open-coded copies in lockstep or the Aplicacao-
2674    /// composition seed's default-fold arm would silently disagree
2675    /// with the M3 declared-slot enumerator on which policy composite
2676    /// a given Caixa resolves to — the seed reading an operator-
2677    /// resolved slot while the enumerator's presence probe read the
2678    /// raw slot would silently split the build-time mesh-artifact
2679    /// emission gate from the M3 declared-slot enumerator's kind-
2680    /// coherence gate, a two-consumer split far from the source
2681    /// `caixa.lisp` with no field naming the policy-drift root cause.
2682    /// Lifting the resolution rule to a typed method on the substrate
2683    /// primitive means every downstream consumer of the caixa's per-
2684    /// `Caixa` MESH-COMPOSITION mesh-policy outer-composite surface
2685    /// reaches for exactly one typed dispatch — the resolver's
2686    /// accept-set migrates as a unit on any future axis addition.
2687    ///
2688    /// Third outer top-level [`Caixa`] `Option<&Composite>`-return
2689    /// composite-reference accessor — sibling to the opening
2690    /// [`Self::limits`] (b2bd9d7) and [`Self::behavior`] (35d8b52)
2691    /// accessors on the outer-`Caixa` `Option<&Composite>` composite-
2692    /// reference sub-family, extends the "one typed dispatch on the
2693    /// substrate primitive, thin projections at each consumer"
2694    /// discipline onto the first of the three M3 mesh-slot axes.
2695    /// Peer of the closed inner mesh-slot outer-composite family the
2696    /// sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
2697    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2698    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2699    /// accessor pins already close on the inner [`crate::AplicacaoSpec`]
2700    /// altitude — opens the outer top-level [`Caixa`] altitude's M3
2701    /// mesh-slot arm of the composite-reference family the remaining
2702    /// two axes (`:placement`, `:entrada`) fold onto in future
2703    /// sibling lifts. Returns `Option<&MeshPolicy>` (not the owning
2704    /// composite by copy or clone) because every downstream consumer
2705    /// of the mesh-policy composite treats it as a read-only per-axis
2706    /// dispatch source — the reference-view is the narrowest borrow
2707    /// that supports every present + roadmapped consumer (per-axis
2708    /// accessor dispatch, `.is_empty()`-gated overlay projection,
2709    /// presence-probe early return on the "author-omitted `:politicas`
2710    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2711    /// seed's default-fold arm) without cloning the composite through
2712    /// every consumer's fast path. The `Option` half of the return-
2713    /// type preserves the load-bearing "author-omitted `:politicas` ⇒
2714    /// cluster-default applies" partition (not a default composite
2715    /// the downstream must reject on emptiness) — the accessor
2716    /// projects the raw `Option<MeshPolicy>` slot's presence bit
2717    /// through the reference-return unchanged. Named `politicas()` to
2718    /// match the storage field's name verbatim and the tatara-lisp
2719    /// author-surface term (`:politicas`) the field's own docstring
2720    /// already carries.
2721    #[must_use]
2722    pub const fn politicas(&self) -> Option<&crate::aplicacao::MeshPolicy> {
2723        self.politicas.as_ref()
2724    }
2725
2726    /// Substrate-canonical per-`Caixa` `:placement` M3 mesh-slot outer-
2727    /// composite MESH-COMPOSITION-shaped distribution optional-composite-
2728    /// reference accessor every consumer of the top-level manifest's
2729    /// per-Aplicacao [`crate::aplicacao::Placement`] outer-composite
2730    /// reader keys off — returns the author-declared `:placement` typed
2731    /// composite verbatim as an `Option<&Placement>` reference over the
2732    /// same backing storage the raw `self.placement.as_ref()` field
2733    /// access borrows from, with `None` naming the "no `:placement`
2734    /// block authored — every per-axis placement scalar defers to the
2735    /// cluster-default arm named on the per-axis
2736    /// [`crate::aplicacao::Placement::estrategia`] /
2737    /// [`crate::aplicacao::Placement::clusters`] /
2738    /// [`crate::aplicacao::Placement::affinity`] /
2739    /// [`crate::aplicacao::Placement::shard_key`] scalar-accessor
2740    /// docstrings" partition every downstream caixa-mesh /
2741    /// caixa-flux / caixa-helm Aplicacao-artifact emitter treats as
2742    /// "emit no per-`:placement` overlay" and the sibling
2743    /// [`Self::aplicacao_view`] Aplicacao-composition seed folds through
2744    /// the [`crate::aplicacao::Placement::default`] cluster-default arm.
2745    ///
2746    /// The outer `:placement` slot carries the M3 mesh-slot per-
2747    /// Aplicacao typed distribution composite — the load-bearing
2748    /// container of every where-does-this-Aplicacao-run axis every
2749    /// caixa-mesh programs.yaml per-cluster distribution overlay /
2750    /// caixa-flux per-Aplicacao GitRepository/HelmRelease fan-out /
2751    /// future M4 per-Aplicacao Akka-style cluster-sharding entity-id
2752    /// resolver emitter fans on (MESH-COMPOSITION §II.4 — the
2753    /// Aplicacao's typed distribution composite; §V CSE invariants —
2754    /// "distribution is a first-class typed composite, not a runtime
2755    /// scheduler hint" the per-axis scalars enforce; §III.3 — the
2756    /// typed inter-Servico contrato-edge overlay the per-cluster
2757    /// mesh renderer keys off). Every per-`:placement` axis threads
2758    /// through a lifted per-slot accessor on the
2759    /// [`crate::aplicacao::Placement`] type: the
2760    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
2761    /// MESH-COMPOSITION distribution-strategy scalar, the
2762    /// [`crate::aplicacao::Placement::clusters`] (a6e18d7) per-cluster
2763    /// distribution-target slice, the [`crate::aplicacao::Placement::affinity`]
2764    /// M3-Adaptive-compression-hint optional-scalar, and the
2765    /// [`crate::aplicacao::Placement::shard_key`] (7cd2a28) Akka-cluster-
2766    /// sharding extractor-expression optional-scalar. Every downstream
2767    /// consumer that reaches for a placement axis first passes through
2768    /// this outer accessor onto the composite and then dispatches onto
2769    /// the per-axis accessor — the two-level dispatch means every per-
2770    /// `:placement` reader now routes through a typed dispatch on the
2771    /// substrate primitive at both altitudes.
2772    ///
2773    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2774    /// seed: the Aplicacao-view builder folds the outer `Option`'s
2775    /// author-omitted arm onto the [`crate::aplicacao::Placement::default`]
2776    /// cluster-default, so the peer inner [`crate::AplicacaoSpec::placement`]
2777    /// (9abb8f0) `&Placement`-return accessor observes a typed composite
2778    /// whether or not the author declared the outer slot. The outer
2779    /// accessor preserves the "author-omitted vs authored-empty" partition
2780    /// the inner accessor collapses at the cluster-default fold —
2781    /// routing the presence bit through this accessor keeps the
2782    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2783    /// `M3_AUTHOR_KEY_PLACEMENT` push separate from the inner
2784    /// [`crate::AplicacaoSpec::validate_placement`]-gated overlay
2785    /// dispatch.
2786    ///
2787    /// Prior to this lift the `.placement` `Option<Placement>`
2788    /// composite was accessed inline at two production sites — the
2789    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2790    /// `self.placement.clone().unwrap_or_default()` traversal head
2791    /// (caixa-core/src/manifest.rs:2036, which drives the fold onto
2792    /// the [`crate::aplicacao::Placement::default`] cluster-default
2793    /// arm the inner [`crate::AplicacaoSpec::placement`] accessor
2794    /// then observes), and the [`Self::declared_mesh_slots`] M3
2795    /// declared-slot-set enumerator's `self.placement.is_some()`
2796    /// presence probe (caixa-core/src/manifest.rs:2100, which drives
2797    /// the `M3_AUTHOR_KEY_PLACEMENT` kebab-case author-label push
2798    /// every [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
2799    /// coherence gate reads) — two open-coded outer-field accesses
2800    /// that expressed no compile-time link back to the typed slot at
2801    /// the [`Caixa`] altitude. A future extension of the `:placement`
2802    /// outer axis to a richer author surface (a per-cluster
2803    /// `:placement-overrides` slot the operator materializes at
2804    /// admission time so a cluster-specific placement can tighten the
2805    /// caixa-declared bound without re-authoring the `caixa.lisp`, a
2806    /// per-tenant placement-alias table the M4
2807    /// `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer resolves
2808    /// per-CR at admission time, a promotion of the plain
2809    /// `Option<Placement>` to a richer `{static, dynamic}` partition
2810    /// once Orleans-style virtual-actor dynamic placement comes into
2811    /// typed scope) would have had to be threaded through both open-
2812    /// coded copies in lockstep or the Aplicacao-composition seed's
2813    /// default-fold arm would silently disagree with the M3 declared-
2814    /// slot enumerator on which distribution composite a given Caixa
2815    /// resolves to — the seed reading an operator-resolved slot while
2816    /// the enumerator's presence probe read the raw slot would
2817    /// silently split the build-time distribution-artifact emission
2818    /// gate from the M3 declared-slot enumerator's kind-coherence
2819    /// gate, a two-consumer split far from the source `caixa.lisp`
2820    /// with no field naming the distribution-drift root cause.
2821    /// Lifting the resolution rule to a typed method on the substrate
2822    /// primitive means every downstream consumer of the caixa's per-
2823    /// `Caixa` MESH-COMPOSITION distribution outer-composite surface
2824    /// reaches for exactly one typed dispatch — the resolver's
2825    /// accept-set migrates as a unit on any future axis addition.
2826    ///
2827    /// Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
2828    /// composite-reference accessor — sibling to the opening
2829    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) M2-
2830    /// Servico-runtime pair and the peer [`Self::politicas`] (5d23d29)
2831    /// M3-mesh-slot arm on the outer-`Caixa` `Option<&Composite>`
2832    /// composite-reference sub-family, folds on the "one typed
2833    /// dispatch on the substrate primitive, thin projections at each
2834    /// consumer" discipline extended onto the second of the three M3
2835    /// mesh-slot axes. Peer of the closed inner mesh-slot outer-
2836    /// composite family the sibling
2837    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2838    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2839    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2840    /// accessor pins already close on the inner
2841    /// [`crate::AplicacaoSpec`] altitude — folds on the outer top-
2842    /// level [`Caixa`] altitude's M3 mesh-slot arm the sibling
2843    /// [`Self::politicas`] opened, extending the discipline onto the
2844    /// second of the three M3 mesh-slot axes. The remaining M3
2845    /// mesh-slot axis (`:entrada`) folds onto this accessor's
2846    /// discipline in the final sibling lift, closing the outer top-
2847    /// level [`Caixa`] `Option<&Composite>` M3 mesh-slot sub-family.
2848    /// Returns `Option<&Placement>` (not the owning composite by copy
2849    /// or clone) because every downstream consumer of the placement
2850    /// composite treats it as a read-only per-axis dispatch source —
2851    /// the reference-view is the narrowest borrow that supports every
2852    /// present + roadmapped consumer (per-axis accessor dispatch,
2853    /// serde composite-serialization on the programs.yaml overlay,
2854    /// presence-probe early return on the "author-omitted `:placement`
2855    /// ⇒ cluster-default applies" partition, `Aplicacao`-composition
2856    /// seed's default-fold arm) without cloning the composite through
2857    /// every consumer's fast path. The `Option` half of the return-
2858    /// type preserves the load-bearing "author-omitted `:placement` ⇒
2859    /// cluster-default applies" partition (not a default composite
2860    /// the downstream must reject on emptiness) — the accessor
2861    /// projects the raw `Option<Placement>` slot's presence bit
2862    /// through the reference-return unchanged. Named `placement()` to
2863    /// match the storage field's name verbatim and the tatara-lisp
2864    /// author-surface term (`:placement`) the field's own docstring
2865    /// already carries.
2866    #[must_use]
2867    pub const fn placement(&self) -> Option<&crate::aplicacao::Placement> {
2868        self.placement.as_ref()
2869    }
2870
2871    /// Substrate-canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
2872    /// composite MESH-COMPOSITION-shaped external-gateway optional-
2873    /// composite-reference accessor every consumer of the top-level
2874    /// manifest's per-Aplicacao [`crate::aplicacao::Entrada`] outer-
2875    /// composite reader keys off — returns the author-declared
2876    /// `:entrada` typed composite verbatim as an `Option<&Entrada>`
2877    /// reference over the same backing storage the raw
2878    /// `self.entrada.as_ref()` field access borrows from, with `None`
2879    /// naming the "no `:entrada` block authored — this Aplicacao is
2880    /// cluster-internal, no `Gateway`/`HTTPRoute` fan-out emitted"
2881    /// partition every downstream caixa-mesh Gateway-API artifact
2882    /// emitter treats as "emit no gateway-listener + no `HTTPRoute`
2883    /// backend for this Aplicacao" and the sibling
2884    /// [`Self::aplicacao_view`] Aplicacao-composition seed forwards
2885    /// verbatim (unlike the peer `:politicas` / `:placement` arms,
2886    /// `:entrada` has no cluster-default fold — an omitted `:entrada`
2887    /// stays `None` on the projected [`crate::AplicacaoSpec`] and the
2888    /// peer inner [`crate::AplicacaoSpec::entrada`] accessor observes
2889    /// the same `Option<&Entrada>` presence bit unchanged).
2890    ///
2891    /// The outer `:entrada` slot carries the M3 mesh-slot per-
2892    /// Aplicacao typed external-gateway composite — the load-bearing
2893    /// container of every how-does-the-outside-world-reach-this-
2894    /// Aplicacao axis every caixa-mesh `Gateway`/`HTTPRoute` fan-out
2895    /// emitter fans on (MESH-COMPOSITION §II.5 — the Aplicacao's typed
2896    /// external-entry composite; §V CSE invariants — "the external
2897    /// gateway is a first-class typed composite, not a per-Servico
2898    /// ingress annotation" the per-axis scalars enforce; §III.4 — the
2899    /// typed hostname + backend-Servico pair the per-cluster Gateway-
2900    /// API renderer keys off). Every per-`:entrada` axis threads
2901    /// through a lifted per-slot accessor on the
2902    /// [`crate::aplicacao::Entrada`] type: the
2903    /// [`crate::aplicacao::Entrada::host`] Gateway-API `Listener.hostname`
2904    /// scalar, the [`crate::aplicacao::Entrada::para`] backend-Servico
2905    /// caixa-name scalar, the [`crate::aplicacao::Entrada::paths`]
2906    /// per-rule `HTTPPathMatch` list, the [`crate::aplicacao::Entrada::port`]
2907    /// backend `trigger.service.port` scalar, and the
2908    /// [`crate::aplicacao::Entrada::resolved_paths`] URL-path fallback
2909    /// resolver every HTTPRoute-aware renderer consumes. Every
2910    /// downstream consumer that reaches for an entry axis first passes
2911    /// through this outer accessor onto the composite and then
2912    /// dispatches onto the per-axis accessor — the two-level dispatch
2913    /// means every per-`:entrada` reader now routes through a typed
2914    /// dispatch on the substrate primitive at both altitudes.
2915    ///
2916    /// Composes through [`Self::aplicacao_view`]'s Aplicacao-composition
2917    /// seed: the Aplicacao-view builder forwards the outer `Option`
2918    /// arm verbatim (no default fold — `:entrada` is inherently
2919    /// optional; a cluster-internal Aplicacao has no external gateway
2920    /// at all, not "an external gateway that defaults to nothing"), so
2921    /// the peer inner [`crate::AplicacaoSpec::entrada`] (d32111c)
2922    /// `Option<&Entrada>`-return accessor observes the same presence
2923    /// bit whether or not the author declared the outer slot. Routing
2924    /// the presence bit through this accessor keeps the
2925    /// [`Self::declared_mesh_slots`] M3 kind-coherence enumerator's
2926    /// `M3_AUTHOR_KEY_ENTRADA` push separate from the inner
2927    /// [`crate::AplicacaoSpec::validate_entrada`]-gated
2928    /// hostname/backend/path emission dispatch.
2929    ///
2930    /// Prior to this lift the `.entrada` `Option<Entrada>` composite
2931    /// was accessed inline at two production sites — the
2932    /// [`Self::aplicacao_view`] Aplicacao-composition seed's
2933    /// `self.entrada.clone()` traversal head (caixa-core/src/manifest.rs:2182,
2934    /// which drives the forward onto the peer inner
2935    /// [`crate::AplicacaoSpec::entrada`] accessor the caixa-mesh
2936    /// Gateway-API fan-out then observes), and the
2937    /// [`Self::declared_mesh_slots`] M3 declared-slot-set enumerator's
2938    /// `self.entrada.is_some()` presence probe (caixa-core/src/manifest.rs:2248,
2939    /// which drives the `M3_AUTHOR_KEY_ENTRADA` kebab-case author-
2940    /// label push every [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
2941    /// kind-coherence gate reads) — two open-coded outer-field
2942    /// accesses that expressed no compile-time link back to the typed
2943    /// slot at the [`Caixa`] altitude. A future extension of the
2944    /// `:entrada` outer axis to a richer author surface (a per-cluster
2945    /// `:entrada-overrides` slot the operator materializes at admission
2946    /// time so a cluster-specific hostname can pin the caixa-declared
2947    /// bound without re-authoring the `caixa.lisp`, a per-tenant
2948    /// gateway-alias table the M4 `mesh.pleme.io/v1alpha1/Aplicacao`
2949    /// CR materializer resolves per-CR at admission time, a promotion
2950    /// of the plain `Option<Entrada>` to a richer
2951    /// `{public, private, internal}` partition once Cilium-identity-
2952    /// scoped internal gateways come into typed scope) would have had
2953    /// to be threaded through both open-coded copies in lockstep or the
2954    /// Aplicacao-composition seed's forward arm would silently
2955    /// disagree with the M3 declared-slot enumerator on which external-
2956    /// gateway composite a given Caixa resolves to — the seed reading
2957    /// an operator-resolved slot while the enumerator's presence probe
2958    /// read the raw slot would silently split the build-time gateway-
2959    /// artifact emission gate from the M3 declared-slot enumerator's
2960    /// kind-coherence gate, a two-consumer split far from the source
2961    /// `caixa.lisp` with no field naming the entry-drift root cause.
2962    /// Lifting the resolution rule to a typed method on the substrate
2963    /// primitive means every downstream consumer of the caixa's per-
2964    /// `Caixa` MESH-COMPOSITION external-gateway outer-composite
2965    /// surface reaches for exactly one typed dispatch — the resolver's
2966    /// accept-set migrates as a unit on any future axis addition.
2967    ///
2968    /// Fifth and final outer top-level [`Caixa`] `Option<&Composite>`-
2969    /// return composite-reference accessor — closes the outer-`Caixa`
2970    /// `Option<&Composite>` composite-reference sub-family opened by
2971    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) on the
2972    /// M2 Servico-runtime arm and extended onto the M3 mesh-slot arm
2973    /// by [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074),
2974    /// folds on the "one typed dispatch on the substrate primitive,
2975    /// thin projections at each consumer" discipline extended onto the
2976    /// third and final M3 mesh-slot axis. Peer of the closed inner
2977    /// mesh-slot outer-composite family the sibling
2978    /// [`crate::AplicacaoSpec::politicas`] (534dc21) /
2979    /// [`crate::AplicacaoSpec::placement`] (9abb8f0) /
2980    /// [`crate::AplicacaoSpec::entrada`] (d32111c) composite-reference
2981    /// accessor pins already close on the inner
2982    /// [`crate::AplicacaoSpec`] altitude — this lift closes the mirror
2983    /// sub-family on the outer top-level [`Caixa`] altitude, so both
2984    /// altitudes of the outer-composite reference-return discipline
2985    /// (per-`Caixa` outer-slot presence + per-`AplicacaoSpec` inner-
2986    /// slot presence) now carry the full five-arm accept-set behind a
2987    /// typed dispatch on the substrate primitive. Returns
2988    /// `Option<&Entrada>` (not the owning composite by copy or clone)
2989    /// because every downstream consumer of the entrada composite
2990    /// treats it as a read-only per-axis dispatch source — the
2991    /// reference-view is the narrowest borrow that supports every
2992    /// present + roadmapped consumer (per-axis accessor dispatch,
2993    /// serde composite-serialization on the programs.yaml overlay,
2994    /// presence-probe early return on the "author-omitted `:entrada`
2995    /// ⇒ cluster-internal Aplicacao" partition, `Aplicacao`-composition
2996    /// seed's forward arm) without cloning the composite through every
2997    /// consumer's fast path. The `Option` half of the return-type
2998    /// preserves the load-bearing "author-omitted `:entrada` ⇒
2999    /// cluster-internal Aplicacao" partition (not a default composite
3000    /// the downstream must reject on emptiness — a cluster-internal
3001    /// Aplicacao has no external gateway at all, not "a default gateway
3002    /// that emits nothing"); the accessor projects the raw
3003    /// `Option<Entrada>` slot's presence bit through the reference-
3004    /// return unchanged. Named `entrada()` to match the storage field's
3005    /// name verbatim and the tatara-lisp author-surface term
3006    /// (`:entrada`) the field's own docstring already carries.
3007    #[must_use]
3008    pub const fn entrada(&self) -> Option<&crate::aplicacao::Entrada> {
3009        self.entrada.as_ref()
3010    }
3011
3012    /// Substrate-canonical per-`Caixa` `:ci` slot accessor — returns the
3013    /// author-declared typed CI run (`canteiro_types::CiRun`) verbatim as
3014    /// an `Option<&CiRun>`, borrowed from the typed slot's own
3015    /// `Option<CiRun>` storage. `None` when the slot is absent (every
3016    /// non-`Acao` kind, and an `Acao` caixa that hasn't declared `:ci`
3017    /// yet — the latter is caught by [`crate::LayoutError::MissingCi`],
3018    /// not silently accepted).
3019    ///
3020    /// Named `ci()` to match the storage field's name and the
3021    /// tatara-lisp author surface (`:ci`); mirrors the sibling
3022    /// `Option<&Composite>` accessors on this same `Caixa` altitude
3023    /// ([`Self::limits`], [`Self::behavior`], [`Self::politicas`],
3024    /// [`Self::placement`], [`Self::entrada`]) — one typed dispatch on
3025    /// the substrate primitive rather than an open-coded `self.ci.as_ref()`
3026    /// at every consumer.
3027    #[must_use]
3028    pub const fn ci(&self) -> Option<&canteiro_types::CiRun> {
3029        self.ci.as_ref()
3030    }
3031
3032    /// Substrate-canonical per-`Caixa` `:estrategia` M2 supervisor-tree-
3033    /// slot flat-spread OTP-shaped sibling-restart-strategy discriminant
3034    /// accessor every consumer of the top-level manifest's per-Supervisor
3035    /// restart-strategy axis keys off — returns the author-declared
3036    /// `:estrategia` variant verbatim as an `Option<RestartStrategy>`,
3037    /// `Copy`-projected from the typed slot's own
3038    /// `Option<crate::supervisor::RestartStrategy>` storage. Optional
3039    /// (`:estrategia` is a flat-spread supervisor-only slot every
3040    /// non-`Supervisor`-kind `defcaixa` carries as `None` by
3041    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3042    /// still omit to defer to [`RestartStrategy::default`] —
3043    /// [`RestartStrategy::OneForOne`] — through the [`Self::supervisor_view`]
3044    /// `unwrap_or_default()` fold; a returned `None` degenerates to the
3045    /// [`SupervisorSpec::default`]-inherited strategy without any silent
3046    /// promotion to a fresh explicit variant at the accessor boundary).
3047    ///
3048    /// The `:estrategia` slot carries the M2 typed OTP-shaped sibling-
3049    /// restart-strategy discriminant every substrate-side per-Supervisor
3050    /// dispatch fans on (INSPIRATIONS §II.2 — OTP `supervisor:strategy`
3051    /// closed-set `one_for_one | one_for_all | rest_for_one |
3052    /// simple_one_for_one` algebra translated onto pleme-io's typed
3053    /// [`RestartStrategy`] enum; CAIXA-SDLC §II — the M2 supervisor-tree
3054    /// slot algebra the operator's hierarchical reconciliation scheduler
3055    /// fans on). The slot is *flat-spread* on the outer top-level `Caixa`
3056    /// (per the field-shape docstring at caixa-core/src/manifest.rs — "The
3057    /// supervisor slots are flat on Caixa (vs nested under a
3058    /// `SupervisorSpec` sub-form) to keep tatara-lisp authoring at one
3059    /// level of nesting"), so the accessor's altitude is the outer
3060    /// [`Caixa`] surface rather than the composed [`SupervisorSpec`]
3061    /// altitude the sibling [`crate::supervisor::SupervisorSpec::estrategia`]
3062    /// (eafb619) accessor keys off. The two typed axes — the outer
3063    /// author-surface `Option<RestartStrategy>` on the [`Caixa`] altitude
3064    /// (author-omitted arm carried as `None`) and the inner post-
3065    /// composition `RestartStrategy` on the [`SupervisorSpec`] altitude
3066    /// (`Option` collapsed through the [`Self::supervisor_view`]
3067    /// `unwrap_or_default()` fold) — now share one accessor discipline for
3068    /// the shared substrate concept "the author-declared OTP-shaped
3069    /// sibling-restart-strategy variant that partitions the downstream
3070    /// per-Supervisor renderer's per-arm fan-out"; the outer-altitude
3071    /// `None` arm is the pre-composition presence bit every declared-slot
3072    /// enumerator ([`Self::declared_supervisor_slots`]) reads, and the
3073    /// inner-altitude non-`Option` `RestartStrategy` is the post-
3074    /// composition partition-dispatch input every strategy-arm consumer
3075    /// ([`SupervisorSpec::validate`], the future wasm-operator's per-
3076    /// Supervisor sibling-restart branch, the future M4
3077    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
3078    /// webhook) fans on.
3079    ///
3080    /// Prior to this lift the `.estrategia` field was accessed inline at
3081    /// two production sites in `caixa-core/src/manifest.rs` — the
3082    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`
3083    /// presence-probe arm at `if self.estrategia.is_some()` (which drives
3084    /// the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3085    /// coherence gate's per-slot label push) and the [`Self::supervisor_view`]
3086    /// `SupervisorSpec` construction site at `estrategia:
3087    /// self.estrategia.unwrap_or_default()` (which composes the flat-
3088    /// spread outer author-surface `Option<RestartStrategy>` onto the
3089    /// inner post-composition [`SupervisorSpec`] `RestartStrategy` field
3090    /// the [`SupervisorSpec::estrategia`] accessor keys off) — two open-
3091    /// coded field-accesses that expressed no compile-time link back to
3092    /// the typed slot. A future extension of the outer `:estrategia` axis
3093    /// to a richer author surface (a per-cluster strategy override the
3094    /// operator pins through a future `:estrategia-overrides` overlay the
3095    /// MESH-COMPOSITION §III.2 supervision-canary roadmap acknowledges,
3096    /// a per-tenant strategy-alias table the M4 CR materializer resolves
3097    /// per-CR, a per-Supervisor dynamic strategy derivation the future
3098    /// adaptive-supervision engine computes from child-failure-history
3099    /// topology, a per-child-cohort strategy split the future
3100    /// `RestForCohort` extension the INSPIRATIONS.md §II.2 Erlang/OTP
3101    /// absorption roadmap acknowledges, a promotion of the plain
3102    /// `Option<RestartStrategy>` to a richer
3103    /// `AuthorDeclaredStrategy { declared, overlay }` newtype once the
3104    /// operator-resolved overlay lands) would have had to be threaded
3105    /// through both open-coded copies in lockstep or the enumerator's
3106    /// presence probe and the composition site's `unwrap_or_default()`
3107    /// fold would silently disagree on which strategy a given [`Caixa`]
3108    /// resolves to (an author's `:estrategia OneForAll` would satisfy
3109    /// the enumerator's presence probe while the composition site
3110    /// silently rendered a stale `OneForOne`, or vice versa). Lifting
3111    /// the resolution rule to a typed method on the substrate primitive
3112    /// means every downstream consumer of the caixa's per-`Caixa` outer-
3113    /// altitude sibling-restart-strategy surface reaches for exactly one
3114    /// typed dispatch — the resolver's accept-set migrates as a unit on
3115    /// any future axis addition.
3116    ///
3117    /// First outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3118    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3119    /// axes — opens the outer-`Caixa` `Option<Copy>` flat-spread
3120    /// projection pattern the sibling per-`Caixa` `:max-restarts`
3121    /// `Option<u32>` and (through the future duration-newtype landing)
3122    /// `:restart-window` `Option<Duration>` future outer-scalar lifts
3123    /// fold on. Peer of the inner-altitude [`crate::supervisor::SupervisorSpec::estrategia`]
3124    /// (eafb619) `Copy`-return sibling-restart-strategy scalar accessor on
3125    /// the post-composition [`SupervisorSpec`] altitude — same "one
3126    /// typed dispatch on the substrate primitive, thin projections at
3127    /// each consumer" discipline extended onto the pre-composition outer
3128    /// author-surface [`Caixa`] altitude for the same OTP-shaped
3129    /// sibling-restart-strategy axis. Peer of the closed outer-`Caixa`
3130    /// `Option<&Composite>` composite-reference family the sibling
3131    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3132    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3133    /// [`Self::entrada`] (e4128e4) accessor pins already carry on the
3134    /// outer `Option<&Composite>` altitude — extends the outer-`Caixa`
3135    /// typed-slot accessor discipline onto the flat-spread M2 supervisor-
3136    /// tree `Option<Copy>`-discriminant sub-family the sibling M3
3137    /// [`crate::aplicacao::Placement::estrategia`] (921fe1b)
3138    /// `PlacementStrategy` `Copy`-composite-enum scalar accessor already
3139    /// pins on the inner-altitude per-`:placement` composite. Named
3140    /// `estrategia()` to match the storage field's name and the
3141    /// per-[`SupervisorSpec`] peer [`crate::supervisor::SupervisorSpec::estrategia`]
3142    /// / per-[`crate::aplicacao::Placement`] peer
3143    /// [`crate::aplicacao::Placement::estrategia`] method-name discipline
3144    /// verbatim; the accessor's identity name maps onto the canonical
3145    /// OTP-shape supervision vocabulary the [`RestartStrategy`] enum's
3146    /// docstring already carries.
3147    #[must_use]
3148    pub const fn estrategia(&self) -> Option<crate::supervisor::RestartStrategy> {
3149        self.estrategia
3150    }
3151
3152    /// Substrate-canonical per-`Caixa` `:max-restarts` M2 supervisor-tree-
3153    /// slot flat-spread OTP-`MaxIntensity`-shaped restart-budget-count
3154    /// scalar accessor every consumer of the top-level manifest's per-
3155    /// Supervisor `:max-restarts` restart-budget-count axis keys off —
3156    /// returns the author-declared `:max-restarts` typed `Option<u32>`
3157    /// verbatim, `Copy`-projected from the typed slot's own `Option<u32>`
3158    /// storage (`u32` is `Copy`, so `Option<u32>` is `Copy` and the
3159    /// accessor returns by value; no borrow of `&self` past the call).
3160    /// Optional (`:max-restarts` is a flat-spread supervisor-only slot
3161    /// every non-`Supervisor`-kind `defcaixa` carries as `None` by
3162    /// `#[serde(default)]`, and every `Supervisor`-kind `defcaixa` may
3163    /// still omit to defer to the [`Self::supervisor_view`]
3164    /// `unwrap_or(5)` fold's OTP-canonical `{intensity, 5, 60}` default).
3165    ///
3166    /// The `:max-restarts` slot carries the M2 typed Erlang/OTP-shaped
3167    /// `MaxIntensity` restart-budget count that pairs with the sibling
3168    /// `:restart-window` `Period` to form the `MaxIntensity / Period`
3169    /// restart-intensity ratio the supervisor trips its own escalation on
3170    /// (INSPIRATIONS §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}`
3171    /// worker-supervisor default; RUNTIME-PATTERNS §II.2; CAIXA-SDLC §II
3172    /// — the M2 supervisor-tree slot algebra the operator's hierarchical
3173    /// reconciliation scheduler fans on). The slot is *flat-spread* on
3174    /// the outer top-level `Caixa` (per the field-shape docstring at
3175    /// caixa-core/src/manifest.rs — "The supervisor slots are flat on
3176    /// Caixa (vs nested under a `SupervisorSpec` sub-form)"), so the
3177    /// accessor's altitude is the outer [`Caixa`] surface rather than the
3178    /// composed [`SupervisorSpec`] altitude the sibling
3179    /// [`crate::supervisor::SupervisorSpec::max_restarts`] accessor keys
3180    /// off. The two typed axes — the outer author-surface `Option<u32>`
3181    /// on the [`Caixa`] altitude (author-omitted arm carried as `None`)
3182    /// and the inner post-composition `u32` on the [`SupervisorSpec`]
3183    /// altitude (`Option` collapsed through the [`Self::supervisor_view`]
3184    /// `unwrap_or(5)` fold) — now share one accessor discipline for the
3185    /// shared substrate concept "the author-declared OTP-shaped
3186    /// restart-budget count every downstream per-Supervisor consumer's
3187    /// restart-intensity budget-vs-count comparator fans on".
3188    ///
3189    /// Prior to this lift the `.max_restarts` field was accessed inline
3190    /// at two production sites in `caixa-core/src/manifest.rs` — the
3191    /// [`Self::declared_supervisor_slots`] `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`
3192    /// presence-probe arm at `if self.max_restarts.is_some()` (which
3193    /// drives the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3194    /// kind-coherence gate's per-slot label push) and the
3195    /// [`Self::supervisor_view`] `SupervisorSpec` construction site at
3196    /// `max_restarts: self.max_restarts.unwrap_or(5)` (which composes the
3197    /// flat-spread outer author-surface `Option<u32>` onto the inner
3198    /// post-composition [`SupervisorSpec`] `u32` field the
3199    /// [`SupervisorSpec::max_restarts`] accessor keys off) — two open-
3200    /// coded field-accesses that expressed no compile-time link back to
3201    /// the typed slot. A future extension of the outer `:max-restarts`
3202    /// axis to a richer author surface (a per-cluster restart-budget
3203    /// override the operator pins through a future `:max-restarts-overrides`
3204    /// overlay the MESH-COMPOSITION §III.2 supervision-canary roadmap
3205    /// acknowledges, a per-tenant restart-budget-alias table the M4 CR
3206    /// materializer resolves per-CR, a per-Supervisor dynamic restart-
3207    /// budget derivation the future adaptive-supervision engine computes
3208    /// from child-failure-history topology, a promotion of the plain
3209    /// `Option<u32>` count to a richer `{MaxR, MaxT}` per-child-cohort
3210    /// restart-budget-partition once the INSPIRATIONS §II.2 Erlang/OTP
3211    /// per-child-cohort roadmap lands) would have had to be threaded
3212    /// through both open-coded copies in lockstep or the enumerator's
3213    /// presence probe and the composition site's `unwrap_or(5)` fold
3214    /// would silently disagree on which restart-budget a given [`Caixa`]
3215    /// resolves to (an author's `:max-restarts 10` would satisfy the
3216    /// enumerator's presence probe while the composition site silently
3217    /// composed the OTP-canonical `5`, or vice versa). Lifting the
3218    /// resolution rule to a typed method on the substrate primitive means
3219    /// every downstream consumer of the caixa's per-`Caixa` outer-altitude
3220    /// restart-budget-count surface reaches for exactly one typed dispatch
3221    /// — the resolver's accept-set migrates as a unit on any future axis
3222    /// addition.
3223    ///
3224    /// Second outer top-level [`Caixa`] `Option<Copy>`-return supervisor-
3225    /// tree-slot flat-spread accessor for M2 supervisor-slot Copy-carry
3226    /// axes — folds on the outer-`Caixa` `Option<Copy>` flat-spread
3227    /// projection pattern the sibling per-`Caixa`
3228    /// [`Self::estrategia`] (ed04d3c) accessor opened, extends the
3229    /// sub-family onto the sibling `Option<u32>` restart-budget-count arm.
3230    /// Peer of the inner-altitude
3231    /// [`crate::supervisor::SupervisorSpec::max_restarts`] `u32` accessor
3232    /// on the post-composition [`SupervisorSpec`] altitude — same "one
3233    /// typed dispatch on the substrate primitive, thin projections at
3234    /// each consumer" discipline extended onto the pre-composition outer
3235    /// author-surface [`Caixa`] altitude for the same OTP-`MaxIntensity`-
3236    /// shaped restart-budget-count axis. Named `max_restarts()` to match
3237    /// the storage field's name and the per-[`SupervisorSpec`] peer
3238    /// [`crate::supervisor::SupervisorSpec::max_restarts`] method-name
3239    /// discipline verbatim; the accessor's identity maps onto the
3240    /// canonical OTP-shape supervision vocabulary the `:max-restarts`
3241    /// field's docstring already carries.
3242    #[must_use]
3243    pub const fn max_restarts(&self) -> Option<u32> {
3244        self.max_restarts
3245    }
3246
3247    /// Substrate-canonical per-`Caixa` `:restart-window` M2 supervisor-
3248    /// tree-slot flat-spread OTP-`Period`-shaped restart-intensity-
3249    /// denominator raw-duration-string scalar accessor every consumer of
3250    /// the top-level manifest's per-Supervisor `:restart-window` sliding-
3251    /// window axis keys off — returns the author-declared `:restart-window`
3252    /// typed `Option<String>` verbatim as an `Option<&str>`, borrowed
3253    /// from the typed slot's own `Option<String>` storage. `None` when
3254    /// the slot is absent (the canonical "never reset — every restart
3255    /// across the supervisor's lifetime counts against the sibling
3256    /// `:max-restarts` budget" sentinel every non-`Supervisor`-kind
3257    /// `defcaixa` carries by `#[serde(default)]` and every
3258    /// `Supervisor`-kind `defcaixa` may still omit to defer to the
3259    /// [`Self::supervisor_view`] `restart_window: None` composition
3260    /// through the [`crate::supervisor::duration_codec::parse`] soft-
3261    /// swallow `.and_then(|s| … .ok())` fold).
3262    ///
3263    /// The `:restart-window` slot carries the raw M2 typed Erlang/OTP-
3264    /// shaped `Period` sliding-observation-interval duration string that
3265    /// pairs with the sibling `:max-restarts` `MaxIntensity` restart-
3266    /// budget count to form the `MaxIntensity / Period` restart-intensity
3267    /// ratio the supervisor trips its own escalation on (INSPIRATIONS
3268    /// §II.2 — Erlang/OTP `supervisor` `{intensity, 5, 60}` worker-
3269    /// supervisor default; RUNTIME-PATTERNS §II.2). The outer-`Caixa`
3270    /// slot stores the raw duration string (`"60s"`, `"5m"`, `"500ms"`)
3271    /// authored under `:restart-window` — the typed [`SupervisorSpec`]
3272    /// holds an `Option<Duration>` routed through the shared
3273    /// [`crate::supervisor::duration_codec`] via `with = "duration_codec"`
3274    /// — so the outer altitude's accessor returns `Option<&str>` (raw
3275    /// authoring surface) while the inner altitude's
3276    /// [`crate::supervisor::SupervisorSpec::restart_window`] returns
3277    /// `Option<Duration>` (parsed typed surface). The parse-refusal arm
3278    /// is closed by the sibling [`Self::validate_restart_window`] gate
3279    /// that surfaces [`ManifestError::RestartWindowMalformed`] naming
3280    /// the offending value; the view-construction path
3281    /// [`Self::supervisor_view`] soft-swallows the same parse error to
3282    /// `None` to keep the view best-effort.
3283    ///
3284    /// Prior to this lift the `.restart_window` field was accessed inline
3285    /// at three production sites in `caixa-core/src/manifest.rs` — the
3286    /// [`Self::declared_supervisor_slots`]
3287    /// `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` presence-probe arm at
3288    /// `if self.restart_window.is_some()` (which drives the
3289    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
3290    /// coherence gate's per-slot label push), the
3291    /// [`Self::validate_restart_window`] `let Some(s) =
3292    /// self.restart_window.as_deref()` empty-and-shape gate binding
3293    /// (which folds the raw string through the shared
3294    /// [`crate::supervisor::duration_codec::parse`] to surface
3295    /// [`ManifestError::RestartWindowMalformed`] naming the offending
3296    /// value), and the [`Self::supervisor_view`] `self.restart_window
3297    /// .as_deref().and_then(…)` view-construction fold (which composes
3298    /// the flat-spread outer author-surface `Option<String>` onto the
3299    /// inner post-composition [`SupervisorSpec`] `Option<Duration>`
3300    /// field the [`SupervisorSpec::restart_window`] accessor keys off) —
3301    /// three open-coded field-accesses that expressed no compile-time
3302    /// link back to the typed slot. A future extension of the outer
3303    /// `:restart-window` axis to a richer author surface (a per-cluster
3304    /// window override, a per-tenant window-alias table, a per-Supervisor
3305    /// dynamic window derivation the future adaptive-supervision engine
3306    /// computes from child-failure-history topology, a promotion of the
3307    /// plain `Option<String>` raw duration to a typed `Option<Duration>`
3308    /// once the future author-surface parser lands at the [`Caixa`]
3309    /// altitude and the raw-string form is retired) would have had to be
3310    /// threaded through every open-coded copy in lockstep or the three
3311    /// consumers would silently disagree on which raw string a given
3312    /// [`Caixa`] resolves to. Lifting the resolution rule to a typed
3313    /// method on the substrate primitive means every downstream consumer
3314    /// of the caixa's per-`Caixa` outer-altitude restart-window raw-
3315    /// string surface reaches for exactly one typed dispatch — the
3316    /// resolver's accept-set migrates as a unit on any future axis
3317    /// addition.
3318    ///
3319    /// Third outer top-level [`Caixa`] supervisor-tree-slot flat-spread
3320    /// accessor — folds on the outer-`Caixa` M2 supervisor-tree flat-
3321    /// spread projection pattern the sibling per-`Caixa`
3322    /// [`Self::estrategia`] (ed04d3c) `Option<Copy>` and
3323    /// [`Self::max_restarts`] `Option<Copy>` accessors opened, extends
3324    /// the sub-family onto the sibling `Option<&str>` raw-duration-
3325    /// string arm (the outer altitude's raw-string form; the inner
3326    /// altitude's parsed [`Duration`] form is the peer
3327    /// [`crate::supervisor::SupervisorSpec::restart_window`] accessor).
3328    /// Peer of the sibling per-`Caixa` `Option<&str>`-return scalar
3329    /// accessors ([`Self::licenca`] / [`Self::repositorio`] /
3330    /// [`Self::descricao`] / [`Self::edicao`]) on the universal-axis
3331    /// outer scalar-projection family the outer-`Caixa` `Option<&str>`
3332    /// sub-family already carries — same "one typed dispatch on the
3333    /// substrate primitive, thin projections at each consumer"
3334    /// discipline extended onto the M2 supervisor-tree flat-spread
3335    /// `Option<&str>` raw-duration-string arm. Named `restart_window()`
3336    /// to match the storage field's name and the per-[`SupervisorSpec`]
3337    /// peer [`crate::supervisor::SupervisorSpec::restart_window`]
3338    /// method-name discipline verbatim; the accessor's identity maps
3339    /// onto the canonical OTP-shape supervision vocabulary the
3340    /// `:restart-window` field's docstring already carries.
3341    #[must_use]
3342    pub const fn restart_window(&self) -> Option<&str> {
3343        match &self.restart_window {
3344            Some(s) => Some(s.as_str()),
3345            None => None,
3346        }
3347    }
3348
3349    /// Substrate-canonical per-`Caixa` `:upgrade-from` M2 typed-slot
3350    /// outer-composite OTP-appup-shaped per-prior-version migration-
3351    /// entry-list slice accessor every consumer of the top-level
3352    /// manifest's per-Servico hot-upgrade-block `&[UpgradeFromEntry]`
3353    /// slice-view keys off — returns the author-declared `:upgrade-from`
3354    /// typed `Vec<UpgradeFromEntry>` verbatim as a
3355    /// `&[UpgradeFromEntry]` slice-view over the same backing buffer
3356    /// the raw `self.upgrade_from.as_slice()` field access borrows
3357    /// from. Empty-slice-carrying (the "no hot-upgrade path declared"
3358    /// arm every `defcaixa` without an `:upgrade-from` block carries;
3359    /// the [`Self::from_lisp`] derive folds an omitted `:upgrade-from`
3360    /// through `#[serde(default)]` to `Vec::new()`, so a `Caixa` past
3361    /// parse definitionally carries a `Vec<UpgradeFromEntry>` slot —
3362    /// possibly empty — and the returned `&[UpgradeFromEntry]`
3363    /// degenerates to an empty slice on that arm without any silent
3364    /// `None` collapse).
3365    ///
3366    /// The outer `:upgrade-from` slot carries the M2 typed OTP-appup
3367    /// migration block — the load-bearing container of every per-
3368    /// prior-`:versao` migration-instruction list the wasm-operator
3369    /// dispatches on at hot-upgrade time (INSPIRATIONS §II.4 — OTP
3370    /// `.appup` per-prior-version `LoadModule | StateChange |
3371    /// SoftPurge | Purge | Restart` instruction algebra translated
3372    /// onto pleme-io's typed `:upgrade-from :from` + `:instructions`
3373    /// entry list; CAIXA-SDLC §II — the typed-M2 slot algebra the
3374    /// operator's hot-upgrade dispatch fans on). Every per-entry axis
3375    /// threads through a lifted per-entry accessor on the
3376    /// [`UpgradeFromEntry`] type: the
3377    /// [`UpgradeFromEntry::prior_versao`] SemVer-shaped previous-
3378    /// version scalar accessor and the
3379    /// [`UpgradeFromEntry::instructions`] `&[UpgradeInstruction]`-
3380    /// return per-entry instruction-list accessor (0137e5a). Every
3381    /// downstream consumer of the hot-upgrade path first passes
3382    /// through this outer accessor onto the slice and then dispatches
3383    /// per-entry through the inner accessors — the two-level dispatch
3384    /// means every per-`:upgrade-from` reader now routes through a
3385    /// typed dispatch on the substrate primitive at both altitudes.
3386    ///
3387    /// Prior to this lift the `.upgrade_from` `Vec<UpgradeFromEntry>`
3388    /// slot was accessed inline at production sites across three
3389    /// files — the [`Self::declared_servico_slots`] M2 declared-slot
3390    /// enumerator's `self.upgrade_from.is_empty()` presence probe
3391    /// (caixa-core/src/manifest.rs, which drives the
3392    /// `M2_AUTHOR_KEY_UPGRADE_FROM` kebab-case author-label push every
3393    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
3394    /// gate reads), the [`crate::StandardLayout::verify`] per-
3395    /// `:upgrade-from` three-stage validation pass (caixa-core/src/
3396    /// layout.rs, which fans onto the
3397    /// [`crate::upgrade::validate_upgrade_from`] per-entry shape +
3398    /// cross-entry duplicate gate, the
3399    /// [`crate::upgrade::validate_upgrade_from_against_versao`]
3400    /// SemVer-precedence cross-slot gate, the
3401    /// [`crate::upgrade::validate_upgrade_from_against_behavior`]
3402    /// `:state-change` ↔ `:on-state-change` cross-slot composition
3403    /// gate, and the per-instruction script-path existence-probe walk
3404    /// that reads each entry's [`UpgradeFromEntry::instructions`] to
3405    /// resolve every declared migration script against the layout
3406    /// root), and the [`crate::render::servico_m2_overlay`] per-
3407    /// Servico M2 overlay emitter's `!caixa.upgrade_from.is_empty()`
3408    /// presence gate + `serde_yaml::to_value(&caixa.upgrade_from)`
3409    /// projection (caixa-core/src/render.rs, which drives the
3410    /// `M2_KEY_UPGRADE_FROM`-keyed `serde_yaml` projection every
3411    /// `caixa-helm` / `caixa-flux` Servico values-block emitter fans
3412    /// on and lands as the ComputeUnit CR's `spec.upgradeFrom` field).
3413    /// A future extension of the outer `:upgrade-from` axis (a per-
3414    /// cluster `:upgrade-overrides` overlay the wasm-engine operator
3415    /// resolves at admission time so a cluster-specific migration
3416    /// policy can tighten a caixa-declared step without re-authoring
3417    /// the `caixa.lisp`, promotion of the plain
3418    /// `Vec<UpgradeFromEntry>` to a richer `{static, dynamic}`
3419    /// partition once runtime-resolved hot-upgrade instructions land,
3420    /// per-entry priority annotation once multi-strategy fan-out
3421    /// lands) would have had to be threaded through all six open-
3422    /// coded copies in lockstep or one consumer would silently
3423    /// disagree with the peers on which upgrade slice a given Caixa
3424    /// resolves to — a six-consumer split at the enumerator, the
3425    /// three-stage validate pass, the script-path probe walk, and the
3426    /// M2 overlay emitter, far from the source `caixa.lisp` with no
3427    /// field naming the upgrade-drift root cause. Lifting the
3428    /// resolution rule to a typed method on the substrate primitive
3429    /// means every downstream consumer of the caixa's per-`Caixa`
3430    /// OTP-appup outer-slice surface reaches for exactly one typed
3431    /// dispatch — the resolver's accept-set migrates as a unit on any
3432    /// future axis addition.
3433    ///
3434    /// First outer top-level [`Caixa`] `&[Composite]`-return slice
3435    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the
3436    /// outer-`Caixa` `&[Composite]` composite-slice projection
3437    /// pattern the sibling `:children`
3438    /// [`crate::supervisor::ChildSpec`] / `:membros`
3439    /// [`crate::aplicacao::Membro`] / `:contratos`
3440    /// [`crate::aplicacao::WitContract`] future outer-composite-slice
3441    /// lifts fold on. Peer of the closed outer-`Caixa` scalar
3442    /// `Option<&Composite>` composite-reference family the sibling
3443    /// [`Self::limits`] (b2bd9d7) / [`Self::behavior`] (35d8b52) /
3444    /// [`Self::politicas`] (5d23d29) / [`Self::placement`] (4fb8074) /
3445    /// [`Self::entrada`] (e4128e4) accessors closed on the outer
3446    /// `Option<&Composite>` altitude, extended here to the outer-
3447    /// `Caixa` `&[Composite]` vec-carry altitude. Peer at the inner
3448    /// altitude of [`crate::upgrade::UpgradeFromEntry::instructions`]
3449    /// (0137e5a) — same "one typed dispatch on the substrate
3450    /// primitive, thin projections at each consumer" discipline
3451    /// folded onto the outer top-level [`Caixa`] altitude, opening the
3452    /// M2 vec-carry slot family's outer-composite-slice axis. Sibling
3453    /// in shape to the peer outer-`Caixa` `&[Dep]`-return
3454    /// [`Self::deps`] (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and
3455    /// `&[String]`-return [`Self::autores`] (b5d813f) /
3456    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`]
3457    /// (8a36c23) / [`Self::exe`] (65d9527) / [`Self::servicos`]
3458    /// (611f78b) slice-accessors on the sibling outer-`Caixa` scalar-
3459    /// element vec-carry axes — folds the "outer [`Caixa`] `&[T]`
3460    /// slice" projection pattern onto the sibling M2 typed-composite-
3461    /// element axis (`UpgradeFromEntry` composite, matching the
3462    /// per-inner [`UpgradeFromEntry::instructions`] element type at a
3463    /// different altitude).
3464    ///
3465    /// Returns `&[UpgradeFromEntry]` (not `&Vec<UpgradeFromEntry>`)
3466    /// because every downstream consumer of the hot-upgrade list
3467    /// treats it as a read-only sequence — the slice-view is the
3468    /// narrowest borrow that supports every present + roadmapped
3469    /// consumer (`.iter()`, `.len()`, `.is_empty()`, `serde` slice-
3470    /// serialization through
3471    /// `serde_yaml::to_value(&[UpgradeFromEntry])`) without leaking
3472    /// the backing `Vec`'s grow/push/reserve surface no consumer of
3473    /// the typed view reaches for (the storage-side `Vec` remains
3474    /// reachable through the `pub upgrade_from` field for the
3475    /// mutation-carrying serde round-trip and per-test fixture-
3476    /// mutation paths). Named `upgrade_from()` to match the storage
3477    /// field's `snake_case` name; the kebab-case author-surface tag
3478    /// `:upgrade-from` is the same axis after tatara-lisp's
3479    /// kebab↔snake fold and the accessor's identity maps onto the
3480    /// canonical CAIXA-SDLC §II vocabulary the slot's docstring
3481    /// already carries.
3482    #[must_use]
3483    pub const fn upgrade_from(&self) -> &[UpgradeFromEntry] {
3484        self.upgrade_from.as_slice()
3485    }
3486
3487    /// Substrate-canonical per-`Caixa` `:children` M2 supervisor-tree-
3488    /// slot outer-composite OTP-shaped per-supervisor static-child-list
3489    /// slice accessor every consumer of the top-level manifest's per-
3490    /// Supervisor `&[ChildSpec]` slice-view keys off — returns the
3491    /// author-declared `:children` typed `Vec<crate::supervisor::ChildSpec>`
3492    /// verbatim as a `&[crate::supervisor::ChildSpec]` slice-view over
3493    /// the same backing buffer the raw `self.children.as_slice()` field
3494    /// access borrows from. Empty-slice-carrying (the "no static children
3495    /// declared" arm every non-`Supervisor`-kind `defcaixa` carries by
3496    /// #[serde(default)] and every `SimpleOneForOne` supervisor carries
3497    /// by [`crate::supervisor::SupervisorError::SimpleOneForOneWithStaticChildren`]
3498    /// gate; the returned `&[ChildSpec]` degenerates to an empty slice
3499    /// on those arms without any silent `None` collapse).
3500    ///
3501    /// The outer `:children` slot carries the M2 typed OTP-supervisor
3502    /// static-child list — the load-bearing container of every per-
3503    /// child `{caixa, versao, restart}` triple the wasm-operator's
3504    /// hierarchical reconciler dispatches on at supervisor-tree
3505    /// materialization time (INSPIRATIONS §II.2 — OTP `supervisor:init/1`
3506    /// static-child list translated onto pleme-io's typed
3507    /// [`crate::supervisor::ChildSpec`] entry list; CAIXA-SDLC §II —
3508    /// the typed-M2 slot algebra the operator's per-supervisor fan-out
3509    /// dispatch fans on). Every per-child axis threads through a lifted
3510    /// per-entry accessor on the [`crate::supervisor::ChildSpec`] type:
3511    /// the [`crate::supervisor::ChildSpec::nome`] DNS-1123-label
3512    /// child-caixa-identity scalar accessor, the peer versao SemVer-2
3513    /// version-requirement scalar accessor, and the
3514    /// [`crate::supervisor::ChildSpec::restart`] `Copy`-composite-enum
3515    /// per-child post-exit restart-decision-policy discriminant
3516    /// accessor (dfb4a81). Every downstream consumer of the supervisor-
3517    /// tree path first passes through this outer accessor onto the
3518    /// slice and then dispatches per-child through the inner accessors
3519    /// — the two-level dispatch means every per-`:children` reader now
3520    /// routes through a typed dispatch on the substrate primitive at
3521    /// both altitudes.
3522    ///
3523    /// Prior to this lift the `.children` `Vec<ChildSpec>` slot was
3524    /// accessed inline at three production sites across two files —
3525    /// the [`Self::declared_supervisor_slots`] supervisor-tree
3526    /// declared-slot enumerator's `!self.children.is_empty()` presence
3527    /// probe (caixa-core/src/manifest.rs, which drives the
3528    /// `SUPERVISOR_AUTHOR_KEY_CHILDREN` kebab-case author-label push
3529    /// every [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
3530    /// kind-coherence gate reads), the [`Self::supervisor_view`]
3531    /// per-supervisor typed-view composer's `self.children.clone()`
3532    /// per-child fold-in path (caixa-core/src/manifest.rs, which
3533    /// materializes the typed [`crate::supervisor::SupervisorSpec`]
3534    /// view every [`crate::StandardLayout::verify`] Supervisor-arm gate
3535    /// dispatches on), and the [`crate::StandardLayout::verify`] per-
3536    /// `:children :caixa` self-parent refusal probe's
3537    /// `&caixa.children`-borrowed
3538    /// [`crate::supervisor::validate_no_self_supervision`] input
3539    /// (caixa-core/src/layout.rs, which pins the "no child names the
3540    /// supervisor's own `:nome`" cross-slot coherence gate). A future
3541    /// extension of the outer `:children` axis (a per-cluster
3542    /// `:children-overrides` overlay the wasm-engine operator resolves
3543    /// at admission time so a cluster-specific child-set can tighten
3544    /// a caixa-declared list without re-authoring the `caixa.lisp`,
3545    /// promotion of the plain `Vec<ChildSpec>` to a richer
3546    /// `{static, dynamic}` partition once Erlang/OTP's
3547    /// `simple_one_for_one`-shaped dynamic-child slot lands as a typed
3548    /// axis, per-child priority annotation once multi-strategy fan-out
3549    /// lands) would have had to be threaded through all three open-
3550    /// coded copies in lockstep or one consumer would silently
3551    /// disagree with the peers on which child slice a given Caixa
3552    /// resolves to — the enumerator's presence probe reading the raw
3553    /// slot while the peer view-composer's fold-in path read an
3554    /// operator-resolved slot would silently split the paired
3555    /// declared-slot enumerator and typed-view composition, and the
3556    /// [`crate::supervisor::validate_no_self_supervision`] self-parent
3557    /// refusal probe reading a third borrow would silently drift the
3558    /// cross-slot coherence gate's traversal input from the two peers,
3559    /// a three-consumer split at the enumerator, the view composer,
3560    /// and the self-parent gate far from the source `caixa.lisp` with
3561    /// no field naming the child-set-drift root cause. Lifting the
3562    /// resolution rule to a typed method on the substrate primitive
3563    /// means every downstream consumer of the caixa's per-`Caixa`
3564    /// OTP-supervisor outer-slice surface reaches for exactly one
3565    /// typed dispatch — the resolver's accept-set migrates as a unit
3566    /// on any future axis addition.
3567    ///
3568    /// Second outer top-level [`Caixa`] `&[Composite]`-return slice
3569    /// accessor for M2 / M3 typed-slot vec-carry axes — folds on the
3570    /// outer-`Caixa` `&[Composite]` composite-slice sub-family the
3571    /// sibling [`Self::upgrade_from`] (2a1f907) accessor opened, peer
3572    /// at the outer altitude of the closed inner-`SupervisorSpec`
3573    /// [`crate::SupervisorSpec::children`] (bc92bce) accessor on the
3574    /// same OTP-supervisor static-child-list axis — same "byte-equal,
3575    /// borrow-shared" outer-accessor discipline extended onto the
3576    /// second outer-`Caixa` `&[Composite]` vec-carry axis. Sibling in
3577    /// shape to the peer outer-`Caixa` `&[Dep]`-return [`Self::deps`]
3578    /// (ad34b4e) / [`Self::deps_dev`] (f7fd81e) and `&[String]`-return
3579    /// [`Self::autores`] (b5d813f) / [`Self::etiquetas`] (78c7d3c) /
3580    /// [`Self::bibliotecas`] (8a36c23) / [`Self::exe`] (65d9527) /
3581    /// [`Self::servicos`] (611f78b) slice-accessors on the sibling
3582    /// outer-`Caixa` scalar-element vec-carry axes — folds the "outer
3583    /// [`Caixa`] `&[T]` slice" projection pattern onto the sibling
3584    /// M2 typed-composite-element axis
3585    /// ([`crate::supervisor::ChildSpec`] composite, matching the
3586    /// per-inner [`crate::SupervisorSpec::children`] element type at a
3587    /// different altitude).
3588    ///
3589    /// Returns `&[crate::supervisor::ChildSpec]` (not
3590    /// `&Vec<ChildSpec>`) because every downstream consumer of the
3591    /// child list treats it as a read-only sequence — the slice-view
3592    /// is the narrowest borrow that supports every present +
3593    /// roadmapped consumer (`.iter()`, `.len()`, `.is_empty()`, the
3594    /// [`crate::supervisor::validate_no_self_supervision`] `&[ChildSpec]`
3595    /// input, `serde` slice-serialization) without leaking the backing
3596    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3597    /// reaches for (the storage-side `Vec` remains reachable through
3598    /// the `pub children` field for the mutation-carrying serde round-
3599    /// trip and per-test fixture-mutation paths, including the
3600    /// [`Self::supervisor_view`] fold-in path that clones the slot
3601    /// into the typed view). Named `children()` to match the storage
3602    /// field's name verbatim and the tatara-lisp author-surface term
3603    /// (`:children`) the field's own docstring already carries; the
3604    /// accessor's identity maps onto the canonical OTP supervision
3605    /// vocabulary the [`Caixa::children`] field's docstring already
3606    /// reaches for ("Static children of a supervisor").
3607    #[must_use]
3608    pub const fn children(&self) -> &[crate::supervisor::ChildSpec] {
3609        self.children.as_slice()
3610    }
3611
3612    /// Substrate-canonical per-`Caixa` `:membros` M3 mesh-slot outer-
3613    /// composite MESH-COMPOSITION-shaped per-Aplicacao member-list slice
3614    /// accessor every consumer of the top-level manifest's per-Aplicacao
3615    /// `&[crate::aplicacao::Membro]` slice-view keys off — returns the
3616    /// author-declared `:membros` typed `Vec<crate::aplicacao::Membro>`
3617    /// verbatim as a `&[crate::aplicacao::Membro]` slice-view over the
3618    /// same backing buffer the raw `self.membros.as_slice()` field access
3619    /// borrows from. Empty-slice-carrying (the "no members declared" arm
3620    /// every non-`Aplicacao`-kind `defcaixa` carries by `#[serde(default)]`
3621    /// and every partially-authored Aplicacao carries before the
3622    /// [`crate::AplicacaoError::MembrosEmpty`] gate fires; the returned
3623    /// `&[Membro]` degenerates to an empty slice on those arms without any
3624    /// silent `None` collapse).
3625    ///
3626    /// The outer `:membros` slot carries the M3 typed MESH-COMPOSITION
3627    /// per-Aplicacao member list — the load-bearing container of every
3628    /// per-member `{caixa, versao}` pair the caixa-mesh renderer's
3629    /// per-Aplicacao program-emission dispatch fans on at mesh-artifact
3630    /// materialization time (MESH-COMPOSITION §III.1 — the typed graph's
3631    /// vertex set the `:contratos` `:de`/`:para` edges resolve against and
3632    /// the `:entrada :para` external-gateway destination validates
3633    /// against; CAIXA-SDLC §II — the typed-M3 slot algebra the operator's
3634    /// per-Aplicacao fan-out dispatch fans on). Every per-member axis
3635    /// threads through a lifted per-entry accessor on the
3636    /// [`crate::aplicacao::Membro`] type: the
3637    /// [`crate::aplicacao::Membro::nome`] DNS-1123-label member-caixa-
3638    /// identity scalar accessor (4a32abf) and the peer
3639    /// [`crate::aplicacao::Membro::versao_requirement`] SemVer-2
3640    /// version-requirement scalar accessor (a40b0e3). Every downstream
3641    /// consumer of the mesh-graph path first passes through this outer
3642    /// accessor onto the slice and then dispatches per-member through
3643    /// the inner accessors — the two-level dispatch means every per-
3644    /// `:membros` reader now routes through a typed dispatch on the
3645    /// substrate primitive at both altitudes.
3646    ///
3647    /// Prior to this lift the `.membros` `Vec<Membro>` slot was accessed
3648    /// inline at three production sites across two files — the
3649    /// [`Self::declared_mesh_slots`] mesh-slot declared-slot
3650    /// enumerator's `!self.membros.is_empty()` presence probe
3651    /// (caixa-core/src/manifest.rs, which drives the
3652    /// `M3_AUTHOR_KEY_MEMBROS` kebab-case author-label push every
3653    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3654    /// gate reads), the [`Self::aplicacao_view`] per-Aplicacao typed-view
3655    /// composer's `self.membros.clone()` per-member fold-in path
3656    /// (caixa-core/src/manifest.rs, which materializes the typed
3657    /// [`crate::aplicacao::AplicacaoSpec`] view every
3658    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate dispatches
3659    /// on), and the [`crate::StandardLayout::verify`] per-`:membros
3660    /// :caixa` self-membership refusal probe's `&caixa.membros`-borrowed
3661    /// [`crate::aplicacao::validate_no_self_membership`] input
3662    /// (caixa-core/src/layout.rs, which pins the "no member names the
3663    /// Aplicacao's own `:nome`" cross-slot coherence gate). A future
3664    /// extension of the outer `:membros` axis (a per-cluster
3665    /// `:membros-overrides` overlay the wasm-engine operator resolves at
3666    /// admission time so a cluster-specific member-set can tighten a
3667    /// caixa-declared list without re-authoring the `caixa.lisp`,
3668    /// promotion of the plain `Vec<Membro>` to a richer
3669    /// `{static, dynamic}` partition once runtime-resolved Aplicacao
3670    /// members land as a typed axis, per-member priority annotation once
3671    /// multi-strategy fan-out lands) would have had to be threaded
3672    /// through all three open-coded copies in lockstep or one consumer
3673    /// would silently disagree with the peers on which member slice a
3674    /// given Caixa resolves to — the enumerator's presence probe reading
3675    /// the raw slot while the peer view-composer's fold-in path read an
3676    /// operator-resolved slot would silently split the paired
3677    /// declared-slot enumerator and typed-view composition, and the
3678    /// [`crate::aplicacao::validate_no_self_membership`] self-membership
3679    /// refusal probe reading a third borrow would silently drift the
3680    /// cross-slot coherence gate's traversal input from the two peers, a
3681    /// three-consumer split at the enumerator, the view composer, and
3682    /// the self-membership gate far from the source `caixa.lisp` with no
3683    /// field naming the member-set-drift root cause. Lifting the
3684    /// resolution rule to a typed method on the substrate primitive
3685    /// means every downstream consumer of the caixa's per-`Caixa`
3686    /// MESH-COMPOSITION outer-slice surface reaches for exactly one
3687    /// typed dispatch — the resolver's accept-set migrates as a unit on
3688    /// any future axis addition.
3689    ///
3690    /// Third outer top-level [`Caixa`] `&[Composite]`-return slice
3691    /// accessor for M2 / M3 typed-slot vec-carry axes — opens the outer-
3692    /// `Caixa` M3 mesh-slot arm of the `&[Composite]` composite-slice
3693    /// sub-family the sibling M2 [`Self::upgrade_from`] (2a1f907) /
3694    /// [`Self::children`] (c17b51e) accessors opened for the M2 vec-carry
3695    /// altitude. Peer at the outer altitude of the closed inner-
3696    /// [`crate::AplicacaoSpec::membros`] (6c77e36) accessor on the same
3697    /// MESH-COMPOSITION per-Aplicacao member-list axis — the two
3698    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3699    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3700    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3701    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3702    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3703    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3704    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3705    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3706    /// pattern onto the sibling M3 typed-composite-element axis
3707    /// ([`crate::aplicacao::Membro`] composite, matching the per-inner
3708    /// [`crate::AplicacaoSpec::membros`] element type at a different
3709    /// altitude).
3710    ///
3711    /// Returns `&[crate::aplicacao::Membro]` (not `&Vec<Membro>`)
3712    /// because every downstream consumer of the member list treats it
3713    /// as a read-only sequence — the slice-view is the narrowest borrow
3714    /// that supports every present + roadmapped consumer (`.iter()`,
3715    /// `.len()`, `.is_empty()`, the
3716    /// [`crate::aplicacao::validate_no_self_membership`] `&[Membro]`
3717    /// input, `serde` slice-serialization) without leaking the backing
3718    /// `Vec`'s grow/push/reserve surface no consumer of the typed view
3719    /// reaches for (the storage-side `Vec` remains reachable through the
3720    /// `pub membros` field for the mutation-carrying serde round-trip
3721    /// and per-test fixture-mutation paths, including the
3722    /// [`Self::aplicacao_view`] fold-in path that clones the slot into
3723    /// the typed view). Named `membros()` to match the storage field's
3724    /// name verbatim and the tatara-lisp author-surface term
3725    /// (`:membros`) the field's own docstring already carries; the
3726    /// accessor's identity maps onto the canonical MESH-COMPOSITION
3727    /// vocabulary the [`Caixa::membros`] field's docstring already
3728    /// reaches for ("Member Servicos that make up this Aplicacao").
3729    #[must_use]
3730    pub const fn membros(&self) -> &[crate::aplicacao::Membro] {
3731        self.membros.as_slice()
3732    }
3733
3734    /// Substrate-canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
3735    /// composite MESH-COMPOSITION-shaped per-Aplicacao WIT-typed
3736    /// inter-Servico contract-list slice accessor every consumer of the
3737    /// top-level manifest's per-Aplicacao `&[crate::aplicacao::WitContract]`
3738    /// slice-view keys off — returns the author-declared `:contratos`
3739    /// typed `Vec<crate::aplicacao::WitContract>` verbatim as a
3740    /// `&[crate::aplicacao::WitContract]` slice-view over the same
3741    /// backing buffer the raw `self.contratos.as_slice()` field access
3742    /// borrows from. Empty-slice-carrying (the "no contracts declared"
3743    /// arm every non-`Aplicacao`-kind `defcaixa` carries by
3744    /// `#[serde(default)]` and every leaf Aplicacao carrying only a
3745    /// single member with no inter-Servico edge carries; the returned
3746    /// `&[WitContract]` degenerates to an empty slice on those arms
3747    /// without any silent `None` collapse).
3748    ///
3749    /// The outer `:contratos` slot carries the M3 typed MESH-COMPOSITION
3750    /// per-Aplicacao WIT-typed inter-Servico edge list — the load-bearing
3751    /// container of every per-edge `{de, para, wit, endpoint | subject |
3752    /// slot}` quadruple the caixa-mesh renderer's per-Aplicacao
3753    /// `CiliumNetworkPolicy` fan-out (one L7 policy per edge —
3754    /// MESH-COMPOSITION §III.2 point 2) and per-`(:de, :para)`
3755    /// adjacency-list seed dispatch on at mesh-artifact materialization
3756    /// time (MESH-COMPOSITION §III.1 — the typed graph's edge set the
3757    /// `:membros` vertex set resolves against, closed by the
3758    /// [`crate::AplicacaoError::ContractoUnknownMember`] / cycle-refusal
3759    /// gates in §III.3; CAIXA-SDLC §II — the typed-M3 slot algebra the
3760    /// operator's per-Aplicacao fan-out dispatch fans on). Every
3761    /// per-edge axis threads through a lifted per-entry accessor on the
3762    /// [`crate::aplicacao::WitContract`] type: the peer `de` / `para`
3763    /// DNS-1123-label member-caixa-name endpoint scalar accessors, the
3764    /// [`crate::aplicacao::WitContract::endpoint`] (7020470) HTTP-shape
3765    /// / [`crate::aplicacao::WitContract::subject`] (90de675)
3766    /// NATS-pub-sub-shape / [`crate::aplicacao::WitContract::slot`]
3767    /// (ed22b66) `wasi:keyvalue/store`-shape payload-carrier accessors,
3768    /// and the WIT-world discriminant. Every downstream consumer of the
3769    /// mesh-graph edge path first passes through this outer accessor
3770    /// onto the slice and then dispatches per-contract through the
3771    /// inner accessors — the two-level dispatch means every
3772    /// per-`:contratos` reader now routes through a typed dispatch on
3773    /// the substrate primitive at both altitudes.
3774    ///
3775    /// Prior to this lift the `.contratos` `Vec<WitContract>` slot was
3776    /// accessed inline at two production sites in
3777    /// caixa-core/src/manifest.rs — the [`Self::declared_mesh_slots`]
3778    /// mesh-slot declared-slot enumerator's
3779    /// `!self.contratos.is_empty()` presence probe (which drives the
3780    /// `M3_AUTHOR_KEY_CONTRATOS` kebab-case author-label push every
3781    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-coherence
3782    /// gate reads) and the [`Self::aplicacao_view`] per-Aplicacao
3783    /// typed-view composer's `self.contratos.clone()` per-contract
3784    /// fold-in path (which materializes the typed
3785    /// [`crate::aplicacao::AplicacaoSpec`] view every
3786    /// [`crate::StandardLayout::verify`] Aplicacao-arm gate and every
3787    /// downstream `caixa-mesh` renderer dispatches on). A future
3788    /// extension of the outer `:contratos` axis (a per-cluster
3789    /// `:contratos-overrides` overlay the wasm-engine operator resolves
3790    /// at admission time so a cluster-specific edge-set can tighten a
3791    /// caixa-declared list without re-authoring the `caixa.lisp`,
3792    /// promotion of the plain `Vec<WitContract>` to a richer
3793    /// `{static, dynamic}` partition once runtime-resolved contract
3794    /// edges land, per-edge policy annotation once the M4 per-edge
3795    /// policy overlay axis lands) would have had to be threaded through
3796    /// both open-coded copies in lockstep or one consumer would
3797    /// silently disagree with the peer on which edge slice a given
3798    /// Caixa resolves to — the enumerator's presence probe reading the
3799    /// raw slot while the peer view-composer's fold-in path read an
3800    /// operator-resolved slot would silently split the paired
3801    /// declared-slot enumerator and typed-view composition, a
3802    /// two-consumer split at the enumerator and the view composer far
3803    /// from the source `caixa.lisp` with no field naming the edge-set-
3804    /// drift root cause. Lifting the resolution rule to a typed method
3805    /// on the substrate primitive means every downstream consumer of
3806    /// the caixa's per-`Caixa` MESH-COMPOSITION outer-slice surface
3807    /// reaches for exactly one typed dispatch — the resolver's
3808    /// accept-set migrates as a unit on any future axis addition.
3809    ///
3810    /// Fourth and final outer top-level [`Caixa`] `&[Composite]`-return
3811    /// slice accessor for M2 / M3 typed-slot vec-carry axes — closes
3812    /// the outer-`Caixa` `&[Composite]` composite-slice sub-family the
3813    /// sibling M2 [`Self::upgrade_from`] (2a1f907) / [`Self::children`]
3814    /// (c17b51e) accessors opened and the M3 [`Self::membros`]
3815    /// (0f26987) accessor folded on, and closes the outer-`Caixa` M3
3816    /// mesh-slot arm of the composite-slice sub-family the sibling
3817    /// [`Self::membros`] accessor opened for the M3 vec-carry altitude.
3818    /// Peer at the outer altitude of the closed inner-
3819    /// [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
3820    /// same MESH-COMPOSITION per-Aplicacao contract-list axis — the two
3821    /// altitudes now share the same "byte-equal, borrow-shared" outer-
3822    /// accessor discipline. Sibling in shape to the peer outer-`Caixa`
3823    /// `&[Dep]`-return [`Self::deps`] (ad34b4e) / [`Self::deps_dev`]
3824    /// (f7fd81e) and `&[String]`-return [`Self::autores`] (b5d813f) /
3825    /// [`Self::etiquetas`] (78c7d3c) / [`Self::bibliotecas`] (8a36c23) /
3826    /// [`Self::exe`] (65d9527) / [`Self::servicos`] (611f78b) slice-
3827    /// accessors on the sibling outer-`Caixa` scalar-element vec-carry
3828    /// axes — folds the "outer [`Caixa`] `&[T]` slice" projection
3829    /// pattern onto the sibling M3 typed-composite-element axis
3830    /// ([`crate::aplicacao::WitContract`] composite, matching the
3831    /// per-inner [`crate::AplicacaoSpec::contratos`] element type at a
3832    /// different altitude).
3833    ///
3834    /// Returns `&[crate::aplicacao::WitContract]` (not
3835    /// `&Vec<WitContract>`) because every downstream consumer of the
3836    /// contract list treats it as a read-only sequence — the slice-view
3837    /// is the narrowest borrow that supports every present + roadmapped
3838    /// consumer (`.iter()`, `.len()`, `.is_empty()`, per-edge WIT-world
3839    /// discriminant dispatch, `serde` slice-serialization) without
3840    /// leaking the backing `Vec`'s grow/push/reserve surface no
3841    /// consumer of the typed view reaches for (the storage-side `Vec`
3842    /// remains reachable through the `pub contratos` field for the
3843    /// mutation-carrying serde round-trip and per-test fixture-mutation
3844    /// paths, including the [`Self::aplicacao_view`] fold-in path that
3845    /// clones the slot into the typed view). Named `contratos()` to
3846    /// match the storage field's name verbatim and the tatara-lisp
3847    /// author-surface term (`:contratos`) the field's own docstring
3848    /// already carries; the accessor's identity maps onto the canonical
3849    /// MESH-COMPOSITION vocabulary the [`Caixa::contratos`] field's
3850    /// docstring already reaches for ("WIT-typed inter-Servico
3851    /// contracts").
3852    #[must_use]
3853    pub const fn contratos(&self) -> &[crate::aplicacao::WitContract] {
3854        self.contratos.as_slice()
3855    }
3856
3857    /// Compose the Aplicacao-related flat slots into a single typed
3858    /// [`crate::aplicacao::AplicacaoSpec`] for validation +
3859    /// downstream renderer consumption. Returns `None` when the
3860    /// caixa isn't a `:kind Aplicacao`.
3861    #[must_use]
3862    pub fn aplicacao_view(&self) -> Option<crate::aplicacao::AplicacaoSpec> {
3863        if !self.kind().is_aplicacao() {
3864            return None;
3865        }
3866        Some(crate::aplicacao::AplicacaoSpec {
3867            membros: self.membros().to_vec(),
3868            contratos: self.contratos().to_vec(),
3869            politicas: self.politicas().cloned().unwrap_or_default(),
3870            placement: self.placement().cloned().unwrap_or_default(),
3871            entrada: self.entrada().cloned(),
3872        })
3873    }
3874
3875    /// The kebab-case `:slot` tags of every M3 mesh slot this caixa
3876    /// *declares* a value on, in canonical declaration order
3877    /// (`:membros` → `:contratos` → `:politicas` → `:placement` →
3878    /// `:entrada`). A slot counts as declared when its backing field
3879    /// carries a value — a non-empty `Vec`, or a `Some(...)`.
3880    ///
3881    /// The M3 mesh slots compose the typed graph of a `:kind Aplicacao`
3882    /// (MESH-COMPOSITION §III.1). [`Self::aplicacao_view`] only folds
3883    /// them into a validatable [`crate::aplicacao::AplicacaoSpec`] when
3884    /// the kind matches (returns `None` otherwise), and the caixa-mesh /
3885    /// caixa-flux / caixa-helm renderers only emit them for an
3886    /// Aplicacao. On any *other* kind a declared mesh slot is the
3887    /// manifest field's documented "ignored otherwise" (see the
3888    /// `:membros` … `:entrada` field docs): it silently passes
3889    /// [`Caixa::from_lisp`] and then vanishes — never validated, never
3890    /// rendered — far from the source caixa.lisp.
3891    /// [`crate::StandardLayout::verify`] consults this to reject that
3892    /// silent-drop at caixa-build time
3893    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]), mirroring the
3894    /// `SupervisorOwnsCode` / `AplicacaoOwnsCode` kind-coherence gates:
3895    /// a slot foreign to the kind is a build error, not a silent drop.
3896    ///
3897    /// Lifted as a typed method (rather than an inline disjunction at
3898    /// the verify call site) so the mesh-slot set lives in one place —
3899    /// a future M4 axis added to the Aplicacao surface (per-edge policy
3900    /// overlay, distributed-app takeover config) is one push here, and
3901    /// every consumer reaching for "which mesh slots are set" (the
3902    /// verify gate, a future `feira lint` kind-coherence advisory)
3903    /// inherits the canonical order without rolling its own.
3904    ///
3905    /// Each per-arm kebab-case label is routed through the peer
3906    /// [`crate::M3_AUTHOR_KEY_MEMBROS`] /
3907    /// [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
3908    /// [`crate::M3_AUTHOR_KEY_POLITICAS`] /
3909    /// [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
3910    /// [`crate::M3_AUTHOR_KEY_ENTRADA`] consts declared next to the
3911    /// [`crate::M3_KEY_PLACEMENT`] renderer-side wire-key peer, so both
3912    /// halves of every M3 top-level mesh slot's dual axis (author-facing
3913    /// kebab-case label + renderer-side artifact key) route through one
3914    /// canonical declaration per arm — same discipline the peer
3915    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`]
3916    /// / [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot consts
3917    /// (f49c8b0) establish on the sibling per-Servico M2 top-level slot
3918    /// axis, extended here to close the M3 mesh-slot author-facing-label
3919    /// axis so both altitudes of the typed-slot algebra
3920    /// (per-Servico M2 + per-Aplicacao M3) share the same
3921    /// "one canonical byte-string per arm, next to the axis" discipline.
3922    #[must_use]
3923    pub fn declared_mesh_slots(&self) -> Vec<&'static str> {
3924        let mut slots = Vec::new();
3925        if !self.membros().is_empty() {
3926            slots.push(crate::render::M3_AUTHOR_KEY_MEMBROS);
3927        }
3928        if !self.contratos().is_empty() {
3929            slots.push(crate::render::M3_AUTHOR_KEY_CONTRATOS);
3930        }
3931        if self.politicas().is_some() {
3932            slots.push(crate::render::M3_AUTHOR_KEY_POLITICAS);
3933        }
3934        if self.placement().is_some() {
3935            slots.push(crate::render::M3_AUTHOR_KEY_PLACEMENT);
3936        }
3937        if self.entrada().is_some() {
3938            slots.push(crate::render::M3_AUTHOR_KEY_ENTRADA);
3939        }
3940        slots
3941    }
3942
3943    /// The kebab-case `:slot` tags of every supervisor-tree slot this
3944    /// caixa *declares* a value on, in canonical declaration order
3945    /// (`:estrategia` → `:max-restarts` → `:restart-window` →
3946    /// `:children`). A slot counts as declared when its backing field
3947    /// carries a value — a `Some(...)`, or a non-empty `Vec`.
3948    ///
3949    /// The supervisor-tree slots compose the typed OTP supervisor of a
3950    /// `:kind Supervisor` (INSPIRATIONS §II.2; the `:estrategia` +
3951    /// `:children` field docs above). [`Self::supervisor_view`] only
3952    /// folds them into a validatable [`SupervisorSpec`] when the kind
3953    /// matches (returns `None` otherwise), and the wasm-operator's
3954    /// hierarchical reconciler only consumes them for a Supervisor. On
3955    /// any *other* kind a declared supervisor slot is the manifest
3956    /// field's documented "ignored otherwise" (see the `:estrategia` …
3957    /// `:children` field docs): it silently passes [`Caixa::from_lisp`]
3958    /// and then vanishes — never validated, never reconciled — far from
3959    /// the source caixa.lisp. [`crate::StandardLayout::verify`] consults
3960    /// this to reject that silent-drop at caixa-build time
3961    /// ([`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]), the
3962    /// exact mirror of the [`Self::declared_mesh_slots`] /
3963    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] gate on the
3964    /// Aplicacao-only slot set: a slot foreign to the kind is a build
3965    /// error, not a silent drop.
3966    #[must_use]
3967    pub fn declared_supervisor_slots(&self) -> Vec<&'static str> {
3968        let mut slots = Vec::new();
3969        if self.estrategia().is_some() {
3970            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA);
3971        }
3972        if self.max_restarts().is_some() {
3973            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS);
3974        }
3975        if self.restart_window().is_some() {
3976            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW);
3977        }
3978        if !self.children().is_empty() {
3979            slots.push(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN);
3980        }
3981        slots
3982    }
3983
3984    /// The kebab-case `:slot` tags of every M2 Servico-runtime slot this
3985    /// caixa *declares* a value on, in canonical declaration order
3986    /// (`:limits` → `:behavior` → `:upgrade-from`). A slot counts as
3987    /// declared when its backing field carries a value — a `Some(...)`,
3988    /// or a non-empty `Vec`.
3989    ///
3990    /// The M2 slots configure the runtime of a long-running wasm
3991    /// component, i.e. a `:kind Servico`: `:limits` is Lunatic
3992    /// per-process sandboxing (INSPIRATIONS §III.1), `:behavior` is the
3993    /// OTP `gen_server` callback set (§II.3), `:upgrade-from` is the OTP
3994    /// appup hot-code-reload table (§II.4). The caixa-helm / caixa-flux
3995    /// renderers gate on [`crate::require_kind`]`(_, Servico)` and only
3996    /// emit these slots for a Servico; on any *other* kind a declared M2
3997    /// slot is the manifest field's documented "ignored otherwise": its
3998    /// well-formedness is checked by [`crate::StandardLayout::verify`]
3999    /// but the value is never rendered into a chart / programs.yaml entry
4000    /// — it silently passes [`Caixa::from_lisp`] + `feira build` and then
4001    /// vanishes, far from the source caixa.lisp.
4002    /// [`crate::StandardLayout::verify`] consults this to reject that
4003    /// silent-drop at caixa-build time
4004    /// ([`crate::LayoutError::ServicoSlotsOnNonServico`]), the exact
4005    /// mirror of the [`Self::declared_mesh_slots`] /
4006    /// [`Self::declared_supervisor_slots`] gates on the peer
4007    /// kind-exclusive slot sets: a slot foreign to the kind is a build
4008    /// error, not a silent drop.
4009    ///
4010    /// Each per-arm kebab-case label is routed through the peer
4011    /// [`crate::M2_AUTHOR_KEY_LIMITS`] / [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
4012    /// [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts declared next to the
4013    /// [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
4014    /// [`crate::M2_KEY_UPGRADE_FROM`] renderer-side wire-key peers, so
4015    /// both halves of the M2 top-level slot's dual axis (author-facing
4016    /// kebab-case label + renderer-side camelCase overlay-container wire
4017    /// key) route through one canonical declaration per arm — same
4018    /// discipline the peer [`crate::M2_BEHAVIOR_AUTHOR_KEY_ON_*`] sub-slot
4019    /// author-label consts (889dc18) establish on the sibling
4020    /// per-callback axis inside the `:behavior` overlay block.
4021    #[must_use]
4022    pub fn declared_servico_slots(&self) -> Vec<&'static str> {
4023        let mut slots = Vec::new();
4024        if self.limits().is_some() {
4025            slots.push(crate::render::M2_AUTHOR_KEY_LIMITS);
4026        }
4027        if self.behavior().is_some() {
4028            slots.push(crate::render::M2_AUTHOR_KEY_BEHAVIOR);
4029        }
4030        if !self.upgrade_from().is_empty() {
4031            slots.push(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM);
4032        }
4033        slots
4034    }
4035
4036    /// The kebab-case `:slot` tags of every code-surface slot this caixa
4037    /// declares a value on that its [`CaixaKind`] doesn't natively own,
4038    /// in canonical declaration order (`:exe` → `:servicos`). A
4039    /// code-surface slot is owned by exactly one kind: `:exe` by
4040    /// [`CaixaKind::Binario`] (the nix-built executable surface), and
4041    /// `:servicos` by [`CaixaKind::Servico`] (the wasm component +
4042    /// `ComputeUnit` daemon surface).
4043    ///
4044    /// Each is silently ignored when declared on the wrong kind: the
4045    /// caixa-helm / caixa-flux / caixa-flake renderers gate on
4046    /// [`crate::require_kind`]`(_, <owning-kind>)`, so on any *other*
4047    /// code-running kind a declared `:exe` / `:servicos` is the manifest
4048    /// field's documented "ignored otherwise" — its path is checked for
4049    /// existence by the layout's `bibliotecas`/`exe`/`servicos` loops
4050    /// (which run after [`Caixa::from_lisp`]), but the value is never
4051    /// rendered into a build target or programs.yaml entry. It silently
4052    /// passes [`Caixa::from_lisp`] + `feira build`, far from the source
4053    /// caixa.lisp, with no field naming which slot is foreign.
4054    ///
4055    /// [`crate::StandardLayout::verify`] consults this to reject that
4056    /// silent-drop at caixa-build time
4057    /// ([`crate::LayoutError::ForeignCodeSlot`]), beside the M2
4058    /// servico-runtime, supervisor-tree, and M3 mesh kind-coherence
4059    /// gates ([`Self::declared_servico_slots`] /
4060    /// [`Self::declared_supervisor_slots`] /
4061    /// [`Self::declared_mesh_slots`]): the fourth kind ↔ slot algebra
4062    /// axis to be closed on the typed surface. The Supervisor /
4063    /// Aplicacao "no code at all" cases ([`crate::LayoutError::SupervisorOwnsCode`]
4064    /// / [`crate::LayoutError::AplicacaoOwnsCode`]) keep their dedicated
4065    /// diagnostics — they fire ahead of this gate on the same `verify`
4066    /// pass, so for Supervisor / Aplicacao the `OwnCode` arm always wins
4067    /// and this method is moot. For Biblioteca / Binario / Servico, this
4068    /// gate fires when a code-running kind declares another code-running
4069    /// kind's exclusive code surface.
4070    ///
4071    /// `:bibliotecas` is deliberately excluded — a Binario or Servico
4072    /// may legitimately ship a `lib/` helper that the underlying
4073    /// substrate (the nix flake for Binario, the wasm component build
4074    /// for Servico) bundles into its build, so the slot's
4075    /// declared-on-wrong-kind cardinality isn't a structural error on
4076    /// either code-running kind. A Biblioteca declaring `:bibliotecas`
4077    /// is the native case (the slot's owning kind). Supervisor /
4078    /// Aplicacao declaring `:bibliotecas` is gated upstream by
4079    /// [`crate::LayoutError::SupervisorOwnsCode`] /
4080    /// [`crate::LayoutError::AplicacaoOwnsCode`].
4081    ///
4082    /// Lifted as a typed method (rather than an inline disjunction at
4083    /// the verify call site) so the foreign-code-slot set lives in one
4084    /// place — a future kind that gains its own code-surface slot is
4085    /// one push here, and every consumer reaching for "which code
4086    /// surfaces are foreign to this kind" (the verify gate, a future
4087    /// `feira lint` kind-coherence advisory, the future `app-operator`'s
4088    /// per-caixa build-target classifier) inherits the canonical order
4089    /// without rolling its own.
4090    #[must_use]
4091    pub fn declared_foreign_code_slots(&self) -> Vec<&'static str> {
4092        let mut slots = Vec::new();
4093        if !self.exe().is_empty() && !self.kind().requires_exe() {
4094            slots.push(":exe");
4095        }
4096        if !self.servicos().is_empty() && !self.kind().requires_servicos() {
4097            slots.push(":servicos");
4098        }
4099        slots
4100    }
4101
4102    /// Validate every entry of `:deps` and `:deps-dev` through
4103    /// [`Dep::validate`] — closing the parity loop with the per-axis
4104    /// `:versao` gates already wired into the typed-graph
4105    /// ([`crate::AplicacaoSpec::validate_membros`] for `:membros`,
4106    /// 9888b13) and typed supervisor tree
4107    /// ([`crate::SupervisorSpec::validate`] for `:children`, b38ff3a).
4108    ///
4109    /// Until this gate landed `:deps :versao` and `:deps-dev :versao`
4110    /// were the only `:versao` axes still untyped past
4111    /// [`Caixa::from_lisp`]: the derive macro stored the requirement
4112    /// as a String without parsing it, so a malformed-but-non-empty
4113    /// requirement (`"^bad-version"`, `"^^0.1"`, `"v0.1"`, `"not-a-req"`)
4114    /// silently passed parse and the `semver::Error` surfaced at
4115    /// lacre-resolve time, far from the source caixa.lisp, with no
4116    /// field naming which `:deps` entry carried the typo. Lifting the
4117    /// gate here makes the four `:versao` typed surfaces (`:deps`,
4118    /// `:deps-dev`, `:membros`, `:children`) structurally equivalent —
4119    /// every requirement string past `validate_deps` is round-trippable
4120    /// through [`crate::parse_requirement`] without re-checking at the
4121    /// resolver layer.
4122    ///
4123    /// Both lists run through the same per-entry validator so a typo
4124    /// in `:deps-dev` surfaces with the same diagnostic as one in
4125    /// `:deps` — neither axis is a second-class citizen of the typed
4126    /// surface.
4127    ///
4128    /// Within each list, [`DepError::DuplicateNome`] closes the
4129    /// set-not-multiset discipline on the `:nome` axis: two entries
4130    /// naming the same caixa carry two `:versao` / `:fonte` / feature
4131    /// triples that the caixa-resolver's lacre pipeline collapses to one
4132    /// via its `HashMap`-keyed-by-`:nome` consumption — the second entry
4133    /// silently overwrites the first at `concrete_versao`-resolve time
4134    /// (the same "second wins / one silently overwrites the other"
4135    /// shape the peer typed-graph duplicate gates already close on every
4136    /// other Vec-shaped authoring surface that keys by name). The
4137    /// duplicate check fires per-list and runs *after* each per-entry
4138    /// [`Dep::validate`] call so a malformed-and-duplicated entry
4139    /// surfaces its narrower per-entry diagnostic
4140    /// ([`DepError::NomeInvalid`], [`DepError::VersaoInvalid`],
4141    /// [`DepError::FonteRepoEmpty`], …) before the cross-entry duplicate
4142    /// diagnostic — the canonical "per-entry shape before cross-entry
4143    /// uniqueness" precedence the peer `:children :caixa`
4144    /// ([`crate::SupervisorSpec::validate`]), `:membros :caixa`
4145    /// ([`crate::AplicacaoSpec::validate_membros`]), `:contratos`
4146    /// ([`crate::AplicacaoSpec::validate`]), `:placement :clusters`
4147    /// ([`crate::AplicacaoSpec::validate_placement`]),
4148    /// `:entrada :paths` ([`crate::AplicacaoSpec::validate`]),
4149    /// `:upgrade-from :from` ([`crate::upgrade::validate_upgrade_from`]),
4150    /// and the within-`:upgrade-from`-entry per-instruction-class
4151    /// singularity gates ([`crate::UpgradeError::DuplicateLoadModule`],
4152    /// [`crate::UpgradeError::DuplicateStateChange`],
4153    /// [`crate::UpgradeError::DuplicateCleanup`]) all establish.
4154    ///
4155    /// Cross-list (`:deps` ↔ `:deps-dev`) coincidence is *not* gated
4156    /// here: Cargo's `[dependencies]` + `[dev-dependencies]` accept the
4157    /// same name in both tables (the dev table's pin overrides the
4158    /// runtime table's pin in test/dev contexts), and caixa's surface
4159    /// mirrors that convention until a deliberate choice retires the
4160    /// override pattern. Only within-list duplicates are structurally
4161    /// incoherent — those are what this gate closes.
4162    ///
4163    /// Compound per-`Caixa` entry gate on the dep-graph axis: folds the
4164    /// two standalone dep-list validators — the per-entry + within-list
4165    /// duplicate-`:nome` walk (the [`Dep::validate`] +
4166    /// [`crate::render::insert_first_seen`] cascade this method opened
4167    /// on) and the cross-slot self-edge gate
4168    /// ([`crate::dep::validate_no_self_dep`]) — onto one substrate
4169    /// primitive on [`Caixa`]. The two arms run in the same canonical
4170    /// order the layout pipeline
4171    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4172    /// author-time gate) has always sequenced them (per-entry +
4173    /// cross-entry duplicate → cross-slot self-edge), so the fold is
4174    /// byte-for-byte equivalent to the pre-fold two-block cascade at
4175    /// that call site (pinned by the paired
4176    /// `validate_deps_folds_per_entry_arm_matches_gate` /
4177    /// `validate_deps_folds_self_edge_arm_matches_gate` equivalence
4178    /// pins and the `validate_deps_per_entry_arm_fires_before_self_edge_arm`
4179    /// ordering pin). Self-contained on `&self` — resolves its three
4180    /// inputs ([`Self::deps`], [`Self::deps_dev`], [`Self::nome`])
4181    /// through the substrate primitives' own accessor family, the same
4182    /// posture every peer per-slot compound gate
4183    /// ([`crate::AplicacaoSpec::validate_contratos`],
4184    /// [`crate::MeshPolicy::validate`],
4185    /// [`crate::SupervisorSpec::validate_children`],
4186    /// [`Self::validate_upgrade_from`]) already carries.
4187    ///
4188    /// Prior to this lift [`crate::dep::validate_no_self_dep`] lived
4189    /// only open-coded at the layout wire-up site
4190    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs)
4191    /// as a standalone two-arg dispatch immediately after this method's
4192    /// per-entry + cross-entry walk, both wrapped through the same
4193    /// [`crate::LayoutError::DepsViolation`] envelope: every future
4194    /// consumer that wanted to gate the dep-graph as a whole — the
4195    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4196    /// per-CR admission webhook re-checking `:deps` / `:deps-dev` after
4197    /// a per-entry patch, a future `feira validate --deps` per-caixa
4198    /// admission verb, a per-`:deps` overlay resolver a per-cluster
4199    /// overlay lift would materialize (each the deferred consumer this
4200    /// method's peer [`Self::deps`] / [`Self::deps_dev`] accessors'
4201    /// docstrings already name) — was structurally forced to either
4202    /// re-inline the two-dispatch cascade in lockstep with the layout
4203    /// wire-up (the duplication the PRIME DIRECTIVE names as a bug) or
4204    /// call the whole [`crate::layout::StandardLayout::verify`] pipeline
4205    /// and pay every peer per-Caixa gate to re-check one slot. Post-fold
4206    /// each such consumer reaches the two-arm compound gate through one
4207    /// call on the substrate primitive.
4208    pub fn validate_deps(&self) -> Result<(), DepError> {
4209        for &list in crate::dep::DepList::ALL {
4210            let mut seen = std::collections::HashSet::new();
4211            for dep in self.deps_of(list) {
4212                dep.validate()?;
4213                crate::render::insert_first_seen(&mut seen, dep.nome(), || {
4214                    DepError::duplicate_nome(dep.nome(), list.as_str())
4215                })?;
4216            }
4217        }
4218        crate::dep::validate_no_self_dep(self.deps(), self.deps_dev(), self.nome())?;
4219        Ok(())
4220    }
4221
4222    /// Run a per-slot typed validator on `self` and, on the per-arm
4223    /// parser-side error arm, thread the error into a paired
4224    /// [`crate::LayoutError`] wrap under `self.nome()`. Substrate
4225    /// primitive folding the 18 self-similar layout-pipeline wire-up
4226    /// sites at [`crate::layout::StandardLayout::verify`] that carry
4227    /// the identical
4228    /// `caixa.validate_<slot>().map_err(|err| crate::LayoutError::<slot>_violation(caixa, err))?;`
4229    /// cascade onto one dispatch. Each of the eighteen sites (`:nome`,
4230    /// `:nome`-chart-name-budget, `:versao`, `:deps`, `:etiquetas`,
4231    /// `:autores`, `:repositorio`, `:descricao`, `:licenca`, `:edicao`,
4232    /// `:bibliotecas`/`:exe`/`:servicos` code-path shape, `:limits`,
4233    /// `:behavior`, `:upgrade-from`, `:restart-window`, per-Supervisor
4234    /// shape, per-Aplicacao shape, per-Acao shape) carried the same
4235    /// four-line "run a per-slot typed validator on `caixa` and, on the
4236    /// per-arm parser-side error arm, thread it into the paired
4237    /// [`crate::LayoutError`] one-slot envelope through the substrate-
4238    /// canonical `layout_violation_ctors!` family (131ca0d)" cascade,
4239    /// differing only in the two names bound at each site — the
4240    /// validator (`Caixa::validate_deps` / `validate_nome` / ...) and
4241    /// the paired ctor (`LayoutError::deps_violation` / ...). Eighteen
4242    /// consumers, one identical shape, one substrate primitive on
4243    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
4244    /// a bug — on the second half of the per-slot cascade the peer
4245    /// substrate primitives on the [`crate::LayoutError`]-wrap side
4246    /// (the `layout_violation_ctors!` macro 131ca0d, the
4247    /// `layout_slot_kind_ctors!` macro 0419438, the `layout_nome_only_ctors!`
4248    /// macro 3fe3dd7, the [`crate::LayoutError::missing_entry`] ctor
4249    /// 1b09f9d, the [`crate::layout::StandardLayout::probe_declared_entry`]
4250    /// primitive fda1e35) each closed on their sibling envelopes; the
4251    /// first half of the cascade (the per-slot compound gates
4252    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
4253    /// baa4688, [`Self::validate_behavior`] 0d2877a,
4254    /// [`Self::validate_upgrade_from`] d6801df,
4255    /// [`Self::validate_aplicacao_shape`] 949a7a0,
4256    /// [`Self::validate_supervisor_shape`] 4c70105,
4257    /// [`Self::validate_acao_shape`] 5d6df54,
4258    /// [`Self::validate_kind_slot_coherence`] f0d286e,
4259    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
4260    /// [`Self::validate_ci_kind_coherence`] 9b55beb,
4261    /// [`Self::validate_required_kind_slot`] 9c385d8) each closed on
4262    /// their per-slot compound gates.
4263    ///
4264    /// Composes the [`crate::layout::LayoutError`] wrap and the per-slot
4265    /// typed validator through two typed callables: `gate` runs on
4266    /// `self` and yields a per-slot error `E`; on the `Err(E)` arm
4267    /// `wrap` re-wraps that error under `self` into a
4268    /// [`crate::layout::LayoutError`]. The `Ok(())` arm passes through
4269    /// verbatim as the fold's identity element — byte-equal to the
4270    /// pre-lift `Result::map_err` short-circuit at the `?;` marker
4271    /// every wire-up site formerly carried. Every future consumer that
4272    /// wants to run one of the per-slot gates and thread its error
4273    /// through the layout wrap (the deferred
4274    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4275    /// webhook per-slot re-check, a future `feira validate --<slot>`
4276    /// per-caixa admission verb, an overlay resolver re-running one
4277    /// gate after a per-slot patch) reaches the two-callable dispatch
4278    /// through one call rather than re-inlining the four-line cascade
4279    /// in lockstep with the pre-existing 18 wire-ups. The two callables
4280    /// reach the primitive as first-class type-checked references
4281    /// rather than the pre-lift `.map_err(|err| CTOR(caixa, err))`
4282    /// closure body — so a mismatch between the validator's `E` type
4283    /// and the ctor's `E` bound trips at the wire-up site (compile-
4284    /// time) rather than at the closure body (also compile-time, but
4285    /// with a diagnostic pointing at the closure expression rather
4286    /// than the two named callables).
4287    pub fn run_layout_gate<E, W>(
4288        &self,
4289        gate: impl FnOnce(&Caixa) -> Result<(), E>,
4290        wrap: W,
4291    ) -> Result<(), crate::LayoutError>
4292    where
4293        W: FnOnce(&Caixa, E) -> crate::LayoutError,
4294    {
4295        gate(self).map_err(|err| wrap(self, err))
4296    }
4297
4298    /// Run one arm of the cross-family kind ↔ owned-slot-family
4299    /// coherence cascade on `self`: on a caixa whose [`Self::kind`] does
4300    /// not own the typed-slot family named by `is_owner`, refuse when
4301    /// the paired `accumulator` reports any declared slot in that
4302    /// family; otherwise pass. Substrate primitive folding the three
4303    /// self-similar four-line
4304    /// `if !self.kind().is_<owner>() { let slots = self.declared_<family>_slots();
4305    /// if !slots.is_empty() { return Err(<wrap>(self, slots)); } }`
4306    /// arms at [`Self::validate_kind_slot_coherence`] onto one dispatch.
4307    /// Three consumers (M3 mesh — Aplicacao-owner, supervisor-tree —
4308    /// Supervisor-owner, M2 Servico-runtime — Servico-owner), one
4309    /// identical shape, one substrate primitive on [`Caixa`] closing
4310    /// the duplication the PRIME DIRECTIVE names as a bug on the
4311    /// outer kind-coherence arm shape — peer with the substrate
4312    /// primitives on the two adjacent halves of the same three-arm
4313    /// cascade the sibling [`Self::declared_mesh_slots`] /
4314    /// [`Self::declared_supervisor_slots`] /
4315    /// [`Self::declared_servico_slots`] accumulator family closes on
4316    /// the inner slot-set enumerator axis and the sibling
4317    /// [`crate::layout::layout_slot_kind_ctors!`] macro (0419438)
4318    /// closes on the inner wrap-envelope ctor axis. Each of the three
4319    /// [`Self::validate_kind_slot_coherence`] arms now reads through
4320    /// one call across every altitude of the per-arm cascade:
4321    /// one dispatch on this primitive for the outer guard shape, one
4322    /// dispatch on `Self::declared_<family>_slots` for the accumulator,
4323    /// one dispatch on `crate::LayoutError::<family>_on_non_<owner>`
4324    /// for the wrap ctor.
4325    ///
4326    /// Composes the outer owner-kind guard, the per-family accumulator,
4327    /// and the per-family wrap ctor through three typed callables:
4328    /// `is_owner` runs on `&self.kind()` (a `&CaixaKind` borrow so the
4329    /// `gen_platform::IsVariant`-derived `fn(&CaixaKind) -> bool`
4330    /// per-arm predicates — [`crate::CaixaKind::is_aplicacao`] /
4331    /// [`crate::CaixaKind::is_supervisor`] / [`crate::CaixaKind::is_servico`]
4332    /// — pass verbatim as function references), `accumulator` runs on
4333    /// `&self` and yields the
4334    /// per-family declared-slot list, and `wrap` runs on `(&self,
4335    /// Vec<&'static str>)` and yields the per-family
4336    /// [`crate::LayoutError`] wrap. The `is_owner` short-circuit fires
4337    /// before the accumulator dispatch (so the owner kind of each
4338    /// family passes without invoking `accumulator`, byte-equal to the
4339    /// pre-lift `if !self.kind().is_<owner>() { … }` outer guard's
4340    /// short-circuit — pinned by
4341    /// `run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator`),
4342    /// and the accumulator's `is_empty` short-circuit fires before the
4343    /// wrap dispatch (so a non-owner kind with no declared slot in that
4344    /// family passes without invoking `wrap`, byte-equal to the pre-lift
4345    /// `if !<slots>.is_empty() { … }` inner guard's short-circuit —
4346    /// pinned by
4347    /// `run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap`).
4348    /// The wrap ctor is `FnOnce(&Caixa, Vec<&'static str>) ->
4349    /// crate::LayoutError` — matching the [`crate::layout::layout_slot_kind_ctors!`]
4350    /// macro's per-variant `fn(&Caixa, Vec<&'static str>) -> LayoutError`
4351    /// substrate-canonical ctor shape verbatim, so
4352    /// [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
4353    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
4354    /// [`crate::LayoutError::servico_slots_on_non_servico`] pass as
4355    /// function references without a closure wrap. A mismatch between
4356    /// the ctor's signature and this bound trips at the wire-up site
4357    /// (compile-time) rather than at a closure body.
4358    ///
4359    /// The sibling [`crate::LayoutError::ForeignCodeSlot`] gate on the
4360    /// code-surface family sits outside this primitive because
4361    /// [`Self::declared_foreign_code_slots`] bakes the per-arm kind-
4362    /// check into the accumulator itself (each arm's
4363    /// `!self.kind().requires_<slot>()` guard fires inside the
4364    /// accumulator, not around it), so the code-surface arm carries no
4365    /// outer `is_owner`-shaped guard and its dispatch reads through
4366    /// [`Self::validate_foreign_code_kind_coherence`] verbatim without
4367    /// this primitive — the same posture the `_no_code_` /
4368    /// `_ci_kind_` coherence axes take on their respective per-arm
4369    /// shapes. The primitive here is specific to the "outer
4370    /// non-owner-kind guard + inner accumulator + inner emptiness
4371    /// guard + wrap" arm shape that fires three times in
4372    /// [`Self::validate_kind_slot_coherence`].
4373    ///
4374    /// Every future consumer that wants to gate one kind-owned slot
4375    /// family as a unit outside the composed cascade (the deferred
4376    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission-
4377    /// webhook per-family re-check after a per-slot patch, a future
4378    /// `feira validate --<family>-coherence` per-caixa admission verb,
4379    /// a per-`Caixa` overlay resolver rejecting a kind-foreign patch
4380    /// on one family) reaches the four-line arm through one call
4381    /// rather than re-inlining the outer-guard + accumulator +
4382    /// emptiness-guard + wrap cascade in lockstep with the pre-existing
4383    /// three arms. Every future kind-owned typed-slot family (an
4384    /// `Actor`-owned per-virtual-actor grain slot the M5 Orleans-
4385    /// inspired kind reaches through, a per-Aplicacao overlay slot the
4386    /// M4 CR materializer consults) folds onto
4387    /// [`Self::validate_kind_slot_coherence`] as one additional
4388    /// dispatch on this primitive rather than a fourth open-coded
4389    /// four-line block.
4390    pub fn run_kind_owned_slot_family_gate<F, A, W>(
4391        &self,
4392        is_owner: F,
4393        accumulator: A,
4394        wrap: W,
4395    ) -> Result<(), crate::LayoutError>
4396    where
4397        F: FnOnce(&crate::CaixaKind) -> bool,
4398        A: FnOnce(&Caixa) -> Vec<&'static str>,
4399        W: FnOnce(&Caixa, Vec<&'static str>) -> crate::LayoutError,
4400    {
4401        if is_owner(&self.kind()) {
4402            return Ok(());
4403        }
4404        let slots = accumulator(self);
4405        if slots.is_empty() {
4406            return Ok(());
4407        }
4408        Err(wrap(self, slots))
4409    }
4410
4411    /// Reject `:nome` values the K8s apiserver would refuse at admission
4412    /// time. The top-level Caixa identity flows directly into every
4413    /// substrate-side artifact's `metadata.name` axis: the
4414    /// `lareira-<nome>` Helm chart name ([`caixa-helm::lib::chart_name`]),
4415    /// the programs.yaml `name:` entry the `lareira-fleet-programs`
4416    /// aggregator keys ComputeUnit derivation off
4417    /// ([`caixa-flux::lib::programs_yaml_entry`]), the
4418    /// `LABEL_APLICACAO` label value carried on every Aplicacao-owned
4419    /// pod and the per-`:contratos` CiliumNetworkPolicy `metadata.name`
4420    /// (`<aplicacao>-<de>-to-<para>`) and the per-`:entrada`
4421    /// `<aplicacao>-<para>` HTTPRoute `metadata.name`
4422    /// ([`caixa-mesh::lib::cilium_network_policies`],
4423    /// [`caixa-mesh::lib::gateway_routes`]), and the default
4424    /// `lib/<nome>.lisp` / `exe/<nome>` layout paths
4425    /// ([`crate::StandardLayout::verify`]). Each K8s apiserver-side
4426    /// schema enforces the DNS-1123 label rule on admission; a
4427    /// structurally invalid `:nome` (`"MyApp"` — the canonical
4428    /// "I copied the display name verbatim" footgun, `"my_app"` — the
4429    /// Python-/Postgres-leak, `"team.app"` — `:nome` is a single label
4430    /// not a subdomain, `"-app"` / `"app-"` — DNS-1123 boundary
4431    /// violations, `"my app"` — the paste-from-doc footgun, `"café"` —
4432    /// IDN must be pre-encoded as Punycode, the 64-byte UUID-shaped
4433    /// over-cap slug) silently passed [`Caixa::from_lisp`] and the
4434    /// failure surfaced at `kubectl apply` time as a `metadata.name:
4435    /// Invalid value` rejection on whichever derived artifact admitted
4436    /// first, far from the source `caixa.lisp` and without any field
4437    /// naming the offending `:nome`.
4438    ///
4439    /// Thin wrapper around [`crate::render::is_dns_1123_label`] (the
4440    /// substrate-side predicate the per-axis name gates already share:
4441    /// `:membros :caixa` 3f9d7a0, `:placement :clusters` 6cbb900,
4442    /// `:children :caixa` 31bfa43) that maps the shared parser-shaped
4443    /// reason into the [`ManifestError::NomeInvalid`] variant, so the
4444    /// diagnostic is self-locating (the offending `:nome` is named
4445    /// verbatim) and the author can grep their `caixa.lisp` for
4446    /// `:nome "<value>"` and fix it in one edit. Same diagnostic shape
4447    /// every per-axis sibling gate already exposes
4448    /// ([`crate::AplicacaoError::MembroCaixaInvalid`],
4449    /// [`crate::AplicacaoError::PlacementClusterInvalid`],
4450    /// [`crate::SupervisorError::ChildCaixaInvalid`]).
4451    ///
4452    /// Empty `:nome` (which [`Caixa::from_lisp`] does not reject — the
4453    /// derive macro stores the raw String) is gated by the narrower
4454    /// [`ManifestError::NomeEmpty`] arm before the predicate is
4455    /// consulted, mirroring the empty-first cascade every per-axis
4456    /// name gate already uses (e.g. `MembroCaixaEmpty` before
4457    /// `MembroCaixaInvalid`, `EmptyChildName` before `ChildCaixaInvalid`).
4458    pub fn validate_nome(&self) -> Result<(), ManifestError> {
4459        // Routes through the shared
4460        // [`crate::render::require_valid_dns_1123_label`] gate the peer
4461        // name axes each land on so drift between the eight axes'
4462        // accepted DNS-1123-label sets is structurally impossible.
4463        let nome = self.nome();
4464        crate::render::require_valid_dns_1123_label(
4465            nome,
4466            || ManifestError::NomeEmpty,
4467            |reason| ManifestError::nome_invalid(nome, reason),
4468        )
4469    }
4470
4471    /// Reject `:nome` values whose joint length with the canonical
4472    /// [`crate::LAREIRA_CHART_NAME_PREFIX`] (`"lareira-"`) overflows
4473    /// the K8s DNS-1123 label cap [`crate::DNS_1123_LABEL_MAX_LEN`]
4474    /// (63 bytes). Every per-Servico / per-Aplicacao renderer the
4475    /// substrate carries materializes the caixa's `:nome` through the
4476    /// canonical [`crate::lareira_chart_name`] helper (f7320d7) into a
4477    /// `lareira-<nome>` artifact that lands as a K8s `metadata.name` /
4478    /// Helm chart name / `HelmRelease` `release_name`: `caixa-helm`'s
4479    /// `ChartDir.name` + `Chart.yaml::name`
4480    /// (caixa-helm/src/lib.rs:207), `caixa-flux`'s `cluster_bundle`
4481    /// `HelmRelease` `chart:` slot (caixa-flux/src/lib.rs:329),
4482    /// `caixa-tatara`'s `process_for_aplicacao` `release_name` +
4483    /// `oci://<registry>/lareira-<nome>` chart ref
4484    /// (caixa-tatara/src/lib.rs:124,178). Helm's own `Chart.yaml::name`
4485    /// admission rule strict-parses against DNS-1123-label, the Helm
4486    /// operator's tracking-secret name is derived from `release_name`
4487    /// and is itself DNS-1123-label-bounded, and the rendered chart's
4488    /// K8s object `metadata.name` axes embed the chart name as a
4489    /// prefix — every one fails admission on a > 63-byte chart name.
4490    ///
4491    /// The per-axis [`Self::validate_nome`] gate (6c992f8) already
4492    /// caps `:nome` itself at 63 bytes via [`is_dns_1123_label`], so a
4493    /// `:nome` of 56–63 bytes silently passed validate (the inner
4494    /// DNS-1123 check accepts the bare `:nome`) but produced a
4495    /// `lareira-<nome>` of 64–71 bytes that the apiserver / `helm lint`
4496    /// rejected at admission — far from the source `caixa.lisp`, with
4497    /// no field naming the overflow root cause. The
4498    /// [`lareira_chart_name`] helper's own doc comment
4499    /// (caixa-core/src/render.rs:3198) explicitly deferred the fix:
4500    /// "the M4 admission webhook will pin the joint-length invariant
4501    /// when it lands". This gate lands the invariant at the
4502    /// manifest-validate layer rather than waiting for the apiserver
4503    /// — the same fail-at-the-source posture every peer per-axis
4504    /// value-shape gate (DNS-1123 on `:nome`, SemVer-2 on `:versao`,
4505    /// SPDX-expression-shape on `:licenca`, 4-digit decimal year on
4506    /// `:edicao`, etc.) takes.
4507    ///
4508    /// Thin wrapper around
4509    /// [`crate::render::is_lareira_chart_name_shape`] (the
4510    /// substrate-side predicate that composes [`lareira_chart_name`] +
4511    /// [`is_dns_1123_label`] via the lifted
4512    /// [`crate::LAREIRA_CHART_NAME_NOME_MAX_LEN`] budget); maps the
4513    /// shared parser-shaped reason into the
4514    /// [`ManifestError::NomeChartNameBudgetExceeded`] variant so the
4515    /// diagnostic is self-locating (the offending `:nome` is named
4516    /// verbatim alongside the rendered chart name and the budget) and
4517    /// the author can shorten in one edit. The gate runs across every
4518    /// `:kind` — `:nome` is the substrate-wide identity axis any
4519    /// future renderer the substrate adds can derive a
4520    /// `lareira-<nome>` artifact from, and uniform enforcement closes
4521    /// the drift footgun where a future kind grows a chart-emitting
4522    /// render path while the validate cascade doesn't catch it.
4523    ///
4524    /// Runs *after* [`Self::validate_nome`] so the narrower
4525    /// `NomeEmpty` / `NomeInvalid` shape diagnostics fire first — a
4526    /// structurally-malformed `:nome` (empty, uppercase, underscore,
4527    /// dot, leading/trailing hyphen, Unicode, > 63 bytes) surfaces its
4528    /// specific shape error rather than the chart-name-budget error,
4529    /// preserving the legitimate "well-shaped `:nome` that happens to
4530    /// overflow the joint cap" arm for this gate.
4531    pub fn validate_nome_chart_name_budget(&self) -> Result<(), ManifestError> {
4532        let nome = self.nome();
4533        crate::render::is_lareira_chart_name_shape(nome)
4534            .map_err(|reason| ManifestError::nome_chart_name_budget_exceeded(nome, reason))
4535    }
4536
4537    /// Reject `:versao` values that don't parse as [`semver::Version`].
4538    /// The top-level Caixa version flows directly into every
4539    /// substrate-side artifact that carries a "this is which version of
4540    /// the caixa" axis: the `lareira-<nome>` Helm chart's `Chart.yaml`
4541    /// `version:` + `appVersion:` axes ([`caixa-helm::lib`] —
4542    /// SemVer-2-strict at `helm template` / `helm install` time per
4543    /// https://helm.sh/docs/topics/charts/#charts-and-versioning), the
4544    /// `feira publish` Zig-style `v<versao>` git tag
4545    /// ([`caixa-flux::lib::programs_yaml_entry`] / the
4546    /// `caixa-publish.yml` reusable workflow), the programs.yaml entry's
4547    /// `versao:` value the `lareira-fleet-programs` aggregator carries
4548    /// onto each rendered ComputeUnit, the OCI image's `:v<versao>` /
4549    /// `:latest` tags the substrate's `wasi-service-flake` builds with
4550    /// `skopeo push`, the lacre closure's pinned versions
4551    /// ([`caixa-resolver`] keys `concrete_versao`), and the
4552    /// `:upgrade-from :from` references peers in this exact `versao`
4553    /// shape (`semver::Version`, not `VersionReq`). Each consumer
4554    /// expects a strict three-part `MAJOR.MINOR.PATCH` (optionally
4555    /// `-prerelease` and/or `+build`); a structurally invalid `:versao`
4556    /// (`"0.1"` — missing patch, the canonical "I shortened it" footgun;
4557    /// `"v0.1.0"` — the git-tag-shape-leaking-into-versao typo;
4558    /// `"latest"` / `"main"` — the "I confused it with a docker tag"
4559    /// footgun; `"^0.1"` / `"~0.1.2"` — the requirement-shape leaking
4560    /// into the version field a peer `:deps :versao` accepts;
4561    /// `"0.1.0.0"` — the four-part Java/Microsoft convention DNS
4562    /// SemVer-2 forbids) silently passed [`Caixa::from_lisp`] (the
4563    /// derive macro stores the raw String) and the failure surfaced at
4564    /// the *first* downstream consumer that strict-parses it: at
4565    /// `helm install` time as a chart-version rejection, at
4566    /// `feira publish` time as a malformed git tag, at lacre-resolve
4567    /// time as a `semver::Error` not naming the offending caixa, at
4568    /// `feira upgrade --to <versao>` time as an unresolvable
4569    /// `:upgrade-from :from` match — far from the source `caixa.lisp`
4570    /// and without any field naming the offending `:versao`.
4571    ///
4572    /// Thin wrapper around [`semver::Version::parse`] — the same parser
4573    /// [`crate::CaixaVersion::parse`] (the typed `:versao` accessor)
4574    /// and [`crate::UpgradeFromEntry::validate`] (the peer
4575    /// `:upgrade-from :from` axis, 26da2c7) consume. Maps the
4576    /// `semver::Error` reason into the [`ManifestError::VersaoInvalid`]
4577    /// variant, carrying the offending `:versao` verbatim + a
4578    /// parser-shaped reason naming the specific violation, so the
4579    /// diagnostic is self-locating (the author can grep their
4580    /// `caixa.lisp` for `:versao "<value>"` and fix it in one edit).
4581    /// Same diagnostic shape as [`ManifestError::NomeInvalid`]
4582    /// (6c992f8) and [`crate::UpgradeError::FromInvalid`]
4583    /// (b0c8389) on the peer axes. With this gate, the typed `:versao`
4584    /// surfaces — top-level `:versao`, `:upgrade-from :from` — are
4585    /// now structurally equivalent (every value past validate is
4586    /// round-trippable through [`semver::Version::parse`] without
4587    /// re-checking at the renderer, resolver, or operator hot-upgrade
4588    /// layer), peer with the four `:versao` requirement axes (`:deps`,
4589    /// `:deps-dev`, `:membros`, `:children`) the prior commits
4590    /// (2420c44, 9888b13, b38ff3a) wired through `parse_requirement`.
4591    ///
4592    /// Empty `:versao` (which [`Caixa::from_lisp`] does not reject —
4593    /// the derive macro stores the raw String) is gated by the
4594    /// narrower [`ManifestError::VersaoEmpty`] arm before the parser is
4595    /// consulted, mirroring the empty-first cascade every per-axis
4596    /// version gate already uses (e.g. `MembroVersaoEmpty` before
4597    /// `MembroVersaoInvalid`, `EmptyChildVersion` before
4598    /// `ChildVersaoInvalid`, `NomeEmpty` before `NomeInvalid`).
4599    pub fn validate_versao(&self) -> Result<(), ManifestError> {
4600        let versao = self.versao();
4601        if versao.is_empty() {
4602            return Err(ManifestError::VersaoEmpty);
4603        }
4604        semver::Version::parse(versao)
4605            .map_err(|e| ManifestError::versao_invalid(versao, e.to_string()))?;
4606        Ok(())
4607    }
4608
4609    /// Compound per-`Caixa` entry gate on the M2 `:upgrade-from` slot:
4610    /// folds the three [`crate::upgrade`] top-level validators — the
4611    /// per-entry shape + cross-entry duplicate-`:from` gate
4612    /// ([`crate::upgrade::validate_upgrade_from`]), the cross-slot
4613    /// `:from < :versao` SemVer-2 precedence gate
4614    /// ([`crate::upgrade::validate_upgrade_from_against_versao`]), and the
4615    /// cross-slot `:state-change` ↔ `:on-state-change` composition gate
4616    /// ([`crate::upgrade::validate_upgrade_from_against_behavior`]) — onto
4617    /// one substrate primitive on [`Caixa`]. The three dispatches run in
4618    /// the same order the layout pipeline
4619    /// ([`crate::layout::StandardLayout::verify`], the `feira build`
4620    /// author-time gate) has always sequenced them, so the fold is
4621    /// byte-for-byte equivalent to the pre-fold three-block cascade at
4622    /// that call site (pinned by the per-arm
4623    /// `validate_upgrade_from_folds_per_entry_arm_matches_gate` /
4624    /// `_folds_versao_arm_matches_gate` / `_folds_behavior_arm_matches_gate`
4625    /// equivalence pins and by the cross-arm
4626    /// `validate_upgrade_from_per_entry_arm_fires_before_versao_arm` /
4627    /// `_versao_arm_fires_before_behavior_arm` ordering pins).
4628    ///
4629    /// Prior to this lift the three [`crate::upgrade`] top-level validators
4630    /// lived only open-coded at the layout wire-up site
4631    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4632    /// each threaded through the same `self.upgrade_from()` slice and each
4633    /// paired with the same [`crate::LayoutError::UpgradeViolation`]-wrap
4634    /// envelope: every future consumer that wanted to gate `:upgrade-from`
4635    /// as a whole — the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
4636    /// materializer's per-CR admission webhook re-checking `:upgrade-from`
4637    /// after a per-`(:from … :instructions …)` patch, a future `feira
4638    /// validate --upgrade` per-caixa admission verb, a per-`:upgrade-from`
4639    /// overlay resolver a per-cluster overlay lift would materialize —
4640    /// was structurally forced to either re-inline the three-dispatch
4641    /// cascade in lockstep with the layout wire-up (the duplication the
4642    /// PRIME DIRECTIVE names as a bug) or call the whole
4643    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4644    /// peer per-Caixa gate to re-check one slot. Post-fold each such
4645    /// consumer reaches the three-arm compound gate through one call on
4646    /// the substrate primitive.
4647    ///
4648    /// The three arms together name one contract with three axes:
4649    ///
4650    ///   - **per-entry + cross-entry graph-edge invariant** — every entry's
4651    ///     `:from` parses as SemVer-2 and every per-instruction / within-
4652    ///     entry ordering / singularity gate on each entry's
4653    ///     `:instructions` list passes, and no two entries share the same
4654    ///     parsed `:from` (the wasm-operator's OTP appup
4655    ///     `release_handler:install_release/1` analog picks at most one
4656    ///     matching block per running version — two entries with the same
4657    ///     parsed semver are an ambiguous edge in the typed upgrade graph).
4658    ///   - **cross-slot reachability invariant** — every entry's `:from`
4659    ///     is strictly less than the caixa's own `:versao` under SemVer-2
4660    ///     precedence. An entry whose `:from >= :versao` is structurally
4661    ///     unreachable by the operator's `:from`-match dispatch (the
4662    ///     operator loads the current `:versao` and matches the *running*
4663    ///     version against each entry's `:from`; an entry whose `:from >=
4664    ///     :versao` is never reached because the operator never runs a
4665    ///     version >= the current one that it could then upgrade *to* the
4666    ///     current one).
4667    ///   - **cross-slot composition invariant** — every entry carrying a
4668    ///     `(:state-change …)` instruction has a `:behavior
4669    ///     :on-state-change` callback declared on the same caixa. The
4670    ///     per-version migration script is the `gen_server:code_change/3`
4671    ///     analog and the runtime hook it is delivered through during hot
4672    ///     upgrade is the `:on-state-change` callback (the upgrade.rs
4673    ///     module doc pins the composition verbatim: "Composes with the
4674    ///     `:behavior :on-state-change` callback to deliver state migration
4675    ///     during hot upgrades").
4676    ///
4677    /// All three axes must hold together — every consumer's
4678    /// `:upgrade-from` accept-set past this compound gate is the same
4679    /// set the `feira build` author-time gate admits.
4680    ///
4681    /// The per-slot compound entry gate discipline lifted here onto the
4682    /// M2 `:upgrade-from` axis is the sibling of the peer per-kind
4683    /// compound entry gates ([`crate::render::require_supervisor_view`]
4684    /// / [`crate::render::require_aplicacao_view`] /
4685    /// [`crate::render::require_v0_servico_shape`]) that fold every
4686    /// per-kind cascade at the per-kind altitude, and of the peer
4687    /// per-slot compound gates ([`crate::AplicacaoSpec::validate_contratos`],
4688    /// [`crate::MeshPolicy::validate`],
4689    /// [`crate::SupervisorSpec::validate_children`]) that fold every
4690    /// structural axis on their slot onto one substrate primitive.
4691    /// Extended here to the last unlifted compound-cascade wire-up at
4692    /// the layout-pipeline altitude — the three-dispatch M2
4693    /// `:upgrade-from` cascade that lived only open-coded at the layout
4694    /// wire-up site.
4695    ///
4696    /// The per-instruction script-path on-disk existence-probe walk that
4697    /// [`crate::layout::StandardLayout::verify`] runs immediately after
4698    /// this gate (which resolves each entry's `:instructions
4699    /// (:state-change :script)` against the layout root) stays open-coded
4700    /// at the layout wire-up site — that arm needs the filesystem oracle
4701    /// on the [`crate::LayoutInvariants`] trait, not the pure per-Caixa
4702    /// typed-shape surface this compound gate folds. Same posture the
4703    /// peer [`Self::validate_code_paths`] takes on the sibling code-path
4704    /// axes: the typed-shape gate fires on the per-Caixa surface, the
4705    /// on-disk existence check fires on the [`crate::StandardLayout`]
4706    /// surface.
4707    ///
4708    /// # Errors
4709    ///
4710    /// Returns [`crate::UpgradeError::FromInvalid`] /
4711    /// [`crate::UpgradeError::ModuleEmpty`] /
4712    /// [`crate::UpgradeError::ModuleInvalid`] /
4713    /// [`crate::UpgradeError::EmptyScript`] /
4714    /// [`crate::UpgradeError::AbsoluteScript`] /
4715    /// [`crate::UpgradeError::ParentEscapeScript`] /
4716    /// [`crate::UpgradeError::NonLispExtensionScript`] /
4717    /// [`crate::UpgradeError::RestartNotExclusive`] /
4718    /// [`crate::UpgradeError::StateChangeWithoutPriorLoad`] /
4719    /// [`crate::UpgradeError::PurgeWithoutPriorLoad`] /
4720    /// [`crate::UpgradeError::StateChangeAfterCleanup`] /
4721    /// [`crate::UpgradeError::DuplicateLoadModule`] /
4722    /// [`crate::UpgradeError::DuplicateStateChange`] /
4723    /// [`crate::UpgradeError::DuplicateCleanup`] /
4724    /// [`crate::UpgradeError::DuplicateFrom`] on the per-entry +
4725    /// cross-entry axis; [`crate::UpgradeError::FromNotBeforeVersao`] on
4726    /// the cross-slot `:from ↔ :versao` axis;
4727    /// [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
4728    /// on the cross-slot `:state-change ↔ :on-state-change` axis.
4729    pub fn validate_upgrade_from(&self) -> Result<(), crate::UpgradeError> {
4730        crate::upgrade::validate_upgrade_from(self.upgrade_from())?;
4731        crate::upgrade::validate_upgrade_from_against_versao(self.upgrade_from(), self.versao())?;
4732        crate::upgrade::validate_upgrade_from_against_behavior(
4733            self.upgrade_from(),
4734            self.behavior(),
4735        )?;
4736        Ok(())
4737    }
4738
4739    /// Compound per-`Caixa` entry gate on the M2 `:limits` slot — folds
4740    /// the [`crate::LimitsSpec::validate`] four-axis cascade (`:memory`
4741    /// wasm32 zero-floor / below-page / above-cap / non-page-multiple;
4742    /// `:fuel` zero-floor / cap; `:wall-clock` zero-floor / cap; `:cpu`
4743    /// zero-floor / cap) onto one substrate primitive on [`Caixa`]. The
4744    /// `#[serde(default)]` absent-slot arm (`limits: None`, the
4745    /// canonical "no bound declared — engine-default applies" author
4746    /// shape [`crate::LimitsSpec::is_empty`]'s per-axis `None` cascade
4747    /// reads) is the fold's identity element and passes trivially; the
4748    /// present-slot arm (`limits: Some(l)`) dispatches to
4749    /// [`crate::LimitsSpec::validate`] verbatim, threading its per-axis
4750    /// [`crate::LimitsError`] Display through untouched.
4751    ///
4752    /// Prior to this lift the M2 `:limits` slot lived only wired
4753    /// open-coded at the layout wire-up site
4754    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4755    /// through the `if let Some(l) = caixa.limits() { l.validate() … }`
4756    /// three-line `Option::None → Ok(()) | Some(_) → …` unwrap-and-
4757    /// dispatch pattern paired with the same
4758    /// [`crate::LayoutError::LimitsViolation`]-wrap envelope: every
4759    /// future consumer that wanted to gate `:limits` as a whole — the
4760    /// deferred `caixa.pleme.io/v1alpha1/Caixa` CR materializer's
4761    /// per-CR admission webhook re-checking `:limits` after a per-
4762    /// `{:memory, :fuel, :wall-clock, :cpu}` patch (the exact case the
4763    /// [`Self::limits`] accessor docstring names as the second
4764    /// consumer of the slot), a future `feira validate --limits` per-
4765    /// caixa admission verb, a per-`:limits` overlay resolver a per-
4766    /// cluster `:limits-overrides` overlay lift would materialize — was
4767    /// structurally forced to either re-inline the two-line
4768    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4769    /// pattern in lockstep with the layout wire-up (the duplication the
4770    /// PRIME DIRECTIVE names as a bug) or call the whole
4771    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4772    /// peer per-Caixa gate ([`Self::validate_nome`],
4773    /// [`Self::validate_versao`], [`Self::validate_deps`],
4774    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4775    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4776    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4777    /// [`Self::validate_upgrade_from`], [`Self::validate_code_paths`],
4778    /// plus the per-kind `require_supervisor_view` /
4779    /// `require_aplicacao_view` gates, plus the on-disk existence
4780    /// walks) to re-check one slot. Post-lift each such consumer
4781    /// reaches the [`crate::LimitsSpec::validate`] four-axis cascade
4782    /// (and its identity-element on the absent slot) through one call
4783    /// on the substrate primitive.
4784    ///
4785    /// The per-slot compound entry-gate discipline lifted here onto the
4786    /// M2 `:limits` axis is the sibling of the peer per-slot compound
4787    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4788    /// [`crate::MeshPolicy::validate`],
4789    /// [`crate::SupervisorSpec::validate_children`],
4790    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`]) that
4791    /// fold every structural + cross-slot axis on their slot onto one
4792    /// substrate primitive. Extended here to the M2 `:limits` slot, the
4793    /// first of the two M2 typed slots (`:limits`, `:behavior`) whose
4794    /// per-Caixa compound-gate wire-up still lived open-coded at the
4795    /// layout altitude after the [`Self::validate_upgrade_from`] lift
4796    /// (d6801df) closed the sibling M2 slot's cascade.
4797    ///
4798    /// # Errors
4799    ///
4800    /// Returns every [`crate::LimitsError`] variant on the present-slot
4801    /// arm — verbatim from [`crate::LimitsSpec::validate`]. Passes
4802    /// trivially on the absent-slot arm (`limits: None`, the fold's
4803    /// identity element).
4804    pub fn validate_limits(&self) -> Result<(), crate::LimitsError> {
4805        match self.limits() {
4806            Some(l) => l.validate(),
4807            None => Ok(()),
4808        }
4809    }
4810
4811    /// Compound per-`Caixa` entry gate on the M2 `:behavior` slot's
4812    /// pure typed-shape surface — folds the
4813    /// [`crate::BehaviorSpec::validate`] six-slot value-shape cascade
4814    /// (each declared `:on-init` / `:on-call` / `:on-cast` / `:on-info`
4815    /// / `:on-state-change` / `:on-terminate` callback-path is
4816    /// non-empty / relative / no-`..`-parent-escape / terminating-
4817    /// `.lisp`-extension, routed through the shared
4818    /// [`crate::render::require_sandboxed_lisp_path`] arm-set) onto one
4819    /// substrate primitive on [`Caixa`]. The `#[serde(default)]`
4820    /// absent-slot arm (`behavior: None`, the canonical "no callback
4821    /// declared — the runtime falls back to the wasm-engine's default
4822    /// callback per arm" author shape [`crate::BehaviorSpec::is_empty`]'s
4823    /// per-slot `None` cascade reads) is the fold's identity element
4824    /// and passes trivially; the present-slot arm (`behavior: Some(b)`)
4825    /// dispatches to [`crate::BehaviorSpec::validate`] verbatim,
4826    /// threading its per-slot [`crate::BehaviorError`] Display through
4827    /// untouched.
4828    ///
4829    /// Scope note — the on-disk callback-path existence walk paired
4830    /// with the value-shape gate at
4831    /// [`crate::layout::StandardLayout::verify`] stays open-coded at
4832    /// the layout altitude, because it needs the
4833    /// [`crate::layout::LayoutInvariants`] filesystem oracle
4834    /// ([`crate::layout::LayoutInvariants::exists`]) that the pure
4835    /// per-Caixa typed-shape surface this compound gate folds onto has
4836    /// no reference to. Same posture the peer M2 `:upgrade-from`
4837    /// per-Caixa compound gate ([`Self::validate_upgrade_from`]
4838    /// d6801df) already carries: the pure typed-shape surface folds
4839    /// onto the substrate primitive; the per-instruction script-path
4840    /// existence probe on the paired axis (there `:state-change
4841    /// :script`; here `:on-*`) stays at the layout altitude.
4842    ///
4843    /// Prior to this lift the pure value-shape surface of the M2
4844    /// `:behavior` slot lived only wired open-coded at the layout
4845    /// wire-up site ([`crate::layout::StandardLayout::verify`],
4846    /// caixa-core/src/layout.rs), through the
4847    /// `if let Some(b) = caixa.behavior() { b.validate() … }`
4848    /// unwrap-and-dispatch pattern paired with the same
4849    /// [`crate::LayoutError::BehaviorViolation`]-wrap envelope: every
4850    /// future consumer that wanted to gate the `:behavior` slot's
4851    /// value-shape as a whole — the deferred
4852    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4853    /// admission webhook re-checking `:behavior` after a per-`{:on-init,
4854    /// :on-call, :on-cast, :on-info, :on-state-change, :on-terminate}`
4855    /// patch (the exact case the peer `:on-*` accessor docstrings on
4856    /// [`crate::BehaviorSpec`] already name as deferred consumers of
4857    /// the slot), a future `feira validate --behavior` per-caixa
4858    /// admission verb, a per-`:behavior` overlay resolver a future
4859    /// per-cluster callback-overlay lift would materialize — was
4860    /// structurally forced to either re-inline the two-line
4861    /// `Option::None → Ok(()) | Some(_) → …` unwrap-and-dispatch
4862    /// pattern in lockstep with the layout wire-up (the duplication the
4863    /// PRIME DIRECTIVE names as a bug) or call the whole
4864    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
4865    /// peer per-Caixa gate ([`Self::validate_nome`],
4866    /// [`Self::validate_versao`], [`Self::validate_deps`],
4867    /// [`Self::validate_etiquetas`], [`Self::validate_autores`],
4868    /// [`Self::validate_repositorio`], [`Self::validate_descricao`],
4869    /// [`Self::validate_licenca`], [`Self::validate_edicao`],
4870    /// [`Self::validate_limits`], [`Self::validate_upgrade_from`],
4871    /// [`Self::validate_code_paths`], plus the per-kind
4872    /// `require_supervisor_view` / `require_aplicacao_view` gates, plus
4873    /// the on-disk existence walks) to re-check one slot. Post-lift
4874    /// each such consumer reaches the [`crate::BehaviorSpec::validate`]
4875    /// six-slot cascade (and its identity-element on the absent slot)
4876    /// through one call on the substrate primitive.
4877    ///
4878    /// The per-slot compound entry-gate discipline lifted here onto the
4879    /// M2 `:behavior` axis is the sibling of the peer per-slot compound
4880    /// gates ([`crate::AplicacaoSpec::validate_contratos`],
4881    /// [`crate::MeshPolicy::validate`],
4882    /// [`crate::SupervisorSpec::validate_children`],
4883    /// [`Self::validate_upgrade_from`], [`Self::validate_deps`],
4884    /// [`Self::validate_limits`]) that fold every structural + cross-
4885    /// slot axis on their slot onto one substrate primitive. Extended
4886    /// here to the M2 `:behavior` slot, the last of the four M2 typed
4887    /// slots (`:limits`, `:behavior`, `:upgrade-from`, plus the
4888    /// supervisor-only `:children` peer) whose per-Caixa compound-gate
4889    /// wire-up still lived open-coded at the layout altitude after the
4890    /// [`Self::validate_limits`] lift (baa4688) closed the sibling M2
4891    /// `:limits` slot's cascade. With this lift the "one named per-slot
4892    /// / per-Caixa compound gate per typed slot folding every structural
4893    /// axis on that slot (plus the `Option::None` identity element for
4894    /// the `Option`-shaped slots) onto one substrate primitive"
4895    /// discipline spans every M2 typed slot uniformly, so a reader who
4896    /// has learned any peer M2 gate reads `:behavior` without a per-
4897    /// slot exception carve-out.
4898    ///
4899    /// # Errors
4900    ///
4901    /// Returns every [`crate::BehaviorError`] variant on the present-
4902    /// slot arm — verbatim from [`crate::BehaviorSpec::validate`].
4903    /// Passes trivially on the absent-slot arm (`behavior: None`, the
4904    /// fold's identity element).
4905    pub fn validate_behavior(&self) -> Result<(), crate::BehaviorError> {
4906        match self.behavior() {
4907            Some(b) => b.validate(),
4908            None => Ok(()),
4909        }
4910    }
4911
4912    /// Reject `:restart-window` values the shared
4913    /// [`crate::supervisor::duration_codec::parse`] refuses. The flat
4914    /// `restart_window: Option<String>` slot on [`Caixa`] is stored
4915    /// raw by the derive macro (the typed [`SupervisorSpec`] holds an
4916    /// `Option<Duration>` routed through the shared codec via `with =
4917    /// "duration_codec"`); the inline `Caixa → SupervisorSpec`
4918    /// view-construction path ([`Self::supervisor_view`]) folds the
4919    /// raw string through the same shared codec and soft-swallows the
4920    /// parse error as `None` to keep the view best-effort. Without
4921    /// this gate a malformed `:restart-window` (`"1.5s"` — the
4922    /// fractional-seconds drift class; `"1.0s"` — the decimal-shaped
4923    /// integer drift; `"0.5m"` — the unit-fraction drift; `"+30s"` /
4924    /// `"-30s"` — the leading-sign drift; `"30x"` — the unknown-unit
4925    /// footgun; `"abc"` — pure garbage; `""` — the empty-after-trim
4926    /// edge case) silently produced a `SupervisorSpec` with
4927    /// `restart_window: None`, indistinguishable from the canonical
4928    /// "omit the slot to express no reset" authoring shape — Erlang/OTP's
4929    /// `MaxIntensity / Period` invariant turns into a never-reset
4930    /// supervisor far from the source `caixa.lisp`, with no field
4931    /// naming the offending `:restart-window`. Lifting the gate to a
4932    /// Caixa-level validator mirrors the trajectory of the peer
4933    /// per-axis identity gates ([`Self::validate_nome`] 6c992f8,
4934    /// [`Self::validate_versao`] 1fdaa02, [`Self::validate_deps`]
4935    /// a7f0d8c) and the ABSORPTION-ROADMAP.md M2.2 test pin
4936    /// (line 196: "reject invalid `:restart-window` (non-duration)").
4937    ///
4938    /// Thin wrapper around [`crate::supervisor::duration_codec::parse`]
4939    /// (the shared codec backing `:supervisor :restart-window` as
4940    /// serde-routed on [`SupervisorSpec`], `:politicas :timeout`, and
4941    /// `:politicas :circuit-breaker :window` — all three covered by
4942    /// the integer-magnitude gate 1c55a2a). Maps the codec's parse
4943    /// error verbatim into the [`ManifestError::RestartWindowMalformed`]
4944    /// variant, carrying the offending raw string + a parser-shaped
4945    /// reason naming the canonical authoring form, so the diagnostic
4946    /// is self-locating (the author can grep their `caixa.lisp` for
4947    /// `:restart-window "<value>"` and fix it in one edit) and
4948    /// uniform with every other manifest-level validate diagnostic.
4949    /// With this gate the four `:restart-window`-shaped surfaces (the
4950    /// flat raw string on [`Caixa`], the typed `Option<Duration>` on
4951    /// [`SupervisorSpec`], the two `MeshPolicy` peer durations) are
4952    /// now structurally equivalent — every value past the codec is in
4953    /// one accepted set, by construction.
4954    ///
4955    /// `None` (the canonical "omit the slot to express no reset"
4956    /// shape) is accepted trivially — the gate is a no-op when the
4957    /// author didn't author a window. The empty string is rejected by
4958    /// the shared codec (its digit-only gate refuses an empty
4959    /// magnitude), surfacing the same `RestartWindowMalformed`
4960    /// diagnostic as every other rejected non-canonical shape.
4961    pub fn validate_restart_window(&self) -> Result<(), ManifestError> {
4962        let Some(s) = self.restart_window() else {
4963            return Ok(());
4964        };
4965        crate::supervisor::duration_codec::parse(s)
4966            .map(|_| ())
4967            .map_err(|reason| ManifestError::RestartWindowMalformed {
4968                restart_window: s.to_string(),
4969                reason,
4970            })
4971    }
4972
4973    /// Compound per-`Caixa` entry gate on the Aplicacao-kind mesh-slot
4974    /// family — folds the paired [`crate::AplicacaoSpec::validate`]
4975    /// typed-shape cascade (per-slot gates on `:membros`, `:contratos`,
4976    /// `:entrada`, `:placement`, `:politicas`, in that declared order)
4977    /// plus the cross-slot self-edge gate
4978    /// ([`crate::aplicacao::validate_no_self_membership`], the
4979    /// `:membros :caixa` ≠ `:nome` invariant the typed view cannot
4980    /// enforce on its own because it carries the membros but not the
4981    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
4982    /// non-Aplicacao kinds the fold is the identity element — the paired
4983    /// [`Self::aplicacao_view`] accessor returns `None` off the
4984    /// Aplicacao arm (peer with the [`Self::validate_limits`] /
4985    /// [`Self::validate_behavior`] M2 `Option`-arm identity element),
4986    /// so the gate passes trivially without touching the mesh slots.
4987    ///
4988    /// Prior to this lift the paired cascade lived only wired open-coded
4989    /// at the layout wire-up site
4990    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
4991    /// as the three-line `let view = caixa.aplicacao_view().expect(...);
4992    /// view.validate() … validate_no_self_membership(...) …` pattern
4993    /// paired with two `.map_err(|err| LayoutError::AplicacaoViolation
4994    /// { caixa, issue })` wraps — every future consumer that wanted to
4995    /// gate the Aplicacao-shape cascade as a whole (the deferred
4996    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's per-CR
4997    /// admission webhook re-checking `:membros` / `:contratos` after a
4998    /// per-slot patch, a future `feira validate --aplicacao` per-caixa
4999    /// admission verb, a per-Aplicacao overlay resolver) was structurally
5000    /// forced to either re-inline the two-dispatch cascade in lockstep
5001    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5002    /// names as a bug) or call the whole
5003    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5004    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5005    /// such consumer reaches the two-arm compound gate through one call
5006    /// on the substrate primitive.
5007    ///
5008    /// Peer to the [`crate::render::require_aplicacao_view`] compound
5009    /// entry gate every per-Aplicacao *renderer* routes through
5010    /// (3aefefb folded `validate_no_self_membership` onto the renderer
5011    /// path) — this gate mirrors the same fold on the *layout* path, so
5012    /// the two consumers of the Aplicacao-shape cascade (the author-time
5013    /// gate and every per-Aplicacao renderer) share one substrate
5014    /// primitive rather than two open-coded cascades kept in lockstep.
5015    /// Same lift discipline the peer per-slot compound gates
5016    /// ([`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5017    /// b5dd55e, [`Self::validate_limits`] baa4688,
5018    /// [`Self::validate_behavior`] 0d2877a) each carry.
5019    ///
5020    /// # Errors
5021    ///
5022    /// Returns every [`crate::AplicacaoError`] variant on the present-
5023    /// kind arm — the typed-shape cascade's per-slot arms first
5024    /// (matching [`crate::AplicacaoSpec::validate`]'s declared order),
5025    /// then the cross-slot self-edge arm
5026    /// ([`crate::AplicacaoError::MembroIsSelfAplicacao`]). Passes
5027    /// trivially on non-Aplicacao kinds (the fold's identity element).
5028    pub fn validate_aplicacao_shape(&self) -> Result<(), crate::AplicacaoError> {
5029        let Some(view) = self.aplicacao_view() else {
5030            return Ok(());
5031        };
5032        view.validate()?;
5033        crate::aplicacao::validate_no_self_membership(self.membros(), self.nome())?;
5034        Ok(())
5035    }
5036
5037    /// Compound per-`Caixa` entry gate on the Supervisor-kind
5038    /// supervision-tree slot family — folds the paired
5039    /// [`crate::SupervisorSpec::validate`] typed-shape cascade
5040    /// (`:estrategia` ↔ `:children` invariants, `:max-restarts` /
5041    /// `:restart-window` bounds, per-child DNS-1123 `:caixa` names,
5042    /// semver-valid `:versao` constraints, the set-not-multiset
5043    /// duplicate-child gate) plus the cross-slot self-edge gate
5044    /// ([`crate::supervisor::validate_no_self_supervision`], the
5045    /// `:children :caixa` ≠ `:nome` invariant the typed view cannot
5046    /// enforce on its own because it carries the children but not the
5047    /// parent `:nome`) onto one substrate primitive on [`Caixa`]. On
5048    /// non-Supervisor kinds the fold is the identity element — the paired
5049    /// [`Self::supervisor_view`] accessor returns `None` off the
5050    /// Supervisor arm (peer with the [`Self::validate_limits`] /
5051    /// [`Self::validate_behavior`] M2 `Option`-arm identity element and
5052    /// the sibling per-Aplicacao [`Self::validate_aplicacao_shape`]),
5053    /// so the gate passes trivially without touching the supervision-tree
5054    /// slots.
5055    ///
5056    /// Prior to this lift the paired cascade lived only wired open-coded
5057    /// at the layout wire-up site
5058    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/layout.rs),
5059    /// as the three-line `let view = caixa.supervisor_view().expect(...);
5060    /// view.validate() … validate_no_self_supervision(...) …` pattern
5061    /// paired with two `.map_err(|err| LayoutError::SupervisorViolation
5062    /// { caixa, issue })` wraps — every future consumer that wanted to
5063    /// gate the Supervisor-shape cascade as a whole (the wasm-operator's
5064    /// hierarchical reconciliation scheduler re-checking `:children` /
5065    /// `:estrategia` after a per-slot patch, the M4
5066    /// `mesh.pleme.io/v1alpha1/Supervisor` CR materializer's admission
5067    /// webhook, a future `feira validate --supervisor` per-caixa
5068    /// admission verb, a per-Supervisor overlay resolver) was structurally
5069    /// forced to either re-inline the two-dispatch cascade in lockstep
5070    /// with the layout wire-up (the duplication the PRIME DIRECTIVE
5071    /// names as a bug) or call the whole
5072    /// [`crate::layout::StandardLayout::verify`] pipeline and pay every
5073    /// peer per-Caixa gate to re-check one slot family. Post-fold each
5074    /// such consumer reaches the two-arm compound gate through one call
5075    /// on the substrate primitive.
5076    ///
5077    /// Peer to the [`crate::render::require_supervisor_view`] compound
5078    /// entry gate every per-Supervisor *renderer* would route through
5079    /// (which already folds the same `spec.validate()` +
5080    /// `validate_no_self_supervision` two-arm cascade behind its
5081    /// `require_kind` + `validate_restart_window` prelude) — this gate
5082    /// mirrors the same fold on the *layout* path, so the two consumers
5083    /// of the Supervisor-shape cascade (the author-time gate and every
5084    /// per-Supervisor renderer) share one substrate primitive rather
5085    /// than two open-coded cascades kept in lockstep. Same lift
5086    /// discipline the peer per-slot compound gates
5087    /// ([`Self::validate_aplicacao_shape`] 949a7a0,
5088    /// [`Self::validate_upgrade_from`] d6801df,
5089    /// [`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5090    /// baa4688, [`Self::validate_behavior`] 0d2877a) each carry.
5091    ///
5092    /// # Errors
5093    ///
5094    /// Returns every [`crate::SupervisorError`] variant on the present-
5095    /// kind arm — the typed-shape cascade's per-slot arms first
5096    /// (matching [`crate::SupervisorSpec::validate`]'s declared order),
5097    /// then the cross-slot self-edge arm
5098    /// ([`crate::SupervisorError::ChildSupervisesSelf`]). Passes
5099    /// trivially on non-Supervisor kinds (the fold's identity element).
5100    pub fn validate_supervisor_shape(&self) -> Result<(), crate::SupervisorError> {
5101        let Some(view) = self.supervisor_view() else {
5102            return Ok(());
5103        };
5104        view.validate()?;
5105        crate::supervisor::validate_no_self_supervision(self.children(), self.nome())?;
5106        Ok(())
5107    }
5108
5109    /// Compound per-`Caixa` entry gate on the Acao-kind `:ci` slot
5110    /// family — folds the [`crate::decompose_ci`] typed decompose gate
5111    /// (`canteiro_types::decompose` refusing every illegal
5112    /// [`canteiro_types::CiRun`] shape: duplicate node name, dependency
5113    /// on an undeclared node, dependency cycle) onto one substrate
5114    /// primitive on [`Caixa`]. On non-`Acao` kinds the fold is the
5115    /// identity element — the paired [`Self::kind`] `is_acao()` guard
5116    /// short-circuits before the decompose gate ever fires (peer with
5117    /// the [`Self::validate_aplicacao_shape`] /
5118    /// [`Self::validate_supervisor_shape`] typed-view identity element
5119    /// and the [`Self::validate_limits`] / [`Self::validate_behavior`]
5120    /// M2 `Option`-arm identity element), so the gate passes trivially
5121    /// without touching the `:ci` slot. An `:kind Acao` caixa with
5122    /// `ci = None` is also an identity-element pass: the presence gate
5123    /// is the sibling axis owned by [`crate::LayoutError::MissingCi`] /
5124    /// [`crate::require_ci`] / [`crate::MissingCiSlot`], not by the
5125    /// decompose gate — a caixa that carries no `:ci` slot has no run
5126    /// to decompose. Same split the peer per-Servico
5127    /// [`crate::LayoutError::ServicoWithoutServicos`] presence gate and
5128    /// per-Binario [`crate::LayoutError::BinarioWithoutExe`] presence
5129    /// gate keep from their sibling per-slot shape gates, so the two
5130    /// axes stay separately diagnosable at the layout altitude.
5131    ///
5132    /// Prior to this lift the decompose gate lived only wired
5133    /// open-coded at the [`caixa_actions::validate`] renderer-side
5134    /// entry gate (routed through the substrate-canonical
5135    /// [`crate::require_acao_view`] compound helper) — the *layout*
5136    /// pipeline ([`crate::layout::StandardLayout::verify`], caixa-core/
5137    /// src/layout.rs) only checked `:ci` *presence* via
5138    /// [`crate::LayoutError::MissingCi`], so a `:kind Acao` caixa
5139    /// carrying a structurally illegal `:ci` (a duplicate node name, a
5140    /// dependency on an undeclared node, a dependency cycle) passed
5141    /// `feira build` cleanly and surfaced the diagnostic only when
5142    /// [`caixa_actions::validate`] later refused it — far from the
5143    /// source `caixa.lisp` on the author-time gate side. Every future
5144    /// consumer that wanted to gate the Acao-shape cascade as a whole
5145    /// (a per-`Acao` CR materializer's admission webhook re-checking
5146    /// `:ci` after a per-node patch, a future `feira validate --acao`
5147    /// per-caixa admission verb, a per-`Acao` overlay resolver
5148    /// rejecting an added / renamed node against a cluster-local
5149    /// snapshot) was structurally forced to either re-inline the
5150    /// decompose dispatch in lockstep with the renderer-side wire-up
5151    /// (the duplication the PRIME DIRECTIVE names as a bug) or call
5152    /// the whole [`caixa_actions::validate`] renderer and pay the
5153    /// per-node accumulation to re-check one slot. Post-fold each such
5154    /// consumer reaches the decompose gate through one call on the
5155    /// substrate primitive.
5156    ///
5157    /// Peer to the [`crate::require_acao_view`] compound entry gate
5158    /// every per-`Acao` *renderer* routes through (which already folds
5159    /// the same `require_ci + decompose_ci` two-arm cascade behind its
5160    /// `require_kind` prelude) — this gate mirrors the same fold on
5161    /// the *layout* path, so the two consumers of the Acao-shape
5162    /// cascade (the author-time gate and every per-`Acao` renderer)
5163    /// share one substrate primitive rather than two open-coded
5164    /// cascades kept in lockstep. Same lift discipline the peer
5165    /// per-kind compound gates ([`Self::validate_aplicacao_shape`]
5166    /// 949a7a0, [`Self::validate_supervisor_shape`] 4c70105,
5167    /// [`Self::validate_upgrade_from`] d6801df, [`Self::validate_deps`]
5168    /// b5dd55e, [`Self::validate_limits`] baa4688,
5169    /// [`Self::validate_behavior`] 0d2877a) each carry. Closes the
5170    /// last per-kind asymmetry: with this lift the four typed
5171    /// named-caixa kinds (`Servico` / `Aplicacao` / `Supervisor` /
5172    /// `Acao`) each carry a compound per-`Caixa` shape gate on the
5173    /// substrate, and the layout pipeline routes through the same one
5174    /// substrate primitive per kind rather than four open-coded
5175    /// cascades.
5176    ///
5177    /// # Errors
5178    ///
5179    /// Returns the [`crate::CiDecomposeFailure`] typed view on the
5180    /// present-slot arm — the caixa's `:nome` alongside the borrowed
5181    /// [`canteiro_types::DecomposeError`] source (`DuplicateNode` /
5182    /// `UnknownDep` / `Cycle`) verbatim, so a consumer that fans on
5183    /// the specific arm reaches for `err.source` directly rather than
5184    /// re-parsing the Display bytes. Passes trivially on non-`Acao`
5185    /// kinds and on `:kind Acao` caixas with absent `:ci` (the fold's
5186    /// two identity-element arms).
5187    pub fn validate_acao_shape(&self) -> Result<(), crate::CiDecomposeFailure> {
5188        if !self.kind().is_acao() {
5189            return Ok(());
5190        }
5191        let Some(ci) = self.ci() else {
5192            return Ok(());
5193        };
5194        crate::render::decompose_ci(self, ci).map(|_| ())
5195    }
5196
5197    /// Compound per-`Caixa` kind ↔ typed-slot coherence gate on the
5198    /// three "declared but ignored" typed-slot families — M3 mesh
5199    /// (`:membros` / `:contratos` / `:politicas` / `:placement` /
5200    /// `:entrada`, owned by `:kind Aplicacao`, MESH-COMPOSITION §III.1),
5201    /// supervisor-tree (`:estrategia` / `:max-restarts` /
5202    /// `:restart-window` / `:children`, owned by `:kind Supervisor`,
5203    /// INSPIRATIONS §II.2), and M2 Servico-runtime (`:limits` /
5204    /// `:behavior` / `:upgrade-from`, owned by `:kind Servico`,
5205    /// INSPIRATIONS §III.1 / §II.3 / §II.4). Folds the three sibling
5206    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5207    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5208    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-coherence
5209    /// gates — each pre-lift a self-similar five-line
5210    /// `if !caixa.kind().is_<owner>() { let slots = caixa.declared_
5211    /// <family>_slots(); if !slots.is_empty() { return
5212    /// Err(LayoutError::<family>_on_non_<owner>(caixa, slots)); } }`
5213    /// block at [`crate::layout::StandardLayout::verify`] — onto one
5214    /// substrate primitive on [`Caixa`]. Every arm passes as an
5215    /// identity element on the owner kind (the paired
5216    /// [`Self::kind`] `is_<owner>()` guard short-circuits before the
5217    /// per-family `declared_*_slots` gate fires) and on non-owner
5218    /// kinds carrying no declared slot in that family (the
5219    /// [`Vec::is_empty`] check short-circuits before the wrap fires),
5220    /// so a bare no-code caixa on any kind passes the fold trivially
5221    /// on all three arms.
5222    ///
5223    /// Prior to this lift the three-arm cascade lived only wired
5224    /// open-coded at the layout wire-up site
5225    /// ([`crate::layout::StandardLayout::verify`], caixa-core/src/
5226    /// layout.rs) as three self-similar five-line blocks paired with
5227    /// three [`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5228    /// [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5229    /// [`crate::LayoutError::servico_slots_on_non_servico`] ctor
5230    /// dispatches (each of which the peer
5231    /// [`crate::layout::layout_slot_kind_ctors!`] macro already folds
5232    /// onto one substrate primitive per typed variant, 0419438) —
5233    /// every future consumer that wanted to gate the whole
5234    /// kind-coherence cascade as a unit (the deferred
5235    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5236    /// webhook re-checking every typed-slot family after a per-slot
5237    /// patch, a future `feira validate --kind-coherence` per-caixa
5238    /// admission verb, a per-`Caixa` overlay resolver rejecting a
5239    /// kind-foreign patch against a cluster-local snapshot) was
5240    /// structurally forced to either re-inline the three-block
5241    /// cascade in lockstep with the layout wire-up (the duplication
5242    /// the PRIME DIRECTIVE names as a bug) or call the whole
5243    /// [`crate::layout::StandardLayout::verify`] pipeline and pay
5244    /// every peer per-`Caixa` gate to re-check three slot families.
5245    /// Post-fold each such consumer reaches the three-arm cascade
5246    /// through one call on the substrate primitive.
5247    ///
5248    /// Diagnostic order matches the pre-fold layout wire-up
5249    /// canonical sequence — mesh → supervisor → servico — pinned by
5250    /// the load-bearing
5251    /// `validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm`
5252    /// / `_supervisor_arm_fires_before_servico_arm` ordering pins
5253    /// below. The three arms enumerate every typed-slot family the
5254    /// substrate carries whose "declared but ignored" footgun is
5255    /// gated at the layout altitude by a `{ caixa, kind, slots }`
5256    /// wrap variant — the peer
5257    /// [`crate::LayoutError::ForeignCodeSlot`] gate on the
5258    /// code-surface family sits outside this fold because
5259    /// [`Self::declared_foreign_code_slots`] bakes the kind-check
5260    /// into the helper (so the layout wire-up carries no outer
5261    /// `if !caixa.kind().is_<owner>()` guard), and the peer
5262    /// [`crate::LayoutError::CiOnNonAcao`] gate on the `:ci` axis
5263    /// carries a distinct `{ caixa, kind }` wrap shape (no `slots`
5264    /// field — `:ci` is a single `Option` not a `Vec`-of-named-slots)
5265    /// and rides on its own peer substrate primitive
5266    /// [`Self::validate_ci_kind_coherence`] (the direct sibling to
5267    /// this fold on the `:ci` axis) — the two folds share the same
5268    /// altitude and diagnostic order at the layout wire-up site but
5269    /// keep their distinct envelope shapes, so no consumer of
5270    /// `CiOnNonAcao` sees a variant rename.
5271    ///
5272    /// Peer to the per-kind compound entry gates every substrate
5273    /// primitive on the M2/M3 typed-slot family already carries
5274    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5275    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5276    /// [`Self::validate_upgrade_from`] d6801df,
5277    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5278    /// [`Self::validate_supervisor_shape`] 4c70105,
5279    /// [`Self::validate_acao_shape`] 5d6df54): the author-time gate
5280    /// axis on the *per-slot* algebra now shares one substrate
5281    /// primitive per compound gate, and this lift closes the
5282    /// symmetric axis on the *cross-family* kind ↔ slot coherence
5283    /// algebra so the layout pipeline routes the three self-similar
5284    /// gates through one substrate primitive rather than three
5285    /// open-coded blocks. Every future kind that adds its own
5286    /// exclusive typed-slot family (an `Actor`-owned per-virtual-
5287    /// actor grain slot the M5 Orleans-inspired kind reaches
5288    /// through, a per-Aplicacao overlay slot the M4 CR materializer
5289    /// consults) folds onto this compound gate as one arm addition
5290    /// rather than a fourth open-coded block at the wire-up site.
5291    ///
5292    /// # Errors
5293    ///
5294    /// Returns the first [`crate::LayoutError`] variant surfacing
5295    /// under the canonical mesh → supervisor → servico order:
5296    /// [`crate::LayoutError::MeshSlotsOnNonAplicacao`] on a non-
5297    /// Aplicacao caixa with a declared M3 mesh slot,
5298    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] on a
5299    /// non-Supervisor caixa with a declared supervisor-tree slot,
5300    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] on a
5301    /// non-Servico caixa with a declared M2 slot. Passes trivially
5302    /// on the owner kind of each family and on non-owner kinds
5303    /// carrying no declared slot in that family (the fold's identity
5304    /// element on both axes).
5305    pub fn validate_kind_slot_coherence(&self) -> Result<(), crate::LayoutError> {
5306        // Each of the three arms routes through the shared
5307        // [`Self::run_kind_owned_slot_family_gate`] substrate primitive
5308        // — the outer non-owner-kind guard + inner accumulator + inner
5309        // emptiness-guard + wrap arm shape now lands on one dispatch
5310        // per family rather than a four-line open-coded block in
5311        // lockstep across all three arms. Canonical mesh → supervisor
5312        // → servico order preserved (the primitive short-circuits
5313        // arm-by-arm; the outer `?;` cascade at this altitude threads
5314        // the first surfaced arm's error verbatim). Each of the three
5315        // ctors ([`crate::LayoutError::mesh_slots_on_non_aplicacao`] /
5316        // [`crate::LayoutError::supervisor_slots_on_non_supervisor`] /
5317        // [`crate::LayoutError::servico_slots_on_non_servico`]) was
5318        // already lifted onto the substrate by the peer
5319        // [`crate::layout::layout_slot_kind_ctors!`] macro, so each arm
5320        // routes through the same substrate-canonical
5321        // `Self::<variant> { caixa, kind, slots }` wrap per arm as the
5322        // pre-lift open-coded blocks — byte-equal, pinned by the
5323        // paired `validate_kind_slot_coherence_folds_<family>_arm_matches_gate`
5324        // equivalence pins and the peer
5325        // `validate_kind_slot_coherence_{mesh,supervisor}_arm_fires_before_<next>_arm`
5326        // ordering pins.
5327        self.run_kind_owned_slot_family_gate(
5328            crate::CaixaKind::is_aplicacao,
5329            Caixa::declared_mesh_slots,
5330            crate::LayoutError::mesh_slots_on_non_aplicacao,
5331        )?;
5332        self.run_kind_owned_slot_family_gate(
5333            crate::CaixaKind::is_supervisor,
5334            Caixa::declared_supervisor_slots,
5335            crate::LayoutError::supervisor_slots_on_non_supervisor,
5336        )?;
5337        self.run_kind_owned_slot_family_gate(
5338            crate::CaixaKind::is_servico,
5339            Caixa::declared_servico_slots,
5340            crate::LayoutError::servico_slots_on_non_servico,
5341        )?;
5342        Ok(())
5343    }
5344
5345    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on
5346    /// the three no-code kinds — `Supervisor` (supervises other
5347    /// caixas, INSPIRATIONS §II.2), `Aplicacao` (composes Servicos,
5348    /// MESH-COMPOSITION §III.1), and `Acao` (owns a typed CI run,
5349    /// CANTEIRO §7.1-C). Each carries no code of its own, so
5350    /// declaring any of `:bibliotecas` / `:exe` / `:servicos`
5351    /// silently passes the layout's path-existence loops (the paths
5352    /// still resolve on disk) and then vanishes downstream — the
5353    /// per-kind renderers gate emission on
5354    /// [`crate::render::require_kind`] and only emit the code
5355    /// surface for its owning kind, so a declared code slot on a
5356    /// no-code kind is the manifest field's documented "ignored
5357    /// otherwise" footgun.
5358    ///
5359    /// Pre-lift each of the three arms lived as a self-similar
5360    /// `if !caixa.kind().is_<no-code-kind>() { … } else if has_code
5361    /// { return Err(LayoutError::<kind>_owns_code(caixa)); }` block
5362    /// at [`crate::layout::StandardLayout::verify`] — three
5363    /// consumers, three identical shapes. Every future consumer
5364    /// that wanted to gate the whole code-surface coherence cascade
5365    /// as a unit (the deferred `caixa.pleme.io/v1alpha1/Caixa` CR
5366    /// materializer's admission webhook re-checking after a
5367    /// per-slot patch, a future `feira validate --no-code-kind`
5368    /// per-caixa admission verb, a per-`Caixa` overlay resolver
5369    /// rejecting a kind-foreign patch) was structurally forced to
5370    /// either re-inline the three-block cascade in lockstep with
5371    /// the layout wire-up (the duplication the PRIME DIRECTIVE
5372    /// names as a bug) or call the whole
5373    /// [`crate::layout::StandardLayout::verify`] pipeline. Post-fold
5374    /// each such consumer reaches the three-arm cascade through
5375    /// one call.
5376    ///
5377    /// Mirror of the sibling [`Self::validate_kind_slot_coherence`]
5378    /// fold (f0d286e) on the author-time typed-slot coherence axis:
5379    /// that gate closes the "non-owner kind declares owner-only
5380    /// typed slots" three-arm cascade on the M2 / supervisor-tree /
5381    /// M3 slot families; this gate closes the reciprocal
5382    /// "no-code kind declares code" three-arm cascade on the
5383    /// `:bibliotecas` / `:exe` / `:servicos` code surface. Together
5384    /// the two folds route every kind ↔ author-shape coherence
5385    /// diagnostic at the layout altitude through one substrate
5386    /// primitive per axis.
5387    ///
5388    /// The gate carries two identity elements:
5389    /// - **`has_code == false`** — any kind (including the three
5390    ///   no-code kinds) that declares no code passes the paired
5391    ///   `!has_code` short-circuit before every per-arm dispatch.
5392    /// - **Code-owning kinds** (`Biblioteca` owning
5393    ///   `:bibliotecas`, `Binario` owning `:exe`, `Servico` owning
5394    ///   `:servicos`) — the three no-code arm-firing predicates
5395    ///   short-circuit on every code-owning kind, so the gate
5396    ///   passes trivially regardless of what code they declare.
5397    ///   Foreign-code-slot violations on a code-owning kind (e.g.
5398    ///   `:kind Servico` declaring `:exe`) surface through the
5399    ///   sibling [`crate::LayoutError::ForeignCodeSlot`] gate on
5400    ///   [`Self::declared_foreign_code_slots`], not through this
5401    ///   gate.
5402    ///
5403    /// Unlike the sibling cross-family
5404    /// [`Self::validate_kind_slot_coherence`], the three arms of
5405    /// this fold are mutually exclusive by construction — `:kind`
5406    /// is a single-valued [`CaixaKind`] discriminator so at most
5407    /// one arm can fire per caixa — and no cross-arm ordering pin
5408    /// is meaningful (the pre-fold three-block cascade at the
5409    /// wire-up site was already unreachable past the first
5410    /// matching arm).
5411    ///
5412    /// Peer to the per-kind compound entry gates every substrate
5413    /// primitive on the M2/M3 typed-slot family already carries
5414    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5415    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5416    /// [`Self::validate_upgrade_from`] d6801df,
5417    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5418    /// [`Self::validate_supervisor_shape`] 4c70105,
5419    /// [`Self::validate_acao_shape`] 5d6df54,
5420    /// [`Self::validate_kind_slot_coherence`] f0d286e): the
5421    /// author-time gate axis on the *per-slot* and *cross-family
5422    /// typed-slot* algebras each share one substrate primitive per
5423    /// compound gate, and this lift closes the third axis on the
5424    /// *code-surface* algebra so the layout pipeline routes all
5425    /// three coherence axes through one substrate primitive rather
5426    /// than nine open-coded blocks. Every future no-code kind
5427    /// (an `Actor` virtual-actor arm the M5 Orleans-inspired kind
5428    /// reaches through if it lands as a no-code composer, a future
5429    /// `Namespace` grouping kind) folds onto this compound gate
5430    /// as one arm addition rather than a fourth open-coded block
5431    /// at the wire-up site.
5432    ///
5433    /// # Errors
5434    ///
5435    /// Returns the [`crate::LayoutError`] variant naming the
5436    /// offending no-code kind:
5437    /// [`crate::LayoutError::SupervisorOwnsCode`] on a `:kind
5438    /// Supervisor` caixa with any declared code,
5439    /// [`crate::LayoutError::AplicacaoOwnsCode`] on a `:kind
5440    /// Aplicacao` caixa with any declared code,
5441    /// [`crate::LayoutError::AcaoOwnsCode`] on a `:kind Acao` caixa
5442    /// with any declared code. Passes trivially on every kind with
5443    /// no declared code and on every code-owning kind regardless
5444    /// of declared code (the fold's two identity-element arms).
5445    pub fn validate_no_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5446        let has_code =
5447            !self.bibliotecas().is_empty() || !self.exe().is_empty() || !self.servicos().is_empty();
5448        if !has_code {
5449            return Ok(());
5450        }
5451        if self.kind().is_supervisor() {
5452            return Err(crate::LayoutError::supervisor_owns_code(self));
5453        }
5454        if self.kind().is_aplicacao() {
5455            return Err(crate::LayoutError::aplicacao_owns_code(self));
5456        }
5457        if self.kind().is_acao() {
5458            return Err(crate::LayoutError::acao_owns_code(self));
5459        }
5460        Ok(())
5461    }
5462
5463    /// Compound per-`Caixa` kind ↔ `:ci` coherence gate — the `Acao`
5464    /// axis-only companion to the sibling three-arm
5465    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) on the
5466    /// M3 mesh / supervisor-tree / M2 Servico-runtime typed-slot
5467    /// families. `:ci` carries a typed CI run
5468    /// ([`canteiro_types::CiRun`], CANTEIRO §7.1-C) that only the
5469    /// `caixa-actions` renderer decomposes + validates and only for a
5470    /// `:kind Acao`. On any *other* kind a declared `:ci` is the
5471    /// manifest field's documented "ignored otherwise" — it silently
5472    /// passes verify and then vanishes (never decomposed, never
5473    /// rendered), far from the source `caixa.lisp`.
5474    ///
5475    /// Pre-lift the arm lived as a self-similar
5476    /// `if caixa.ci().is_some() && !caixa.kind().is_acao() { return
5477    /// Err(LayoutError::CiOnNonAcao { caixa: caixa.nome().to_string(),
5478    /// kind: caixa.kind() }); }` block at
5479    /// [`crate::layout::StandardLayout::verify`] — one consumer today
5480    /// but every future consumer that wanted to gate the `:ci`
5481    /// coherence axis as a unit (the deferred
5482    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5483    /// webhook re-checking after a per-slot patch, a future
5484    /// `feira validate --ci-coherence` per-caixa admission verb, a
5485    /// per-`Caixa` overlay resolver rejecting a kind-foreign `:ci`
5486    /// patch) was structurally forced to either re-inline the
5487    /// two-condition guard in lockstep with the layout wire-up (the
5488    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5489    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5490    /// Post-fold each such consumer reaches the arm through one call.
5491    ///
5492    /// Peer of the sibling three-arm
5493    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5494    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5495    /// axes under a uniform `{ caixa, kind, slots }` envelope
5496    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5497    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5498    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]). The `:ci`
5499    /// axis stays on its own primitive because
5500    /// [`crate::LayoutError::CiOnNonAcao`] carries a distinct
5501    /// `{ caixa, kind }` wrap shape (no `slots` field — `:ci` is a
5502    /// single `Option` not a `Vec`-of-named-slots) whose reshape
5503    /// onto the sibling `{ caixa, kind, slots }` envelope would
5504    /// force a variant rename touching every consumer of
5505    /// `CiOnNonAcao`; the two folds share the same
5506    /// author-time-vs-renderer split and diagnostic altitude, and
5507    /// route through peer substrate primitives on the same
5508    /// [`Caixa`] surface.
5509    ///
5510    /// Peer to the per-kind compound entry gates every substrate
5511    /// primitive on the M2/M3 typed-slot family already carries
5512    /// ([`Self::validate_deps`] b5dd55e, [`Self::validate_limits`]
5513    /// baa4688, [`Self::validate_behavior`] 0d2877a,
5514    /// [`Self::validate_upgrade_from`] d6801df,
5515    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5516    /// [`Self::validate_supervisor_shape`] 4c70105,
5517    /// [`Self::validate_acao_shape`] 5d6df54,
5518    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5519    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2): every
5520    /// author-time coherence axis on the typed [`Caixa`] surface now
5521    /// routes through one substrate primitive per axis rather than
5522    /// an open-coded block at the layout wire-up site.
5523    ///
5524    /// The gate carries two identity elements:
5525    /// - **`ci().is_none()`** — a caixa that declares no `:ci`
5526    ///   passes the first short-circuit before every per-arm
5527    ///   dispatch, on every kind. The canonical shape of the four
5528    ///   non-`Acao` kinds (`Biblioteca` / `Binario` / `Servico` /
5529    ///   `Supervisor` / `Aplicacao`) is `ci = None` — the arm
5530    ///   never fires on a well-shaped fixture.
5531    /// - **`:kind Acao`** — the owner-kind arm short-circuits on
5532    ///   every `Acao` caixa regardless of its `:ci` shape; a
5533    ///   malformed `:ci` on an `Acao` surfaces through the peer
5534    ///   [`Self::validate_acao_shape`] compound decompose gate
5535    ///   (5d6df54), not through this coherence gate.
5536    ///
5537    /// # Errors
5538    ///
5539    /// Returns [`crate::LayoutError::CiOnNonAcao`] naming the
5540    /// offending caixa's nome + kind on any non-`Acao` caixa with
5541    /// `:ci` declared. Passes trivially on every kind that declares
5542    /// no `:ci` and on every `:kind Acao` caixa regardless of
5543    /// declared `:ci` (the fold's two identity-element arms).
5544    pub fn validate_ci_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5545        if self.ci().is_some() && !self.kind().is_acao() {
5546            return Err(crate::LayoutError::CiOnNonAcao {
5547                caixa: self.nome().to_string(),
5548                kind: self.kind(),
5549            });
5550        }
5551        Ok(())
5552    }
5553
5554    /// Compound per-`Caixa` kind ↔ code-surface coherence gate on the
5555    /// two exclusive code-surface slots — `:exe` (owned only by
5556    /// [`crate::CaixaKind::Binario`], the nix-built executable surface)
5557    /// and `:servicos` (owned only by [`crate::CaixaKind::Servico`],
5558    /// the wasm-component + `ComputeUnit` daemon surface). The
5559    /// `caixa-helm` / `caixa-flux` / `caixa-flake` renderers gate
5560    /// emission on [`crate::render::require_kind`]`(_, <owning-kind>)`
5561    /// and only emit the slot for its owning kind — so on any *other*
5562    /// code-running kind a declared `:exe` / `:servicos` is the
5563    /// manifest field's documented "ignored otherwise": the path is
5564    /// validated by the per-kind path-existence loops in
5565    /// [`crate::layout::StandardLayout::verify`], but the value is
5566    /// never rendered into a build target or programs.yaml entry —
5567    /// it silently passes `feira build` and then vanishes, far from
5568    /// the source `caixa.lisp`, with no field naming which slot is
5569    /// foreign.
5570    ///
5571    /// Pre-lift the arm lived as a self-similar four-line `let
5572    /// foreign_code_slots = caixa.declared_foreign_code_slots(); if
5573    /// !foreign_code_slots.is_empty() { return
5574    /// Err(LayoutError::foreign_code_slot(caixa, foreign_code_slots));
5575    /// }` block at [`crate::layout::StandardLayout::verify`] — one
5576    /// consumer today but every future consumer that wanted to gate
5577    /// the code-surface coherence axis as a unit (the deferred
5578    /// `caixa.pleme.io/v1alpha1/Caixa` CR materializer's admission
5579    /// webhook re-checking after a per-slot patch, a future
5580    /// `feira validate --foreign-code` per-caixa admission verb, a
5581    /// per-`Caixa` overlay resolver rejecting a kind-foreign code-
5582    /// slot patch) was structurally forced to either re-inline the
5583    /// two-condition guard in lockstep with the layout wire-up (the
5584    /// duplication the PRIME DIRECTIVE names as a bug) or call the
5585    /// whole [`crate::layout::StandardLayout::verify`] pipeline.
5586    /// Post-fold each such consumer reaches the arm through one call.
5587    ///
5588    /// Peer of the sibling three-arm
5589    /// [`Self::validate_kind_slot_coherence`] fold (f0d286e) — that
5590    /// gate carries the M3 mesh / supervisor-tree / M2 Servico-runtime
5591    /// axes under the uniform `{ caixa, kind, slots }` envelope
5592    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
5593    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
5594    /// [`crate::LayoutError::ServicoSlotsOnNonServico`]); this gate
5595    /// carries the code-surface axis under the same
5596    /// `{ caixa, kind, slots }` envelope
5597    /// ([`crate::LayoutError::ForeignCodeSlot`]). The two folds share
5598    /// the envelope shape but stay separate primitives because the
5599    /// per-arm predicate differs: the cross-family fold rides on the
5600    /// outer `!self.kind().is_<owner>()` guard *paired* with a
5601    /// per-family `declared_<family>_slots` accumulator, while this
5602    /// fold's per-arm kind-check is baked into
5603    /// [`Self::declared_foreign_code_slots`] itself (each arm's
5604    /// `!self.kind().requires_<slot>()` guard fires inside the
5605    /// accumulator, not around it) — so a `:kind Binario` declaring
5606    /// `:servicos` and a `:kind Servico` declaring `:exe` are both
5607    /// caught by one accumulator sweep rather than by two independent
5608    /// arm dispatches. Peer with [`Self::validate_ci_kind_coherence`]
5609    /// (9b55beb) which carries the `:ci` axis on its own primitive
5610    /// for the same "distinct per-arm predicate shape, shared
5611    /// diagnostic altitude" reason.
5612    ///
5613    /// Peer to the per-kind and per-slot compound entry gates every
5614    /// substrate primitive on the M2/M3 typed-slot family already
5615    /// carries ([`Self::validate_deps`] b5dd55e,
5616    /// [`Self::validate_limits`] baa4688,
5617    /// [`Self::validate_behavior`] 0d2877a,
5618    /// [`Self::validate_upgrade_from`] d6801df,
5619    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5620    /// [`Self::validate_supervisor_shape`] 4c70105,
5621    /// [`Self::validate_acao_shape`] 5d6df54,
5622    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5623    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5624    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5625    /// author-time coherence axis on the typed [`Caixa`] surface now
5626    /// routes through one substrate primitive per axis rather than an
5627    /// open-coded block at the layout wire-up site. This closes the
5628    /// last open-coded kind ↔ slot coherence gate at the layout
5629    /// altitude — every kind-coherence diagnostic is now a substrate
5630    /// primitive.
5631    ///
5632    /// The gate carries three identity elements:
5633    /// - **Code-owning kinds on their native slot** — a
5634    ///   [`crate::CaixaKind::Binario`] declaring `:exe`, a
5635    ///   [`crate::CaixaKind::Servico`] declaring `:servicos` — each
5636    ///   arm's `!requires_<slot>()` predicate short-circuits inside
5637    ///   [`Self::declared_foreign_code_slots`], so the accumulator
5638    ///   returns an empty `Vec` and the outer `is_empty` short-
5639    ///   circuits before the wrap fires.
5640    /// - **Bare caixas** — a caixa with no declared code on any kind
5641    ///   passes the same accumulator's `is_empty` short-circuit on
5642    ///   every arm.
5643    /// - **No-code kinds** ([`crate::CaixaKind::Supervisor`] /
5644    ///   [`crate::CaixaKind::Aplicacao`] / [`crate::CaixaKind::Acao`])
5645    ///   declaring code — dominated upstream by the sibling
5646    ///   [`Self::validate_no_code_kind_coherence`] (3bbf6a2) which
5647    ///   surfaces [`crate::LayoutError::SupervisorOwnsCode`] /
5648    ///   [`crate::LayoutError::AplicacaoOwnsCode`] /
5649    ///   [`crate::LayoutError::AcaoOwnsCode`] first at the layout
5650    ///   wire-up site, so this gate never fires on a no-code kind
5651    ///   through the layout pipeline. A standalone caller reaching
5652    ///   this primitive without the sibling `_no_code_` gate first
5653    ///   would see a no-code kind's declared `:exe` / `:servicos`
5654    ///   surface `ForeignCodeSlot` here (the two folds partition the
5655    ///   diagnostic responsibility along the "declared no-code slot"
5656    ///   axis: no-code kinds get `OwnsCode`, code-running kinds get
5657    ///   `ForeignCodeSlot`), and the layout wire-up's canonical
5658    ///   `_no_code_` → `_foreign_code_` ordering keeps the
5659    ///   [`crate::LayoutError::SupervisorOwnsCode`] / … arm the one
5660    ///   that surfaces in the composed pipeline.
5661    ///
5662    /// Diagnostic order within the arm matches the pre-fold layout
5663    /// wire-up canonical sequence — `:exe` → `:servicos` — pinned by
5664    /// [`Self::declared_foreign_code_slots`]'s per-arm push order.
5665    ///
5666    /// # Errors
5667    ///
5668    /// Returns [`crate::LayoutError::ForeignCodeSlot`] naming the
5669    /// offending caixa's nome + kind + declared foreign-code slot
5670    /// list on any code-running kind ([`crate::CaixaKind::Biblioteca`]
5671    /// / [`crate::CaixaKind::Binario`] / [`crate::CaixaKind::Servico`])
5672    /// declaring another code-running kind's exclusive code surface.
5673    /// Passes trivially on every native-slot declaration (Binario
5674    /// with `:exe`, Servico with `:servicos`), on every bare caixa,
5675    /// and on every no-code kind (dominated upstream by the sibling
5676    /// [`Self::validate_no_code_kind_coherence`] `OwnsCode` gates —
5677    /// see the identity-element notes above).
5678    pub fn validate_foreign_code_kind_coherence(&self) -> Result<(), crate::LayoutError> {
5679        let foreign_code_slots = self.declared_foreign_code_slots();
5680        if !foreign_code_slots.is_empty() {
5681            return Err(crate::LayoutError::foreign_code_slot(
5682                self,
5683                foreign_code_slots,
5684            ));
5685        }
5686        Ok(())
5687    }
5688
5689    /// Compound per-`Caixa` required-slot gate on the three
5690    /// [`crate::CaixaKind`] arms whose sole payload is a canonical
5691    /// typed slot: `Binario`'s `:exe`, `Servico`'s `:servicos`,
5692    /// `Acao`'s `:ci`. Each arm refuses a caixa on its owner kind
5693    /// that declares no value in the corresponding required slot,
5694    /// so `feira build` (the canonical author-time gate) surfaces the
5695    /// self-locating "this kind needs this slot" diagnostic at the
5696    /// source `caixa.lisp` rather than deferring the failure to a
5697    /// downstream consumer (a nix build with no `:exe` to build, a
5698    /// programs.yaml fan-out with no `:servicos` to enumerate, a
5699    /// `caixa-actions` decompose with no `:ci` to walk).
5700    ///
5701    /// Pre-lift each of the three arms lived as a self-similar
5702    /// `if caixa.kind().requires_<slot>() && caixa.<slot>().is_<empty>() {
5703    /// return Err(LayoutError::<kind>_without_<slot>(caixa)); }`
5704    /// block at [`crate::layout::StandardLayout::verify`] — three
5705    /// consumers, three identical shapes, one substrate primitive on
5706    /// [`Caixa`] closing the duplication the PRIME DIRECTIVE names as
5707    /// a bug. Each of the three inner ctors
5708    /// ([`crate::LayoutError::binario_without_exe`] /
5709    /// [`crate::LayoutError::servico_without_servicos`] /
5710    /// [`crate::LayoutError::missing_ci`]) was already lifted onto
5711    /// the substrate by the peer [`crate::layout::layout_nome_only_ctors!`]
5712    /// macro, so the primitive routes through the same
5713    /// `Self::<variant>(caixa.nome().to_string())` tuple-literal
5714    /// wrap per arm as the pre-lift open-coded blocks.
5715    ///
5716    /// The paired `Biblioteca`-arm required-slot check
5717    /// ([`crate::LayoutError::MissingLib`]) stays open-coded at the
5718    /// layout wire-up site by design: it needs the filesystem oracle
5719    /// on [`crate::layout::LayoutInvariants`] to check the default
5720    /// `lib/<nome>.lisp` fallback path, which the pure per-`Caixa`
5721    /// typed-shape surface this fold rides on has no reference to.
5722    /// Same posture the peer [`Self::validate_no_code_kind_coherence`]
5723    /// fold takes on the on-disk existence loops.
5724    ///
5725    /// Diagnostic order at the primitive matches the pre-fold layout
5726    /// wire-up canonical sequence — `:exe` → `:servicos` → `:ci` —
5727    /// the same three-arm sweep the peer [`crate::CaixaKind`]
5728    /// discriminator carries at its `requires_*` accessors. Unlike
5729    /// the sibling cross-family [`Self::validate_kind_slot_coherence`]
5730    /// fold, the three arms of this fold are mutually exclusive by
5731    /// construction — `:kind` is a single-valued [`crate::CaixaKind`]
5732    /// discriminator so at most one arm can fire per caixa — and no
5733    /// cross-arm ordering pin is meaningful (the pre-fold three-block
5734    /// cascade at the wire-up site was already unreachable past the
5735    /// first matching arm).
5736    ///
5737    /// Peer to the per-kind and per-slot compound entry gates every
5738    /// substrate primitive on the M2/M3 typed-slot family already
5739    /// carries ([`Self::validate_deps`] b5dd55e,
5740    /// [`Self::validate_limits`] baa4688,
5741    /// [`Self::validate_behavior`] 0d2877a,
5742    /// [`Self::validate_upgrade_from`] d6801df,
5743    /// [`Self::validate_aplicacao_shape`] 949a7a0,
5744    /// [`Self::validate_supervisor_shape`] 4c70105,
5745    /// [`Self::validate_acao_shape`] 5d6df54,
5746    /// [`Self::validate_kind_slot_coherence`] f0d286e,
5747    /// [`Self::validate_no_code_kind_coherence`] 3bbf6a2,
5748    /// [`Self::validate_ci_kind_coherence`] 9b55beb): every
5749    /// author-time coherence axis on the typed [`Caixa`] surface
5750    /// now routes through one substrate primitive per axis rather
5751    /// than an open-coded block at the layout wire-up site.
5752    ///
5753    /// The gate carries two identity elements:
5754    /// - **Non-owner kinds** — each per-arm predicate is
5755    ///   `self.kind().requires_<slot>()`, which returns `true` only
5756    ///   for the owning kind ([`crate::CaixaKind::Binario`] on `:exe`,
5757    ///   [`crate::CaixaKind::Servico`] on `:servicos`,
5758    ///   [`crate::CaixaKind::Acao`] on `:ci`). Every non-owner kind
5759    ///   passes each per-arm dispatch trivially.
5760    /// - **Owner kinds with the required slot present** — a
5761    ///   [`crate::CaixaKind::Binario`] with a non-empty `:exe`, a
5762    ///   [`crate::CaixaKind::Servico`] with a non-empty `:servicos`,
5763    ///   an [`crate::CaixaKind::Acao`] with `ci = Some(_)` — passes
5764    ///   its arm's `is_empty` / `is_none` short-circuit.
5765    ///
5766    /// # Errors
5767    ///
5768    /// Returns the [`crate::LayoutError`] variant naming the
5769    /// offending owner kind:
5770    /// [`crate::LayoutError::BinarioWithoutExe`] on a
5771    /// [`crate::CaixaKind::Binario`] caixa with no declared `:exe`,
5772    /// [`crate::LayoutError::ServicoWithoutServicos`] on a
5773    /// [`crate::CaixaKind::Servico`] caixa with no declared
5774    /// `:servicos`, [`crate::LayoutError::MissingCi`] on a
5775    /// [`crate::CaixaKind::Acao`] caixa with no declared `:ci`.
5776    /// Passes trivially on every non-owner kind and on every owner
5777    /// kind with its required slot present.
5778    pub fn validate_required_kind_slot(&self) -> Result<(), crate::LayoutError> {
5779        if self.kind().requires_exe() && self.exe().is_empty() {
5780            return Err(crate::LayoutError::binario_without_exe(self));
5781        }
5782        if self.kind().requires_servicos() && self.servicos().is_empty() {
5783            return Err(crate::LayoutError::servico_without_servicos(self));
5784        }
5785        if self.kind().requires_ci() && self.ci().is_none() {
5786            return Err(crate::LayoutError::missing_ci(self));
5787        }
5788        Ok(())
5789    }
5790
5791    /// Reject per-entry values on the three Caixa-level code-surface
5792    /// path lists (`:bibliotecas`, `:exe`, `:servicos`) that the
5793    /// layout checker's `root.join(p)` sandbox would silently subvert.
5794    /// Same three structural footguns the peer
5795    /// [`BehaviorSpec::validate`] (b0c8389) and
5796    /// [`crate::UpgradeInstruction::validate`] `StateChange` arm
5797    /// (26da2c7) already close on the M2 `:behavior :on-*` and
5798    /// `:upgrade-from :state-change :script` axes, here lifted onto
5799    /// the three top-level code-path axes through the shared
5800    /// [`is_sandboxed_relative_path`] predicate:
5801    ///
5802    ///   - empty entry (`(:bibliotecas (""))` / `(:exe (""))` /
5803    ///     `(:servicos (""))`): `PathBuf::new()` round-trips through
5804    ///     [`Path::join`] as the base itself — `root.join("")` ==
5805    ///     `root`, so the existence check (`self.exists(&root)`)
5806    ///     trivially passes (the project root exists), and the layout
5807    ///     silently treats the project root as a biblioteca / exe /
5808    ///     servico entry. The `:bibliotecas` loop then hands the root
5809    ///     to `tatara_lisp::read` at `feira build` time as if the root
5810    ///     directory itself were a Lisp source file — a parse error
5811    ///     far from the source `caixa.lisp` with no field naming the
5812    ///     offending entry.
5813    ///   - absolute path (`(:bibliotecas ("/etc/passwd"))`):
5814    ///     [`Path::join`] *replaces* the base when the right-hand side
5815    ///     is absolute, so `root.join("/etc/passwd")` resolves to
5816    ///     `"/etc/passwd"` and escapes the project sandbox entirely.
5817    ///     The existence check then silently consults whatever the
5818    ///     escaped path resolves to — for `:bibliotecas`, the layout
5819    ///     has no `starts_with`-fence (only `:exe` is fenced under
5820    ///     `exe/` and `:servicos` under `servicos/`), so an absolute
5821    ///     `:bibliotecas` entry that happens to resolve on disk
5822    ///     silently passes. For `:exe` / `:servicos` the fence catches
5823    ///     the absolute case downstream as `ExeOutsideDir` /
5824    ///     `ServicoOutsideDir` (or `MissingEntry` if the absolute path
5825    ///     doesn't exist), but with a downstream-shaped diagnostic
5826    ///     that names the resolved escape path rather than the
5827    ///     authoring footgun at the source.
5828    ///   - parent-escape (`(:bibliotecas ("../sibling/x.lisp"))` /
5829    ///     `(:exe ("exe/../../escape.lisp"))`): a [`PathBuf`] with any
5830    ///     [`std::path::Component::ParentDir`] anywhere round-trips
5831    ///     through [`Path::join`] as a traversal above the caixa root.
5832    ///     The `:exe` / `:servicos` `starts_with(<dir>)` fence is
5833    ///     *component-aware* (not canonical-path-aware), so
5834    ///     `root.join("exe/../../escape.lisp")` `starts_with(exe_dir)`
5835    ///     is **true** even though the canonical resolution
5836    ///     `{parent of root}/escape.lisp` lives outside the caixa root
5837    ///     — the fence silently lets the parent-escape through, and
5838    ///     the existence check passes if that escape-target happens
5839    ///     to exist. Caught regardless of where the `..` sits
5840    ///     (leading, mid-path, trailing) so the gate matches the peer
5841    ///     predicate's full coverage.
5842    ///
5843    /// Same `Empty` → `Absolute` → `ParentEscape` arm-ordering every peer
5844    /// `is_sandboxed_relative_path` consumer follows (b0c8389 / 26da2c7);
5845    /// same per-slot diagnostic shape every peer per-axis path-gate
5846    /// exposes (`*Empty { slot }` / `*Absolute { slot, path }` /
5847    /// `*ParentEscape { slot, path }`). Cross-slot precedence is
5848    /// `:bibliotecas` → `:exe` → `:servicos` — the same declaration
5849    /// order [`Caixa::declared_foreign_code_slots`] uses for its
5850    /// canonical foreign-code-slot diagnostic, so a manifest with
5851    /// multiple malformed slots surfaces the lexicographically-earliest
5852    /// slot's diagnostic deterministically.
5853    ///
5854    /// Lifted to the typed surface as a Caixa-level validator (peer
5855    /// of [`Self::validate_nome`] / [`Self::validate_versao`] /
5856    /// [`Self::validate_deps`] / [`Self::validate_restart_window`])
5857    /// and wired into [`crate::StandardLayout::verify`] before the
5858    /// existence-check loops so the diagnostic names the offending
5859    /// slot at the source caixa.lisp rather than reporting a
5860    /// downstream `MissingEntry` / `ExeOutsideDir` /
5861    /// `ServicoOutsideDir` against the resolved sandbox-escape path.
5862    /// The fourth typed code-path surface — every author-supplied
5863    /// path on the manifest — is now structurally accept-shaped
5864    /// past validate, peer with `:behavior :on-*` and
5865    /// `:upgrade-from :state-change :script`.
5866    pub fn validate_code_paths(&self) -> Result<(), ManifestError> {
5867        /// Per-slot file-type contract for the three Caixa-level
5868        /// code-path surfaces (`:bibliotecas`, `:exe`, `:servicos`).
5869        /// Each variant names the predicate the per-entry file-type
5870        /// gate consults; [`Self::None`] opts the slot out of any
5871        /// file-type contract. Lifted as a typed local enum so the
5872        /// per-slot dispatch is exhaustive at the `match` — adding a
5873        /// future axis to the typed-substrate `:` slot set (the
5874        /// future `:assets` resource axis the M5 roadmap names, the
5875        /// future `:nix-flake` derivation axis the caixa-flake
5876        /// emitter consults) lands as one variant + one `match` arm,
5877        /// not a coordinated rewrite of every per-slot bool flag.
5878        ///
5879        /// Peer of the typed-substrate per-slot variant disciplines
5880        /// already established on this surface
5881        /// ([`crate::supervisor::RestartStrategy`] +
5882        /// [`crate::supervisor::RestartPolicy`] on the OTP-shape
5883        /// supervision-tree axis,
5884        /// [`crate::aplicacao::PlacementStrategy`] on the §III.1
5885        /// placement axis, [`crate::aplicacao::WitTarget`] on the
5886        /// `:contratos` payload-target axis): the typed `enum` is
5887        /// the substrate's single source of truth for the per-axis
5888        /// dispatch, and every consumer (the per-arm body here, the
5889        /// future feira-lint per-slot diagnostic renderer, the M4
5890        /// per-axis admission webhook) reaches for the same typed
5891        /// surface rather than re-deriving the partition from inline
5892        /// flag combinations.
5893        enum CodePathFileType {
5894            /// `:exe` — nix-build derivation output, no terminating-
5895            /// extension contract (the canonical `"exe/<name>"`
5896            /// fixtures the layout's `ExeOutsideDir` error message
5897            /// documents carry no extension by convention).
5898            None,
5899            /// `:bibliotecas` — tatara-lisp source files the
5900            /// `feira build` loop reads through `tatara_lisp::read`
5901            /// at parse time. Routes to [`is_lisp_extension`].
5902            LispSource,
5903            /// `:servicos` — ComputeUnit-CR YAML files the
5904            /// caixa-helm / caixa-flux renderers consume through
5905            /// `serde_yaml::from_str`. Routes to
5906            /// [`is_computeunit_yaml_extension`].
5907            ComputeUnitYaml,
5908        }
5909
5910        // The per-slot [`CodePathFileType`] selects which axes carry the
5911        // lifted file-type predicate. `:bibliotecas` is the tatara-lisp
5912        // source axis (the `feira build` loop at
5913        // `caixa-feira/src/cmd/build.rs:33` reads each entry through
5914        // `tatara_lisp::read` at parse time) — the lifted
5915        // [`is_lisp_extension`] predicate gates the `.lisp` extension.
5916        // `:exe` is the nix-built executable surface (per the canonical
5917        // `"exe/<name>"`-shaped fixtures the layout's `ExeOutsideDir`
5918        // error message documents and every in-tree
5919        // `caixa_with_code_paths` positive control uses) — its file-type
5920        // contract is "nix-build derivation output", not a typed source
5921        // file, so [`CodePathFileType::None`] opts the slot out of any
5922        // file-type gate. `:servicos` is the `.computeunit.yaml`
5923        // ComputeUnit-CR axis (the peer caixa-helm / caixa-flux
5924        // renderers consume each entry through `serde_yaml::from_str` as
5925        // a typed `ComputeUnit` CR) — the lifted
5926        // [`is_computeunit_yaml_extension`] predicate gates the compound
5927        // `.computeunit.yaml` suffix. All three axes are surfaced through
5928        // the same iteration so the sandbox-shape + duplicate gates
5929        // apply uniformly; the typed file-type dispatch fires per-slot
5930        // exactly where the downstream consumer's accepted set demands
5931        // it. The third file-type variant ([`ComputeUnitYaml`]) is the
5932        // compounding lift on the peer 64772a9 `:bibliotecas`
5933        // `.lisp`-gate trajectory — the second of the three code-path
5934        // axes to land on a typed compound-suffix gate, with the same
5935        // self-locating per-slot diagnostic shape every peer per-axis
5936        // file-type lift uses (`*NonLispExtension { slot, path }` /
5937        // `*NonComputeUnitYamlExtension { slot, path }`).
5938        for (slot, list, file_type) in [
5939            (
5940                ":bibliotecas",
5941                &self.bibliotecas,
5942                CodePathFileType::LispSource,
5943            ),
5944            (":exe", &self.exe, CodePathFileType::None),
5945            (
5946                ":servicos",
5947                &self.servicos,
5948                CodePathFileType::ComputeUnitYaml,
5949            ),
5950        ] {
5951            // Per-slot set-not-multiset gate on the typed code-path axis.
5952            // Every peer Vec-shaped author-supplied list past validate is
5953            // a set, not a multiset: `:membros :caixa`
5954            // ([`crate::AplicacaoError::MembroDuplicate`]), `:placement
5955            // :clusters` ([`crate::AplicacaoError::PlacementClusterDuplicate`]),
5956            // `:entrada :paths` ([`crate::AplicacaoError::EntradaPathDuplicate`]),
5957            // `:contratos` ([`crate::AplicacaoError::ContratoDuplicate`]),
5958            // `:children :caixa` ([`crate::SupervisorError::DuplicateChild`]),
5959            // `:deps` / `:deps-dev` `:nome` ([`crate::DepError::DuplicateNome`]
5960            // per 359fba5), `:upgrade-from :from` ([`crate::UpgradeError::DuplicateFrom`]),
5961            // `:etiquetas` ([`ManifestError::EtiquetaDuplicate`] per 360a499),
5962            // `:autores` ([`ManifestError::AutorDuplicate`] per 86c769b) —
5963            // the three code-path lists are the last Vec-shaped author-
5964            // supplied slots on the typed Caixa surface still admitting a
5965            // duplicate entry silently. Scope is per-list (`:bibliotecas`
5966            // duplicates are flagged within `:bibliotecas`, not across
5967            // `:bibliotecas` ↔ `:exe`) — the same per-list scope `:deps`
5968            // ↔ `:deps-dev` use (a `:nome` present in both lists is a
5969            // legitimate dev-vs-runtime shape on the dep axis, fenced
5970            // separately by [`crate::dep::validate_no_self_dep`]). On the
5971            // code-path axis a cross-slot collision is structurally
5972            // impossible by the layout's `starts_with(<exe|servicos>_dir)`
5973            // fence — `:exe` and `:servicos` entries are confined to their
5974            // own directory trees, so the only way a string could appear
5975            // on two code-path lists is the (rare, structurally invalid)
5976            // case where `:bibliotecas` carries an `"exe/<x>"` or
5977            // `"servicos/<x>.yaml"`-shaped path.
5978            //
5979            // Without the gate three authoring footguns silently passed:
5980            //
5981            //   - `:bibliotecas ("lib/foo.lisp" "lib/foo.lisp")` — the
5982            //     canonical copy-paste-the-wrong-file footgun. `feira
5983            //     build` (`caixa-feira/src/cmd/build.rs:33`) walks the
5984            //     list and re-parses the same file twice, wasting work
5985            //     and silently masking the author's intent to declare a
5986            //     *second* biblioteca.
5987            //   - `:exe ("exe/cli" "exe/cli")` — the same footgun on the
5988            //     Binario surface. The future `caixa-flake` `nix flake`
5989            //     emitter that materializes each `:exe` entry as a flake
5990            //     `packages.<exe-name>` derivation would collide on the
5991            //     duplicate package name and surface a flake-eval error
5992            //     far from the source `caixa.lisp`.
5993            //   - `:servicos ("servicos/x.computeunit.yaml"
5994            //     "servicos/x.computeunit.yaml")` — the same footgun on
5995            //     the Servico surface. The peer `caixa-helm` / `caixa-flux`
5996            //     renderers already refuse `:servicos.len() != 1` with
5997            //     the narrower [`UnsupportedServicoCount`] diagnostic, but
5998            //     that diagnostic surfaces "too many servicos" without
5999            //     naming "duplicate entry" — the typed self-locating
6000            //     "which entry is the duplicate" framing only lands at
6001            //     this gate.
6002            //
6003            // Same `seen.insert(entry.as_str())` shape every peer per-list
6004            // duplicate gate uses (`:etiquetas` 360a499, `:autores`
6005            // 86c769b, `:deps` 359fba5) and the same "structural shape
6006            // checks fire before the duplicate check on the same entry"
6007            // ordering (a `(:bibliotecas ("" "lib/x.lisp" "lib/x.lisp"))`
6008            // shape surfaces the narrower [`Self::CodePathEmpty`] for the
6009            // empty entry first, not the duplicate on the later pair).
6010            let mut seen = std::collections::HashSet::new();
6011            for entry in list {
6012                let path = Path::new(entry);
6013                match is_sandboxed_relative_path(path) {
6014                    Ok(()) => {}
6015                    Err(PathShapeViolation::Empty) => {
6016                        return Err(ManifestError::CodePathEmpty { slot });
6017                    }
6018                    Err(PathShapeViolation::Absolute) => {
6019                        return Err(ManifestError::code_path_absolute(slot, path));
6020                    }
6021                    Err(PathShapeViolation::ParentEscape) => {
6022                        return Err(ManifestError::code_path_parent_escape(slot, path));
6023                    }
6024                }
6025                // The per-slot file-type gate dispatched through the
6026                // typed [`CodePathFileType`] selector above. Each variant
6027                // routes to the lifted predicate the downstream consumer
6028                // demands:
6029                //
6030                //   - [`LispSource`] → [`is_lisp_extension`] for
6031                //     `:bibliotecas` (the `feira build` loop's
6032                //     `tatara_lisp::read` consumer);
6033                //   - [`ComputeUnitYaml`] → [`is_computeunit_yaml_extension`]
6034                //     for `:servicos` (the caixa-helm / caixa-flux
6035                //     `serde_yaml::from_str` consumer's `ComputeUnit` CR
6036                //     accepted set);
6037                //   - [`None`] for `:exe` — the nix-build derivation-
6038                //     output axis has no terminating-extension contract.
6039                //
6040                // Fires after the sandbox-shape arms so a path that is
6041                // *both* sandbox-escaping and wrong-extension surfaces
6042                // the more fundamental sandbox-shape diagnostic first
6043                // (mirrors the peer `EmptyPath` → `AbsolutePath` →
6044                // `ParentEscape` → `NonLispExtension` arm-ordering on
6045                // `:behavior :on-*` c97815a, and `EmptyScript` →
6046                // `AbsoluteScript` → `ParentEscapeScript` →
6047                // `NonLispExtensionScript` on
6048                // `:upgrade-from :state-change :script` 33cc830), and
6049                // before the duplicate gate so the narrower per-entry
6050                // file-type shape dominates the cross-entry uniqueness
6051                // diagnostic (a
6052                // `("servicos/x.yaml" "servicos/x.yaml")` shape on
6053                // `:servicos` surfaces
6054                // `CodePathNonComputeUnitYamlExtension` on the first
6055                // entry rather than `CodePathDuplicate` on the pair —
6056                // peer with the 64772a9 `:bibliotecas`
6057                // `("lib/x.txt" "lib/x.txt")` ordering).
6058                match file_type {
6059                    CodePathFileType::None => {}
6060                    CodePathFileType::LispSource => {
6061                        if !is_lisp_extension(path) {
6062                            return Err(ManifestError::code_path_non_lisp_extension(slot, path));
6063                        }
6064                    }
6065                    CodePathFileType::ComputeUnitYaml => {
6066                        if !is_computeunit_yaml_extension(path) {
6067                            return Err(ManifestError::code_path_non_computeunit_yaml_extension(
6068                                slot, path,
6069                            ));
6070                        }
6071                    }
6072                }
6073                crate::render::insert_first_seen(&mut seen, entry.as_str(), || {
6074                    ManifestError::code_path_duplicate(slot, path)
6075                })?;
6076            }
6077        }
6078        Ok(())
6079    }
6080
6081    /// Reject `:etiquetas` lists with an empty entry or with two entries
6082    /// agreeing on the same string. `:etiquetas` is the universal
6083    /// registry-search-tag axis on [`Caixa`] (every kind carries the
6084    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6085    /// `Chart.yaml` `keywords:` array on every Servico (caixa-helm's
6086    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:236` folds it through
6087    /// a [`std::collections::BTreeSet`] alongside the four substrate-
6088    /// fixed tags `lareira` / `wasm` / `tatara-lisp` / `caixa-servico`).
6089    /// Two authoring footguns silently passed validate without this gate:
6090    ///
6091    ///   - Empty entry (`(:etiquetas (""))` — the canonical paste-from-
6092    ///     blank-doc footgun) rendered as `keywords: ["", "caixa-servico",
6093    ///     "lareira", "tatara-lisp", "wasm"]` in `Chart.yaml`. Helm's
6094    ///     `chart.metadata.keywords` admits the value without a strict
6095    ///     parser-side gate, but the empty keyword has no operational
6096    ///     meaning — it indexes nothing in the future caixa-registry
6097    ///     search axis and clutters the rendered chart with a no-op tag.
6098    ///   - Duplicate entries (`(:etiquetas ("demo" "demo"))` — the
6099    ///     copy-paste-the-wrong-tag footgun) silently passed validate
6100    ///     and were silently dedup'd by caixa-helm's `BTreeSet` collect
6101    ///     at chart render — a "second wins / one silently disappears"
6102    ///     shape divergent from every peer typed-graph set gate
6103    ///     ([`crate::AplicacaoError::MembroDuplicate`] on `:membros`,
6104    ///     [`crate::AplicacaoError::PlacementClusterDuplicate`] on
6105    ///     `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6106    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6107    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6108    ///     `:deps` / `:deps-dev` per 359fba5, [`crate::UpgradeError::DuplicateFrom`]
6109    ///     on `:upgrade-from`, the per-instruction-class singularity
6110    ///     gates [`crate::UpgradeError::DuplicateLoadModule`] /
6111    ///     [`crate::UpgradeError::DuplicateStateChange`] /
6112    ///     [`crate::UpgradeError::DuplicateCleanup`]). The typed-graph
6113    ///     discipline is uniform: every Vec-shaped author-supplied list
6114    ///     past validate is set-not-multiset, by construction.
6115    ///
6116    /// Past the empty arm the gate enforces the chart-keyword shape
6117    /// predicate via [`crate::render::is_chart_keyword_shape`]: Cargo's
6118    /// crates.io `[package] keywords` grammar — 1..=20 bytes, starts
6119    /// with an ASCII letter, ASCII alphanumeric / `_` / `-`
6120    /// continuation. Closes the canonical paste-from-doc footguns the
6121    /// bare empty + duplicate arms left open: paste-from-aligned-doc
6122    /// whitespace (`" mesh"`, `"mesh "`), paste-from-multiline-doc
6123    /// newline (`"mesh\nhttp"` — the author pasted a multi-tag block
6124    /// into one entry instead of splitting), paste-from-Windows-CRLF-doc
6125    /// carriage return, CSV-list-separator confusion (`"mesh,http,grpc"`
6126    /// — the author meant three separate list entries), path-separator
6127    /// confusion (`"caixa/servico"`), namespace-suffix (`"http.1"`),
6128    /// leading-digit (`"1foo"`), kebab-leak (`"-foo"`), snake-leak
6129    /// (`"_foo"`), non-ASCII (`"café"`), and paste-from-binary-blob
6130    /// control bytes that would silently land as malformed search tags
6131    /// in the rendered Chart.yaml `keywords:` array and break the
6132    /// Artifact Hub keyword index lookup far from the source caixa.lisp.
6133    /// Mirrors the [`Self::validate_autores`] shape-predicate cascade
6134    /// established on the sibling universal-axis `Vec<String>` surface
6135    /// — the second universal-axis Vec<String> surface to land the
6136    /// empty-first-then-shape-then-duplicate per-entry cascade.
6137    ///
6138    /// Same empty-first cascade discipline every peer per-axis gate
6139    /// uses: the per-entry empty arm fires before the per-entry shape
6140    /// arm fires before the cross-entry duplicate arm, so an
6141    /// `("" "mesh" "mesh")` authoring shape surfaces the narrower
6142    /// [`ManifestError::EtiquetaEmpty`] (the structural "this entry
6143    /// has no value" defect) before either the shape or the duplicate
6144    /// diagnostic. Walks the list in declaration order so the
6145    /// first-collision diagnostic surfaces the lexicographically-
6146    /// earliest offending position, peer with every other duplicate
6147    /// gate on this surface.
6148    ///
6149    /// Universal-axis (every kind carries `:etiquetas`), so wired at the
6150    /// caixa-build gate alongside the peer universal gates
6151    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6152    /// [`Self::validate_deps`] / [`Self::validate_code_paths`] — before
6153    /// the kind-coherence gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`]
6154    /// / [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6155    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6156    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6157    /// slot sets. The future caixa-registry search axis can reach for
6158    /// `caixa.etiquetas` knowing every entry is a non-empty distinct
6159    /// chart-keyword-shaped string without re-deriving the precondition.
6160    pub fn validate_etiquetas(&self) -> Result<(), ManifestError> {
6161        let mut seen = std::collections::HashSet::new();
6162        for etiqueta in self.etiquetas() {
6163            if etiqueta.is_empty() {
6164                return Err(ManifestError::EtiquetaEmpty);
6165            }
6166            crate::render::is_chart_keyword_shape(etiqueta)
6167                .map_err(|reason| ManifestError::etiqueta_invalid(etiqueta, reason))?;
6168            crate::render::insert_first_seen(&mut seen, etiqueta.as_str(), || {
6169                ManifestError::EtiquetaDuplicate {
6170                    etiqueta: etiqueta.clone(),
6171                }
6172            })?;
6173        }
6174        Ok(())
6175    }
6176
6177    /// Reject `:autores` lists with an empty entry or with two entries
6178    /// agreeing on the same string. `:autores` is the universal
6179    /// maintainer-axis on [`Caixa`] (every kind carries the
6180    /// `Vec<String>` slot) and lands verbatim as the Helm chart
6181    /// `Chart.yaml` `maintainers:` array on every Servico (caixa-helm's
6182    /// `build_chart_yaml` at `caixa-helm/src/lib.rs:251` maps each entry
6183    /// to a `Maintainer { name, email: None }` without dedup). Two
6184    /// authoring footguns silently passed validate without this gate:
6185    ///
6186    ///   - Empty entry (`(:autores (""))` — the canonical paste-from-
6187    ///     blank-doc footgun) rendered as
6188    ///     `maintainers: [{name: "", email: null}]` in `Chart.yaml`. The
6189    ///     empty maintainer name has no operational meaning — it
6190    ///     identifies no one in the substrate's authorship index and
6191    ///     clutters the rendered chart with a no-op maintainer.
6192    ///   - Duplicate entries (`(:autores ("pleme-io" "pleme-io"))` —
6193    ///     the copy-paste-the-wrong-author footgun) silently passed
6194    ///     validate and rendered as two identical maintainer entries.
6195    ///     Unlike the [`Self::validate_etiquetas`] peer (caixa-helm's
6196    ///     `BTreeSet`-collect on `:etiquetas` silently dedups the
6197    ///     rendered `keywords:` array at chart-render time), the
6198    ///     `maintainers:` rendering has *no* dedup — duplicate `:autores`
6199    ///     entries stack verbatim in the chart, divergent from every
6200    ///     peer typed-graph set gate ([`crate::AplicacaoError::MembroDuplicate`]
6201    ///     on `:membros`, [`crate::AplicacaoError::PlacementClusterDuplicate`]
6202    ///     on `:placement :clusters`, [`crate::AplicacaoError::EntradaPathDuplicate`]
6203    ///     on `:entrada :paths`, [`crate::AplicacaoError::ContratoDuplicate`]
6204    ///     on `:contratos`, [`crate::DepError::DuplicateNome`] on
6205    ///     `:deps` / `:deps-dev`, [`crate::UpgradeError::DuplicateFrom`]
6206    ///     on `:upgrade-from`, [`ManifestError::EtiquetaDuplicate`] on
6207    ///     `:etiquetas`).
6208    ///
6209    /// Past the empty arm the gate enforces the chart-maintainer-name
6210    /// shape predicate via [`crate::render::is_chart_maintainer_name_shape`]:
6211    /// the structural single-line printable-UTF-8 floor every realistic
6212    /// Helm chart maintainer name carries — 1..=128 bytes, no leading
6213    /// or trailing whitespace, no ASCII control characters anywhere,
6214    /// Unicode bytes accepted. Closes the canonical paste-from-doc
6215    /// footguns the bare empty + duplicate arms left open:
6216    /// paste-from-aligned-doc whitespace (`" pleme-io"`, `"pleme-io "`),
6217    /// paste-from-multiline-doc newline (`"alice\nbob"` — the author
6218    /// pasted a multi-line block of author records into one `:autores`
6219    /// entry instead of splitting into one entry per author),
6220    /// paste-from-Windows-CRLF-doc carriage return, tab-from-aligned-doc,
6221    /// and the paste-from-binary-blob control bytes that would silently
6222    /// land as YAML-illegal byte sequences in the rendered Chart.yaml
6223    /// `maintainers:` array. Mirrors the shape-predicate cascade
6224    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6225    /// [`Self::validate_edicao`] / [`Self::validate_repositorio`]
6226    /// establish past their own empty arms on the sibling universal-axis
6227    /// `Option<String>` surfaces — the first universal-axis Vec<String>
6228    /// surface to land the empty-first-then-shape-then-duplicate per-entry
6229    /// cascade.
6230    ///
6231    /// Same empty-first cascade discipline every peer per-axis gate
6232    /// uses: the per-entry empty arm fires before the per-entry shape
6233    /// arm before the cross-entry duplicate arm. Walks the list in
6234    /// declaration order so the first-collision diagnostic surfaces the
6235    /// lexicographically-earliest offending position, peer with every
6236    /// other duplicate gate on this surface.
6237    ///
6238    /// Universal-axis (every kind carries `:autores`), so wired at the
6239    /// caixa-build gate alongside the peer universal gates
6240    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6241    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6242    /// [`Self::validate_code_paths`] — before the kind-coherence gates
6243    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6244    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6245    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6246    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-specific
6247    /// slot sets.
6248    pub fn validate_autores(&self) -> Result<(), ManifestError> {
6249        let mut seen = std::collections::HashSet::new();
6250        for autor in self.autores() {
6251            if autor.is_empty() {
6252                return Err(ManifestError::AutorEmpty);
6253            }
6254            crate::render::is_chart_maintainer_name_shape(autor)
6255                .map_err(|reason| ManifestError::autor_invalid(autor, reason))?;
6256            crate::render::insert_first_seen(&mut seen, autor.as_str(), || {
6257                ManifestError::AutorDuplicate {
6258                    autor: autor.clone(),
6259                }
6260            })?;
6261        }
6262        Ok(())
6263    }
6264
6265    /// Reject `:repositorio` values whose shape the shared
6266    /// [`crate::render::is_git_repo_url`] predicate refuses. The flat
6267    /// `repositorio: Option<String>` slot on [`Caixa`] is the
6268    /// universal git-shaped homepage axis every kind carries — the
6269    /// substrate routes the same string through two load-bearing
6270    /// consumers:
6271    ///
6272    ///   - [`caixa-helm`] folds it verbatim into the rendered
6273    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `home:` field
6274    ///     (`build_chart_yaml` at `caixa-helm/src/lib.rs:268`) and into
6275    ///     the chart `README.md` `repo = …` interpolation
6276    ///     (`caixa-helm/src/lib.rs:359`).
6277    ///   - [`caixa-flux`] folds it verbatim into the standalone
6278    ///     `ClusterBundleOpts::for_caixa` `git_url:` field
6279    ///     (`caixa-flux/src/lib.rs:293`), which becomes the `FluxCD`
6280    ///     `GitRepository.spec.url` the cluster's source-controller
6281    ///     polls — the load-bearing deploy-time axis.
6282    ///
6283    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6284    /// substitute a placeholder when the slot is absent (`None` → the
6285    /// fallback fires); a `Some("")` *skips the fallback* and silently
6286    /// passes the empty string through to `Chart.yaml home: ""` /
6287    /// `GitRepository url: ""` — Helm's chart lint and `FluxCD`'s source
6288    /// controller both reject the empty URL far from the source
6289    /// `caixa.lisp`, with no field naming the offending `:repositorio`.
6290    /// Similarly a malformed `:repositorio` (whitespace, control char,
6291    /// missing `:` separator, leading `-`) silently lands in the
6292    /// rendered artifacts and breaks at `git clone` / `helm template`
6293    /// / `flux reconcile` time.
6294    ///
6295    /// Thin wrapper around [`crate::render::is_git_repo_url`] — the
6296    /// same shared predicate the peer [`crate::DepSource::validate`]
6297    /// routes the `:fonte (:tipo git :repo …)` axis through. With this
6298    /// gate the two `git URL`-shaped surfaces on the typed Caixa
6299    /// (`:repositorio` here, `:deps :fonte :repo` peer) are
6300    /// structurally equivalent: every value past validate is
6301    /// guaranteed-acceptable by the predicate's union of constraints
6302    /// (non-empty, length-bounded, no leading `-`, no whitespace, no
6303    /// control chars, ASCII only, no leading `:`, contains a `:`
6304    /// separator). The predicate accepts every documented authoring
6305    /// shape — `github:org/repo` shorthand, `https://host/path`,
6306    /// `ssh://[user@]host/path`, `git://host/path`, `git@host:path`
6307    /// scp-style SSH, `file:///path` — and refuses the canonical
6308    /// paste-from-blank-doc / paste-from-multiline-doc / CLI-arg-
6309    /// injection footguns at validate time. Maps the predicate's
6310    /// `String` reason verbatim into the
6311    /// [`ManifestError::RepositorioInvalid`] variant, carrying the
6312    /// offending value + parser-shaped reason so the diagnostic is
6313    /// self-locating (the author can grep their `caixa.lisp` for
6314    /// `:repositorio "<value>"` and fix it in one edit).
6315    ///
6316    /// `None` (the canonical "omit the slot to express no published
6317    /// homepage" shape) is accepted trivially — the gate is a no-op
6318    /// when the author didn't declare a value. `Some("")` is gated by
6319    /// the narrower [`ManifestError::RepositorioEmpty`] arm before the
6320    /// shape predicate is consulted, mirroring the empty-first cascade
6321    /// every peer per-axis identity gate uses
6322    /// ([`ManifestError::NomeEmpty`] → [`ManifestError::NomeInvalid`],
6323    /// [`ManifestError::VersaoEmpty`] → [`ManifestError::VersaoInvalid`],
6324    /// [`crate::DepError::FonteRepoEmpty`] →
6325    /// [`crate::DepError::FonteRepoInvalid`]).
6326    ///
6327    /// Universal-axis (every kind carries `:repositorio`), so wired at
6328    /// the caixa-build gate alongside the peer universal gates
6329    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6330    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6331    /// [`Self::validate_autores`] / [`Self::validate_code_paths`] —
6332    /// before the kind-coherence gates
6333    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6334    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6335    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6336    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6337    /// specific slot sets.
6338    pub fn validate_repositorio(&self) -> Result<(), ManifestError> {
6339        let Some(s) = self.repositorio() else {
6340            return Ok(());
6341        };
6342        if s.is_empty() {
6343            return Err(ManifestError::RepositorioEmpty);
6344        }
6345        is_git_repo_url(s).map_err(|reason| ManifestError::repositorio_invalid(s, reason))
6346    }
6347
6348    /// Reject `:descricao` values that are the empty string. The flat
6349    /// `descricao: Option<String>` slot on [`Caixa`] is the universal
6350    /// free-form-prose homepage axis every kind carries — the
6351    /// substrate routes the same string through two load-bearing
6352    /// consumers in the [`caixa-helm`] renderer:
6353    ///
6354    ///   - `build_chart_yaml` folds it verbatim into the rendered
6355    ///     `lareira-<nome>` Helm chart's `Chart.yaml` `description:`
6356    ///     field (`caixa-helm/src/lib.rs:232-235`).
6357    ///   - `build_readme` folds it verbatim into the rendered chart
6358    ///     `README.md` header (`caixa-helm/src/lib.rs:333-336`).
6359    ///
6360    /// Both consumers use `Option::unwrap_or_else(|| <fallback>)` to
6361    /// substitute a `caixa.nome`-derived placeholder when the slot is
6362    /// absent (`None` → the fallback fires); a `Some("")` *skips the
6363    /// fallback* and silently passes the empty string through to
6364    /// `Chart.yaml description: ""` / a blank chart `README.md`
6365    /// header. Helm's chart spec requires a non-empty `description:`
6366    /// field on `apiVersion: v2` charts (`helm lint` surfaces it as
6367    /// `WARNING [chart.metadata.description]: description is required`),
6368    /// so the empty `Some("")` silently lands in the rendered
6369    /// artifacts and breaks at `helm lint` / `helm install` time far
6370    /// from the source `caixa.lisp`, with no field naming the
6371    /// offending `:descricao`.
6372    ///
6373    /// `None` (the canonical "omit the slot to defer to the renderer's
6374    /// `caixa.nome`-derived fallback" shape) is accepted trivially —
6375    /// the gate is a no-op when the author didn't declare a value.
6376    /// `Some("")` is gated by the narrower
6377    /// [`ManifestError::DescricaoEmpty`] arm, mirroring the empty-arm
6378    /// shape every peer per-axis empty gate uses
6379    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6380    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6381    /// [`ManifestError::RepositorioEmpty`]).
6382    ///
6383    /// Universal-axis (every kind carries `:descricao`), so wired at
6384    /// the caixa-build gate alongside the peer universal gates
6385    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6386    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6387    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6388    /// [`Self::validate_code_paths`] — before the kind-coherence
6389    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6390    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6391    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6392    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6393    /// specific slot sets.
6394    ///
6395    /// Past the empty arm the gate enforces the chart-description
6396    /// shape predicate via [`crate::render::is_chart_description_shape`]:
6397    /// the structural single-line UTF-8 floor every realistic chart
6398    /// description in the wild matches — 1..=512 bytes, no leading
6399    /// or trailing whitespace, no ASCII control characters anywhere
6400    /// (`0x00..=0x1F` plus `0x7F` DEL — banning tab, newline,
6401    /// carriage return, and every other control byte), Unicode
6402    /// continuation bytes accepted (the canonical fixtures carry
6403    /// `→` and `—`). Closes the canonical paste-from-doc footguns
6404    /// the bare empty-arm gate left open: paste-from-aligned-doc
6405    /// leading / trailing whitespace (`" Checkout flow."`,
6406    /// `"Checkout flow. "`), paste-from-multiline-doc newline
6407    /// (`"Checkout\nflow."`), paste-from-Windows-CRLF-doc CR
6408    /// (`"Checkout\rflow."`), tab-from-aligned-doc
6409    /// (`"Checkout\tflow."`), and paste-from-binary-blob NUL / BEL /
6410    /// ESC / DEL bytes. Mirrors the shape-predicate cascade
6411    /// [`Self::validate_repositorio`] / [`Self::validate_licenca`] /
6412    /// [`Self::validate_edicao`] establish past their own empty arms
6413    /// on the sibling universal-axis `Option<String>` Caixa-level
6414    /// value-shape surfaces.
6415    ///
6416    /// The empty-first cascade discipline mirrors every peer per-axis
6417    /// identity gate: [`ManifestError::DescricaoEmpty`] runs before
6418    /// [`ManifestError::DescricaoInvalid`], so the narrower empty
6419    /// diagnostic surfaces on `Some("")` rather than the broader
6420    /// shape-predicate diagnostic — peer with how
6421    /// [`ManifestError::LicencaEmpty`] runs before
6422    /// [`ManifestError::LicencaInvalid`],
6423    /// [`ManifestError::EdicaoEmpty`] runs before
6424    /// [`ManifestError::EdicaoInvalid`],
6425    /// [`ManifestError::RepositorioEmpty`] runs before
6426    /// [`ManifestError::RepositorioInvalid`].
6427    pub fn validate_descricao(&self) -> Result<(), ManifestError> {
6428        let Some(s) = self.descricao() else {
6429            return Ok(());
6430        };
6431        if s.is_empty() {
6432            return Err(ManifestError::DescricaoEmpty);
6433        }
6434        crate::render::is_chart_description_shape(s)
6435            .map_err(|reason| ManifestError::descricao_invalid(s, reason))?;
6436        Ok(())
6437    }
6438
6439    /// Reject `:licenca` values that are the empty string. The flat
6440    /// `licenca: Option<String>` slot on [`Caixa`] is the universal
6441    /// SPDX-shaped license-expression axis every kind carries — the
6442    /// substrate routes the same string through the [`caixa-helm`]
6443    /// renderer's `build_readme` which folds it verbatim into the
6444    /// rendered `lareira-<nome>` Helm chart's `README.md` `## License`
6445    /// section (`caixa-helm/src/lib.rs:361`) via
6446    /// `caixa.licenca.clone().unwrap_or_else(|| "MIT".into())`. The
6447    /// fallback only fires on `None`; a `Some("")` *skips the
6448    /// fallback* and silently passes the empty string through to a
6449    /// chart `README.md` whose `License` section renders as the bare
6450    /// trailing period (`.\n`) — peer footgun with the
6451    /// `Some("")`-skips-`unwrap_or_else` shape the
6452    /// [`Self::validate_descricao`] and [`Self::validate_repositorio`]
6453    /// gates close on the sibling free-form-prose and git-URL axes.
6454    ///
6455    /// `None` (the canonical "omit the slot to defer to the
6456    /// renderer's `MIT` fallback" shape every existing fixture
6457    /// carries) is accepted trivially — the gate is a no-op when the
6458    /// author didn't declare a value. `Some("")` is gated by the
6459    /// narrower [`ManifestError::LicencaEmpty`] arm, mirroring the
6460    /// empty-arm shape every peer per-axis empty gate uses
6461    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6462    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6463    /// [`ManifestError::RepositorioEmpty`],
6464    /// [`ManifestError::DescricaoEmpty`]).
6465    ///
6466    /// Universal-axis (every kind carries `:licenca`), so wired at
6467    /// the caixa-build gate alongside the peer universal gates
6468    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6469    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6470    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6471    /// [`Self::validate_descricao`] / [`Self::validate_code_paths`]
6472    /// — before the kind-coherence gates
6473    /// ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6474    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6475    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6476    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6477    /// specific slot sets.
6478    ///
6479    /// Past the empty arm the gate enforces the SPDX-expression shape
6480    /// predicate via [`crate::render::is_spdx_expression_shape`]: the
6481    /// structural alphabet floor every realistic SPDX expression in
6482    /// the wild uses — ASCII alphanumeric plus `.`, `-`, `+`, `(`,
6483    /// `)`, `:` (the `DocumentRef-…:LicenseRef-…` separator), and a
6484    /// single ASCII space (token separator). Closes the canonical
6485    /// paste-from-doc footguns the bare empty-arm gate left open:
6486    /// paste-from-doc whitespace (`"MIT "`, `" MIT"`), paste-from-
6487    /// multiline-doc CRLF (`"MIT\n"`), tab-from-aligned-doc
6488    /// (`"MIT\tOR Apache-2.0"`), non-ASCII smart-quote paste,
6489    /// underscore-instead-of-hyphen typo (`"Apache_2.0"`),
6490    /// comma-instead-of-`OR`-keyword colloquial idiom (`"MIT,
6491    /// Apache-2.0"`), slash-dual-license colloquial idiom (`"MIT/
6492    /// Apache-2.0"`), and semicolon-list-separator confusion
6493    /// (`"MIT; Apache-2.0"`). Mirrors the shape-predicate cascade
6494    /// [`Self::validate_repositorio`] / [`Self::validate_edicao`]
6495    /// establish past their own empty arms.
6496    ///
6497    /// The empty-first cascade discipline mirrors every peer per-axis
6498    /// identity gate: [`ManifestError::LicencaEmpty`] runs before
6499    /// [`ManifestError::LicencaInvalid`], so the narrower empty
6500    /// diagnostic surfaces on `Some("")` rather than the broader
6501    /// shape-predicate diagnostic — peer with how
6502    /// [`ManifestError::EdicaoEmpty`] runs before
6503    /// [`ManifestError::EdicaoInvalid`],
6504    /// [`ManifestError::RepositorioEmpty`] runs before
6505    /// [`ManifestError::RepositorioInvalid`].
6506    ///
6507    /// A future tightening on this axis can extend the alphabet
6508    /// floor into a full SPDX expression parser + license-id
6509    /// allowlist (rejecting alphabet-valid values that don't name a
6510    /// real SPDX license identifier — e.g., `"NotAReal"` is
6511    /// alphabet-valid but no `NotAReal` license-id exists). That
6512    /// parser only becomes meaningful past a real SPDX-spec
6513    /// dependency; this gate establishes the structural floor by
6514    /// refusing every non-SPDX-alphabet value at validate time.
6515    pub fn validate_licenca(&self) -> Result<(), ManifestError> {
6516        let Some(s) = self.licenca() else {
6517            return Ok(());
6518        };
6519        if s.is_empty() {
6520            return Err(ManifestError::LicencaEmpty);
6521        }
6522        crate::render::is_spdx_expression_shape(s)
6523            .map_err(|reason| ManifestError::licenca_invalid(s, reason))?;
6524        Ok(())
6525    }
6526
6527    /// Reject `:edicao` values that are the empty string. The flat
6528    /// `edicao: Option<String>` slot on [`Caixa`] is the universal
6529    /// language-edition axis every kind carries — it determines the
6530    /// tatara-lisp macro surface + compatibility flags the substrate
6531    /// applies when building a caixa, and lands verbatim in the
6532    /// `Caixa::template` author-time scaffold (the canonical
6533    /// `:edicao "2026"` line every `feira init` emits via
6534    /// [`Caixa::template`] at `caixa-core/src/manifest.rs:1193`) and
6535    /// in every renderer-side fixture (`caixa-helm/src/lib.rs:375`,
6536    /// `caixa-flux/src/lib.rs:445`, `caixa-mesh/src/lib.rs:629`,
6537    /// `caixa-core/src/render.rs:2510`) via
6538    /// `edicao: Some("2026".into())`.
6539    ///
6540    /// `None` (the canonical "omit the slot to defer to the
6541    /// substrate's default edition" shape every existing
6542    /// [`caixa-resolver`] integration test fixture carries via
6543    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6544    /// is accepted trivially — the gate is a no-op when the author
6545    /// didn't declare a value. `Some("")` is gated by the narrower
6546    /// [`ManifestError::EdicaoEmpty`] arm, mirroring the empty-arm
6547    /// shape every peer per-axis empty gate uses
6548    /// ([`ManifestError::NomeEmpty`], [`ManifestError::VersaoEmpty`],
6549    /// [`ManifestError::EtiquetaEmpty`], [`ManifestError::AutorEmpty`],
6550    /// [`ManifestError::RepositorioEmpty`],
6551    /// [`ManifestError::DescricaoEmpty`], [`ManifestError::LicencaEmpty`]).
6552    ///
6553    /// Universal-axis (every kind carries `:edicao`), so wired at
6554    /// the caixa-build gate alongside the peer universal gates
6555    /// [`Self::validate_nome`] / [`Self::validate_versao`] /
6556    /// [`Self::validate_deps`] / [`Self::validate_etiquetas`] /
6557    /// [`Self::validate_autores`] / [`Self::validate_repositorio`] /
6558    /// [`Self::validate_descricao`] / [`Self::validate_licenca`] /
6559    /// [`Self::validate_code_paths`] — before the kind-coherence
6560    /// gates ([`crate::LayoutError::MeshSlotsOnNonAplicacao`] /
6561    /// [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] /
6562    /// [`crate::LayoutError::ServicoSlotsOnNonServico`] /
6563    /// [`crate::LayoutError::ForeignCodeSlot`]) which fence kind-
6564    /// specific slot sets.
6565    ///
6566    /// Past the empty arm the gate enforces the canonical year-shape
6567    /// predicate: every documented tatara-lisp edition is a 4-digit
6568    /// ASCII decimal year (`"2026"` is the only edition currently
6569    /// minted; future-introduced siblings will follow the same
6570    /// shape, peer with Cargo's `[package] edition` grammar which
6571    /// every value Cargo has ever accepted matches — `"2015"`,
6572    /// `"2018"`, `"2021"`, `"2024"`). Any value that's not exactly
6573    /// 4 ASCII decimal bytes is rejected with the narrower
6574    /// [`ManifestError::EdicaoInvalid`] arm, mirroring the
6575    /// shape-predicate cascade [`Self::validate_repositorio`]
6576    /// establishes past its own empty arm
6577    /// ([`ManifestError::RepositorioEmpty`] →
6578    /// [`ManifestError::RepositorioInvalid`]). Closes the canonical
6579    /// paste-from-doc footguns the bare empty-arm gate left open:
6580    ///
6581    ///   - leading / trailing whitespace from a paste-from-doc
6582    ///     (`"2026 "`, `" 2026"`)
6583    ///   - control characters / CRLF from a paste-from-multiline-doc
6584    ///     (`"2026\n"`)
6585    ///   - non-ASCII look-alikes from a fullwidth keyboard
6586    ///     (`"2026"`) which would silently land as a non-ASCII
6587    ///     string in the rendered caixa.lisp
6588    ///   - free-form non-year values (`"x"`, `"latest"`,
6589    ///     `"nightly"`) that have no operational meaning on the
6590    ///     substrate's build-time edition selector
6591    ///   - leading non-digit prefixes (`"v2026"`, `"e2026"`,
6592    ///     `"r2026"`) — common version-tag idioms that don't apply
6593    ///     to the year-shaped edition axis
6594    ///   - decimal-shaped values (`"2026.1"`, `"2026.0"`) — every
6595    ///     edition is a year, not a fractional version
6596    ///   - wrong-length numeric values (`"26"`, `"202"`, `"20260"`,
6597    ///     `"00026"`) that don't name a year
6598    ///
6599    /// `None` (the canonical "omit the slot to defer to the
6600    /// substrate's default edition" shape every existing
6601    /// [`caixa-resolver`] integration test fixture carries via
6602    /// `edicao: None` — see `caixa-resolver/tests/git_integration.rs`)
6603    /// is accepted trivially — the gate is a no-op when the author
6604    /// didn't declare a value. The empty-first cascade discipline
6605    /// mirrors every peer per-axis identity gate:
6606    /// [`ManifestError::EdicaoEmpty`] runs before
6607    /// [`ManifestError::EdicaoInvalid`], so the narrower empty
6608    /// diagnostic surfaces on `Some("")` rather than the broader
6609    /// shape-predicate diagnostic — peer with how
6610    /// [`ManifestError::NomeEmpty`] runs before
6611    /// [`ManifestError::NomeInvalid`],
6612    /// [`ManifestError::VersaoEmpty`] runs before
6613    /// [`ManifestError::VersaoInvalid`],
6614    /// [`ManifestError::RepositorioEmpty`] runs before
6615    /// [`ManifestError::RepositorioInvalid`].
6616    ///
6617    /// A future tightening on this axis can extend the shape
6618    /// predicate into a known-edition allowlist (rejecting
6619    /// year-shaped values that don't name a tatara-lisp edition
6620    /// the substrate actually understands — e.g., `"1999"` is
6621    /// year-shaped but no `1999` edition exists). That allowlist
6622    /// only becomes meaningful past the introduction of a sibling
6623    /// edition to `"2026"`; this gate establishes the structural
6624    /// floor by refusing every non-year-shaped value at validate
6625    /// time.
6626    pub fn validate_edicao(&self) -> Result<(), ManifestError> {
6627        let Some(s) = self.edicao() else {
6628            return Ok(());
6629        };
6630        if s.is_empty() {
6631            return Err(ManifestError::EdicaoEmpty);
6632        }
6633        if s.len() != 4 || !s.bytes().all(|b| b.is_ascii_digit()) {
6634            return Err(ManifestError::edicao_invalid(
6635                s,
6636                "must be a 4-digit ASCII decimal year (canonical \"2026\")",
6637            ));
6638        }
6639        Ok(())
6640    }
6641
6642    /// Compose the supervisor-related flat slots into a single
6643    /// [`SupervisorSpec`] for validation. Returns `None` when the
6644    /// caixa isn't a `:kind Supervisor`.
6645    ///
6646    /// The flat representation in [`Caixa`] keeps tatara-lisp authoring
6647    /// simple (one form, no nested `:supervisor (…)` block); this view
6648    /// is the "typed shape" the operator + supervisor reconciler
6649    /// consume.
6650    #[must_use]
6651    pub fn supervisor_view(&self) -> Option<SupervisorSpec> {
6652        if !self.kind().is_supervisor() {
6653            return None;
6654        }
6655        // Fold through the shared `supervisor::duration_codec::parse`
6656        // — the same parser the serde-routed `with = "duration_codec"`
6657        // on `SupervisorSpec::restart_window`, the `:politicas
6658        // :timeout` codec, and the `:politicas :circuit-breaker
6659        // :window` codec all consume. The prior inline f64-shaped
6660        // duplicate (`parse_window_inline`) admitted every magnitude
6661        // the integer-magnitude gate (1c55a2a) rejects on the three
6662        // serde-routed siblings — `"1.5s"`, `"1.0s"`, `"0.5m"`,
6663        // `"+30s"`, `"-30s"` — and silently dropped malformed input as
6664        // `None` (i.e. "no reset"), divergent from the shared codec's
6665        // integer-magnitude discipline by construction. The fold
6666        // closes the divergence: every value the typed
6667        // `SupervisorSpec` carries past `supervisor_view` is in the
6668        // shared codec's accepted set. The `.ok()` here preserves the
6669        // existing soft-swallow shape on this view-construction path;
6670        // the new [`Caixa::validate_restart_window`] (sibling of
6671        // [`Self::validate_nome`] / [`Self::validate_versao`]) names
6672        // the offending raw string at build time so authoring tools
6673        // (`feira lint`, the future layout-side wire-up) surface a
6674        // self-locating diagnostic instead of a silently dropped
6675        // window.
6676        let restart_window = self
6677            .restart_window()
6678            .and_then(|s| crate::supervisor::duration_codec::parse(s).ok());
6679        Some(SupervisorSpec {
6680            // Route the author-omitted `:estrategia` arm through the
6681            // substrate-canonical
6682            // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
6683            // `pub const` rather than the transitively-derived
6684            // [`RestartStrategy::default`] route the prior
6685            // `.unwrap_or_default()` fold reached for — one source of
6686            // truth for the Erlang/OTP `one_for_one` half of Learn You
6687            // Some Erlang's `{one_for_one, intensity, 5, 60}` worker-
6688            // supervisor canonical default that also backs the
6689            // [`crate::supervisor::Default for RestartStrategy`] impl
6690            // and the [`crate::supervisor::Default for SupervisorSpec`]
6691            // impl's struct-literal `estrategia` field, all now routed
6692            // through the same lifted constant. Prior to the lift the
6693            // composition site carried `.unwrap_or_default()` with no
6694            // compile-time link back to the shared OTP-canonical
6695            // default that the peer paired
6696            // `.unwrap_or(SUPERVISOR_MAX_RESTARTS_DEFAULT)` (b698ec0)
6697            // arm on the sibling `:max-restarts` axis routes through —
6698            // so a future rebrand of the OTP-canonical strategy default
6699            // (a widening to `rest_for_one` once the substrate
6700            // discovers startup-order-coupled child cohorts as the more
6701            // common shape, a per-cluster overlay the operator pins
6702            // through the MESH-COMPOSITION §III.2 supervision-canary
6703            // `:estrategia-overrides` roadmap slot) would have had to
6704            // migrate the paired `MaxIntensity` + `Period` halves
6705            // through the lifted constants and the `one_for_one` half
6706            // through a `RestartStrategy::default()` route in lockstep
6707            // or the three halves of the same OTP-canonical default
6708            // would silently drift out of pairing. Byte-parity against
6709            // the lifted constant closes the split. Pinned by
6710            // [`supervisor_view_estrategia_fallback_routes_through_lifted_default`]
6711            // in the tests module.
6712            estrategia: self
6713                .estrategia()
6714                .unwrap_or(crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT),
6715            // Route the author-omitted `:max-restarts` arm through the
6716            // substrate-canonical [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`]
6717            // typed `pub const` rather than the raw `5` literal — one
6718            // source of truth for the Erlang/OTP-canonical
6719            // `{intensity, 5, 60}` `MaxIntensity` default that also
6720            // backs the serde-side wire-format author-omitted arm on
6721            // [`crate::supervisor::SupervisorSpec::max_restarts`] via
6722            // `#[serde(default = "default_max_restarts")]` and the
6723            // [`Default for SupervisorSpec`] impl's struct-literal
6724            // default field. Prior to the lift the composition site
6725            // carried a raw `5` with no compile-time link back to the
6726            // serde-side default, so a future rebrand of the OTP-
6727            // canonical default (a tightening to Elixir's `3`, a
6728            // widening to a per-cluster overlay the operator pins
6729            // through the MESH-COMPOSITION §III.2 supervision-canary
6730            // `:supervisor :max-restarts-overrides` roadmap slot)
6731            // would have had to be threaded through both open-coded
6732            // copies in lockstep or the wire-format author-omitted arm
6733            // and this view-construction author-omitted arm would
6734            // silently disagree on which restart-budget an omitted
6735            // `:max-restarts` resolves to. Pinned by
6736            // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
6737            // in the tests module.
6738            max_restarts: self
6739                .max_restarts()
6740                .unwrap_or(crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT),
6741            restart_window,
6742            children: self.children().to_vec(),
6743        })
6744    }
6745
6746    /// A minimal starter manifest emitted by `feira init`.
6747    #[must_use]
6748    pub fn template(nome: &str) -> String {
6749        format!(
6750            "(defcaixa\n  \
6751               :nome        {nome:?}\n  \
6752               :versao      \"0.1.0\"\n  \
6753               :kind        Biblioteca\n  \
6754               :edicao      \"2026\"\n  \
6755               :descricao   \"FIXME — describe this caixa\"\n  \
6756               :autores     ()\n  \
6757               :etiquetas   ()\n  \
6758               :deps        ()\n  \
6759               :deps-dev    ()\n  \
6760               :bibliotecas (\"lib/{nome}.lisp\"))\n"
6761        )
6762    }
6763
6764    /// Serialize to a canonical `caixa.lisp` source — suitable for writing
6765    /// back after mutation (e.g. `feira add`).
6766    ///
6767    /// Goes through serde JSON → canonical Sexp → per-field pretty print.
6768    /// The derive-macro `compile_from_sexp` path is the inverse, so any
6769    /// `Caixa` round-trips through `to_lisp` + `from_lisp`.
6770    #[must_use]
6771    pub fn to_lisp(&self) -> String {
6772        let json = serde_json::to_value(self).expect("Caixa serialize");
6773        let sexp = tatara_lisp::domain::json_to_sexp(&json);
6774        let tatara_lisp::Sexp::List(items) = sexp else {
6775            return format!("(defcaixa {sexp})\n");
6776        };
6777        let mut out = String::from("(defcaixa");
6778        let mut i = 0;
6779        while i + 1 < items.len() {
6780            out.push_str("\n  ");
6781            out.push_str(&items[i].to_string());
6782            out.push(' ');
6783            out.push_str(&items[i + 1].to_string());
6784            i += 2;
6785        }
6786        out.push_str(")\n");
6787        out
6788    }
6789}
6790
6791/// Errors raised by top-level [`Caixa`] validators that don't fit
6792/// the per-axis [`DepError`] / [`crate::AplicacaoError`] /
6793/// [`crate::SupervisorError`] / [`crate::LayoutError`] families —
6794/// the Caixa's own identity axes (`:nome`, `:versao`) that flow
6795/// through every substrate-side artifact's `metadata.name` /
6796/// version derivation.
6797///
6798/// A future top-level sum (the M4 `CaixaError` the [`DepError`]
6799/// doc-comment anticipates) can hold one of each per-axis error
6800/// family without reshaping individual diagnostics; this enum is
6801/// the first such per-Caixa-identity family.
6802#[derive(Debug, Error, PartialEq, Eq)]
6803pub enum ManifestError {
6804    #[error(
6805        ":nome is empty (every caixa must name itself; the value flows \
6806         into every K8s artifact's `metadata.name` derivation and into \
6807         the default `lib/<nome>.lisp` / `exe/<nome>` layout paths)"
6808    )]
6809    NomeEmpty,
6810    #[error(
6811        ":nome {nome:?} is not a valid DNS-1123 label: {reason} (the K8s \
6812         apiserver enforces this rule on every `metadata.name` the \
6813         caixa's substrate-side renderers derive from `:nome` — the \
6814         `lareira-<nome>` Helm chart name, the programs.yaml entry \
6815         name, the `LABEL_APLICACAO` label value, the `<aplicacao>-<de>-to-<para>` \
6816         CiliumNetworkPolicy name, the `<aplicacao>-<para>` HTTPRoute \
6817         name; use a lowercase alphanumeric + hyphen identifier like \
6818         `\"checkout\"` or `\"cart-v2\"`)"
6819    )]
6820    NomeInvalid { nome: String, reason: String },
6821    #[error(
6822        ":nome {nome:?} overflows the joint-length budget on the canonical \
6823         `lareira-<nome>` chart-name shape: {reason} (every per-Servico / \
6824         per-Aplicacao renderer the substrate carries — `caixa-helm`'s \
6825         `Chart.yaml::name`, `caixa-flux`'s `cluster_bundle` HelmRelease \
6826         `chart:` slot, `caixa-tatara`'s `release_name` + \
6827         `oci://<registry>/lareira-<nome>` chart ref — derives the same \
6828         joint name through the canonical `lareira_chart_name` helper, and \
6829         Helm's `Chart.yaml::name` admission rule + the K8s apiserver's \
6830         DNS-1123 label cap on every chart-name-derived `metadata.name` \
6831         reject any joint name exceeding 63 bytes; the narrower \
6832         `:nome` shape (`NomeInvalid`) gates the bare-`:nome` budget, this \
6833         arm gates the chart-name budget downstream renderers inherit)"
6834    )]
6835    NomeChartNameBudgetExceeded { nome: String, reason: String },
6836    #[error(
6837        ":versao is empty (every caixa must pin its own version; the value flows \
6838         into the `lareira-<nome>` Helm chart's `Chart.yaml` version + appVersion, \
6839         the `feira publish` `v<versao>` git tag, the OCI image's `:v<versao>` / \
6840         `:latest` tags, the lacre closure's `concrete_versao`, and the \
6841         `:upgrade-from :from` peers — use a SemVer-2 literal like `\"0.1.0\"`)"
6842    )]
6843    VersaoEmpty,
6844    #[error(
6845        ":versao {versao:?} is not a valid SemVer-2 version: {reason} (the substrate \
6846         consumes this string as `semver::Version` — three-part `MAJOR.MINOR.PATCH` \
6847         with optional `-prerelease` and `+build` — across every artifact derived \
6848         from `:versao`: the `lareira-<nome>` Helm chart's `Chart.yaml` version + \
6849         appVersion (Helm SemVer-2-strict), the `feira publish` `v<versao>` git tag, \
6850         the OCI image's `:v<versao>` tag, the lacre closure's `concrete_versao`, \
6851         and the `:upgrade-from :from` peers that match against this exact shape; \
6852         use a literal like `\"0.1.0\"`, `\"0.2.0-rc.1\"`, or `\"1.0.0+build.42\"` — \
6853         not a git-tag-shape like `\"v0.1.0\"`, a docker-tag-shape like `\"latest\"`, \
6854         a requirement-shape like `\"^0.1\"`, or a four-part `\"0.1.0.0\"`)"
6855    )]
6856    VersaoInvalid { versao: String, reason: String },
6857    #[error(
6858        ":restart-window {restart_window:?} is not a valid duration: {reason} (the \
6859         substrate consumes this string through the shared \
6860         `supervisor::duration_codec` — the same parser routed via `with = \
6861         \"duration_codec\"` onto the typed `SupervisorSpec::restart_window`, \
6862         `:politicas :timeout`, and `:politicas :circuit-breaker :window` slots; \
6863         the canonical authoring form is `<integer><unit>` where the unit is one \
6864         of `ms` / `s` / `m` / `h` and the magnitude has no decimal point and no \
6865         leading `+` / `-` sign — e.g. `\"60s\"`, `\"5m\"`, `\"1h\"`, `\"500ms\"`. \
6866         Without this gate a malformed `:restart-window` silently produced a \
6867         supervisor with `restart_window: None` (\"never reset\"), turning OTP's \
6868         `MaxIntensity / Period` invariant into a never-reset supervisor far from \
6869         the source `caixa.lisp`; the gate moves the diagnostic to the manifest \
6870         layer with the offending value named verbatim. Omit the slot entirely to \
6871         express \"no reset\"; carry a positive integer duration to express the \
6872         sliding window)"
6873    )]
6874    RestartWindowMalformed {
6875        restart_window: String,
6876        reason: String,
6877    },
6878    #[error(
6879        "{slot} entry is an empty path string — every {slot} entry must name \
6880         a file relative to the caixa root; omit the entry to omit the file \
6881         (the layout checker's `root.join(\"\")` resolves to the caixa root \
6882         itself, so an empty entry silently aliases the project root as a \
6883         declared {slot} file, then fails downstream at parse / existence \
6884         time with a diagnostic that names the root rather than the offending \
6885         entry)"
6886    )]
6887    CodePathEmpty { slot: &'static str },
6888    #[error(
6889        "{slot} entry {} is an absolute path — entries must be relative to \
6890         the caixa root, since `Path::join` replaces the base with an absolute \
6891         right-hand side and `root.join(\"/abs/...\")` resolves to \"/abs/...\" \
6892         outside the caixa root sandbox; rewrite the entry as a relative path \
6893         under the caixa root (e.g. `\"lib/<name>.lisp\"`, `\"exe/<name>\"`, \
6894         `\"servicos/<name>.computeunit.yaml\"`)",
6895        path.display()
6896    )]
6897    CodePathAbsolute { slot: &'static str, path: PathBuf },
6898    #[error(
6899        "{slot} entry {} contains a `..` component — entries must not traverse \
6900         above the caixa root (the layout's `starts_with(<dir>)` fence on \
6901         `:exe` / `:servicos` is component-aware, not canonical-path-aware, \
6902         so a mid-path `..` silently traverses the sandbox; `:bibliotecas` \
6903         has no such fence, so a leading `..` escapes unconditionally if the \
6904         resolved target happens to exist)",
6905        path.display()
6906    )]
6907    CodePathParentEscape { slot: &'static str, path: PathBuf },
6908    #[error(
6909        "{slot} entry {} does not terminate in the `.lisp` extension — every \
6910         `:bibliotecas` entry is a tatara-lisp source file the `feira build` \
6911         loop reads through `tatara_lisp::read` at parse time, so any other \
6912         extension (`.rs`, `.txt`, `.lisp.bak`) or no-extension shape is \
6913         structurally a parser error far from the source caixa.lisp, with \
6914         no field naming the offending `:bibliotecas` entry. Pin a relative \
6915         path under the caixa root whose terminating extension is \
6916         lowercase-`.lisp` (e.g. `\"lib/<name>.lisp\"`, \
6917         `\"lib/handlers.lisp\"`) — the same file-type contract the peer \
6918         `:behavior :on-*` (c97815a) and `:upgrade-from :state-change :script` \
6919         (33cc830) axes already carry through the same lifted \
6920         `is_lisp_extension` predicate",
6921        path.display()
6922    )]
6923    CodePathNonLispExtension { slot: &'static str, path: PathBuf },
6924    #[error(
6925        "{slot} entry {} does not terminate in the `.computeunit.yaml` \
6926         compound suffix — every `:servicos` entry is a typed `ComputeUnit` \
6927         CR YAML file the peer caixa-helm / caixa-flux renderers consume \
6928         through `serde_yaml::from_str` at chart / FluxCD bundle render \
6929         time, so any other extension (`.yaml`, `.yml`, `.json`, the \
6930         off-by-one-segment `.computeunit-yaml`, the editor-backup \
6931         `.computeunit.yaml.bak`) or no-extension shape is structurally a \
6932         YAML-parser error / `ComputeUnit` schema-mismatch far from the \
6933         source caixa.lisp, with no field naming the offending `:servicos` \
6934         entry. Pin a relative path under the caixa root whose terminating \
6935         compound suffix is lowercase-`.computeunit.yaml` (e.g. \
6936         `\"servicos/<name>.computeunit.yaml\"`, \
6937         `\"servicos/hello-rio.computeunit.yaml\"`) — the same file-type \
6938         contract the sibling `:bibliotecas` axis (64772a9) already carries \
6939         on the tatara-lisp-source axis through the peer lifted \
6940         `is_lisp_extension` predicate, here on the compound-suffix axis \
6941         `Path::extension` can't express on its own through the lifted \
6942         `is_computeunit_yaml_extension` predicate",
6943        path.display()
6944    )]
6945    CodePathNonComputeUnitYamlExtension { slot: &'static str, path: PathBuf },
6946    #[error(
6947        "{slot} entry {} appears more than once (the code-path list is \
6948         a set, not a multiset; every peer Vec-shaped author-supplied \
6949         list past validate is set-not-multiset — `:membros :caixa`, \
6950         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
6951         `:children :caixa`, `:deps` / `:deps-dev` `:nome`, \
6952         `:upgrade-from :from`, `:etiquetas`, `:autores` — and the three \
6953         code-path lists are the last Vec-shaped author-supplied slots on \
6954         the typed Caixa surface still admitting a duplicate entry. \
6955         `:bibliotecas` duplicates re-parse the same file at \
6956         `feira build` time and silently mask the author's intent to \
6957         declare a *second* biblioteca; `:exe` duplicates collide on the \
6958         flake `packages.<name>` derivation key at the future \
6959         `caixa-flake` materializer; `:servicos` duplicates surface as the \
6960         narrower [`caixa-helm`] / [`caixa-flux`] `UnsupportedServicoCount` \
6961         rejection far from the source `caixa.lisp`. Drop the duplicate \
6962         or rename it to the actual second file intended)",
6963        path.display()
6964    )]
6965    CodePathDuplicate { slot: &'static str, path: PathBuf },
6966    #[error(
6967        ":etiquetas entry is empty (every tag must carry a non-empty \
6968         registry-search identifier; the empty entry has no operational \
6969         meaning — it indexes nothing in the future caixa-registry search \
6970         axis and clutters the rendered Helm `Chart.yaml` `keywords:` array \
6971         with a no-op tag; omit the entry to express \"no tag on this \
6972         position\")"
6973    )]
6974    EtiquetaEmpty,
6975    #[error(
6976        ":etiquetas entry {etiqueta:?} appears more than once (the \
6977         registry-search tag set is a set, not a multiset; duplicate \
6978         entries are silently dedup'd by caixa-helm's `BTreeSet` collect \
6979         at chart render — a \"second wins / one silently disappears\" \
6980         shape divergent from every peer typed-graph set gate \
6981         (`:membros :caixa`, `:placement :clusters`, `:entrada :paths`, \
6982         `:contratos`, `:deps :nome`, `:upgrade-from :from`); drop the \
6983         duplicate or rename it to the actual tag intended)"
6984    )]
6985    EtiquetaDuplicate { etiqueta: String },
6986    #[error(
6987        ":etiquetas entry {etiqueta:?} is not a valid chart-keyword shape: \
6988         {reason} (the substrate consumes this string through the shared \
6989         `crate::render::is_chart_keyword_shape` predicate — the same \
6990         Cargo crates.io `[package] keywords` grammar entry shape: 1..=20 \
6991         bytes, starts with an ASCII letter, ASCII alphanumeric / `_` / `-` \
6992         continuation. The canonical authoring shapes are short kebab-case \
6993         identifiers like `\"mesh\"`, `\"wasm\"`, `\"tatara-lisp\"`, \
6994         `\"hello-world\"`, `\"caixa-servico\"`, `\"infrastructure\"`. \
6995         Without this gate a malformed `:etiquetas` entry (paste-from-doc \
6996         leading / trailing whitespace `\" mesh\"` / `\"mesh \"`; \
6997         paste-from-multiline-doc newline `\"mesh\\nhttp\"`; \
6998         paste-from-Windows-CRLF-doc CR; CSV-list-separator confusion \
6999         `\"mesh,http,grpc\"` — the author meant to author three separate \
7000         list entries; path-separator confusion `\"caixa/servico\"`; \
7001         namespace-suffix `\"http.1\"`; leading-digit `\"1foo\"`; \
7002         kebab-leak `\"-foo\"`; snake-leak `\"_foo\"`; non-ASCII \
7003         `\"café\"` — every legitimate search tag is strict ASCII; \
7004         paste-from-binary-blob NUL / BEL / ESC / DEL byte) silently \
7005         passed `from_lisp` + `validate_etiquetas` + \
7006         `StandardLayout::verify` and landed in the rendered \
7007         `lareira-<nome>` Helm chart's `Chart.yaml keywords:` array as a \
7008         malformed search tag — Artifact Hub's keyword index + the future \
7009         caixa-registry's keyword index would either silently drop the \
7010         tag or fail to index it far from the source caixa.lisp; the gate \
7011         moves the diagnostic to the manifest layer with the offending \
7012         value named verbatim)"
7013    )]
7014    EtiquetaInvalid { etiqueta: String, reason: String },
7015    #[error(
7016        ":autores entry is empty (every maintainer must carry a non-empty \
7017         identifier; the empty entry has no operational meaning — it \
7018         identifies no one in the substrate's authorship index and renders \
7019         as `maintainers: [{{name: \"\", email: null}}]` in the Helm chart's \
7020         `Chart.yaml`, a no-op maintainer the substrate cannot route to; \
7021         omit the entry to express \"no maintainer on this position\")"
7022    )]
7023    AutorEmpty,
7024    #[error(
7025        ":autores entry {autor:?} appears more than once (the maintainer \
7026         set is a set, not a multiset; unlike `:etiquetas`, caixa-helm's \
7027         `maintainers:` rendering does *no* dedup — duplicate entries \
7028         stack verbatim in `Chart.yaml` as two identical \
7029         `Maintainer {{ name, email: None }}` records, divergent from every \
7030         peer typed-graph set gate (`:etiquetas`, `:membros :caixa`, \
7031         `:placement :clusters`, `:entrada :paths`, `:contratos`, \
7032         `:deps :nome`, `:upgrade-from :from`); drop the duplicate or \
7033         rename it to the actual author intended)"
7034    )]
7035    AutorDuplicate { autor: String },
7036    #[error(
7037        ":autores entry {autor:?} is not a valid chart-maintainer-name shape: \
7038         {reason} (the substrate consumes this string through the shared \
7039         `crate::render::is_chart_maintainer_name_shape` predicate — the same \
7040         single-line-UTF-8 floor every realistic chart maintainer name carries: \
7041         1..=128 bytes, no leading or trailing whitespace, no ASCII control \
7042         characters anywhere, Unicode bytes accepted. The canonical authoring \
7043         shapes are short single-line identifiers like `\"pleme-io\"`, \
7044         `\"Pleme Contributors\"`, `\"alice <alice@example.com>\"`, \
7045         `\"François Dupont\"`. Without this gate a malformed `:autores` entry \
7046         (paste-from-aligned-doc leading whitespace `\" pleme-io\"` / trailing \
7047         whitespace `\"pleme-io \"`; paste-from-multiline-doc newline \
7048         `\"alice\\nbob\"` — the author pasted a multi-line block of author \
7049         records into one entry instead of splitting into one entry per author; \
7050         paste-from-Windows-CRLF-doc carriage return `\"alice\\rbob\"`; \
7051         tab-from-aligned-doc `\"Pleme\\tContributors\"`; paste-from-binary-blob \
7052         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7053         `validate_autores` + `StandardLayout::verify` and landed in the \
7054         rendered `lareira-<nome>` Helm chart's `Chart.yaml maintainers:` array \
7055         as a YAML-illegal multi-line scalar or a silently-trimmed whitespace \
7056         round-trip — every chart-aware UI (`helm list`, `helm search`, \
7057         Artifact Hub maintainer index) would render the maintainer name in a \
7058         single-line column far from the source caixa.lisp; the gate moves the \
7059         diagnostic to the manifest layer with the offending value named \
7060         verbatim)"
7061    )]
7062    AutorInvalid { autor: String, reason: String },
7063    #[error(
7064        ":repositorio is the empty string (every published caixa names its \
7065         git source via a non-empty `:repositorio` locator — the value \
7066         flows verbatim into the rendered `lareira-<nome>` Helm chart's \
7067         `Chart.yaml` `home:` field via `caixa-helm` and into the FluxCD \
7068         `GitRepository.spec.url` via `caixa-flux`'s \
7069         `ClusterBundleOpts::for_caixa`; both consumers' \
7070         `Option::unwrap_or_else` fallbacks only fire when the slot is \
7071         `None`, so an empty `Some(\"\")` silently lands as `home: \"\"` / \
7072         `url: \"\"` in the rendered artifacts and breaks at `helm \
7073         template` / FluxCD source-controller reconcile time far from the \
7074         source caixa.lisp; omit the slot entirely to defer to the \
7075         renderer's `https://github.com/pleme-io/<nome>` / \
7076         `caixa.nome`-derived fallback, or carry a canonical authoring \
7077         shape like `\"github:org/repo\"`, `\"https://host/path\"`, \
7078         `\"ssh://[user@]host/path\"`, `\"git@host:path\"`, or \
7079         `\"file:///path\"`)"
7080    )]
7081    RepositorioEmpty,
7082    #[error(
7083        ":repositorio {repositorio:?} is not a valid git repo URL: {reason} \
7084         (the substrate consumes this string through the shared \
7085         `crate::render::is_git_repo_url` predicate — the same parser the \
7086         peer `:deps :fonte (:tipo git :repo …)` axis routes its `:repo` \
7087         value through via `DepSource::validate`; the canonical authoring \
7088         shapes are `\"github:org/repo\"` shorthand, `\"https://host/path\"` \
7089         / `\"ssh://[user@]host/path\"` / `\"git://host/path\"` / \
7090         `\"file:///path\"` URL schemes, or the `\"git@host:path\"` \
7091         scp-style SSH form. Without this gate a malformed `:repositorio` \
7092         (whitespace from a paste-from-doc; control characters / CRLF \
7093         from a paste-from-multiline-doc; a leading `-` from a \
7094         CLI-argument-injection footgun; a missing `:` separator from a \
7095         bare `org/repo` shape git treats as a relative filesystem path) \
7096         silently landed in the rendered `Chart.yaml home:` and the \
7097         FluxCD `GitRepository.spec.url` and broke at `git clone` / \
7098         FluxCD reconcile time far from the source caixa.lisp; the gate \
7099         moves the diagnostic to the manifest layer with the offending \
7100         value named verbatim)"
7101    )]
7102    RepositorioInvalid { repositorio: String, reason: String },
7103    #[error(
7104        ":descricao is the empty string (every published caixa names \
7105         its purpose via a non-empty `:descricao` summary — the value \
7106         flows verbatim into the rendered `lareira-<nome>` Helm \
7107         chart's `Chart.yaml` `description:` field via `caixa-helm`'s \
7108         `build_chart_yaml` and into the chart `README.md` header via \
7109         `build_readme`; both consumers' `Option::unwrap_or_else` \
7110         `caixa.nome`-derived fallbacks only fire when the slot is \
7111         `None`, so an empty `Some(\"\")` silently lands as \
7112         `description: \"\"` / a blank `README.md` header in the \
7113         rendered artifacts and breaks at `helm lint` time \
7114         (`WARNING [chart.metadata.description]: description is \
7115         required` on `apiVersion: v2` charts) far from the source \
7116         caixa.lisp; omit the slot entirely to defer to the \
7117         renderer's `\"Generated chart for caixa Servico <nome>\"` / \
7118         `\"caixa Servico <nome>\"` fallbacks, or carry a non-empty \
7119         summary like `\"Canonical Rust→wasm32-wasip2 caixa \
7120         Servico.\"`)"
7121    )]
7122    DescricaoEmpty,
7123    #[error(
7124        ":descricao {descricao:?} is not a valid chart-description shape: \
7125         {reason} (the substrate consumes this string through the shared \
7126         `crate::render::is_chart_description_shape` predicate — the same \
7127         single-line-UTF-8 floor every realistic chart description carries: \
7128         1..=512 bytes, no leading or trailing whitespace, no ASCII control \
7129         characters anywhere, Unicode prose bytes accepted. The canonical \
7130         authoring shapes are short single-line summaries like `\"Canonical \
7131         Rust→wasm32-wasip2 caixa Servico.\"`, `\"Checkout flow.\"`, \
7132         `\"AWS provider caixa for tatara-lisp\"`. Without this gate a \
7133         malformed `:descricao` (paste-from-aligned-doc leading whitespace \
7134         `\" Checkout flow.\"` / trailing whitespace `\"Checkout flow. \"`; \
7135         paste-from-multiline-doc newline `\"Checkout\\nflow.\"`; \
7136         paste-from-Windows-CRLF-doc carriage return `\"Checkout\\rflow.\"`; \
7137         tab-from-aligned-doc `\"Checkout\\tflow.\"`; paste-from-binary-blob \
7138         NUL / BEL / ESC / DEL byte) silently passed `from_lisp` + \
7139         `validate_descricao` + `StandardLayout::verify` and landed in the \
7140         rendered `lareira-<nome>` Helm chart's `Chart.yaml description:` \
7141         field + `README.md` header paragraph as a YAML-illegal multi-line \
7142         scalar or a silently-trimmed whitespace round-trip — every \
7143         chart-aware UI (`helm list`, `helm search`, Artifact Hub) would \
7144         render the description in a single-line column far from the source \
7145         caixa.lisp; the gate moves the diagnostic to the manifest layer \
7146         with the offending value named verbatim)"
7147    )]
7148    DescricaoInvalid { descricao: String, reason: String },
7149    #[error(
7150        ":licenca is the empty string (every published caixa names \
7151         its license via a non-empty `:licenca` SPDX expression — the \
7152         value flows verbatim into the rendered `lareira-<nome>` Helm \
7153         chart's `README.md` `## License` section via `caixa-helm`'s \
7154         `build_readme` at `caixa-helm/src/lib.rs:361`; the consumer's \
7155         `Option::unwrap_or_else(|| \"MIT\".into())` `MIT` fallback \
7156         only fires when the slot is `None`, so an empty `Some(\"\")` \
7157         silently lands as a bare trailing period in the rendered \
7158         chart `README.md` `License` section far from the source \
7159         caixa.lisp; omit the slot entirely to defer to the \
7160         renderer's `MIT` fallback, or carry a canonical SPDX \
7161         expression like `\"MIT\"`, `\"Apache-2.0\"`, \
7162         `\"Apache-2.0 OR MIT\"`)"
7163    )]
7164    LicencaEmpty,
7165    #[error(
7166        ":licenca {licenca:?} is not a valid SPDX expression shape: {reason} \
7167         (the substrate consumes this string through the shared \
7168         `crate::render::is_spdx_expression_shape` predicate — the same \
7169         alphabet-floor parser every peer per-axis value-shape gate routes \
7170         its value through; the canonical authoring shapes are single \
7171         license identifiers like `\"MIT\"`, `\"Apache-2.0\"`, `\"BSD-3-Clause\"`, \
7172         compound expressions like `\"Apache-2.0 OR MIT\"`, \
7173         `\"MIT AND BSD-3-Clause\"`, `\"(MIT OR Apache-2.0) AND ISC\"`, \
7174         license-with-exception forms like `\"Apache-2.0 WITH LLVM-exception\"`, \
7175         `+`-suffix variants like `\"GPL-2.0+\"`, and user-defined references \
7176         like `\"LicenseRef-MyLicense\"` / \
7177         `\"DocumentRef-doc:LicenseRef-MyLicense\"`. Without this gate a \
7178         malformed `:licenca` (paste-from-doc whitespace `\"MIT \"` / \
7179         `\" MIT\"`; paste-from-multiline-doc CRLF `\"MIT\\n\"`; \
7180         tab-from-aligned-doc `\"MIT\\tOR Apache-2.0\"`; non-ASCII byte from \
7181         a smart-quote paste; underscore-instead-of-hyphen typo \
7182         `\"Apache_2.0\"`; comma-instead-of-`OR`-keyword colloquial idiom \
7183         `\"MIT, Apache-2.0\"`; slash-dual-license colloquial idiom \
7184         `\"MIT/Apache-2.0\"`; semicolon-list-separator confusion \
7185         `\"MIT; Apache-2.0\"`) silently landed in the rendered chart \
7186         `README.md` `## License` section + a future SPDX-aware \
7187         `Chart.yaml license:` emitter would refuse the value at \
7188         `helm lint` time far from the source caixa.lisp; the gate moves \
7189         the diagnostic to the manifest layer with the offending value \
7190         named verbatim)"
7191    )]
7192    LicencaInvalid { licenca: String, reason: String },
7193    #[error(
7194        ":edicao is the empty string (every published caixa names \
7195         its language edition via a non-empty `:edicao` value — the \
7196         edition determines the tatara-lisp macro surface + \
7197         compatibility flags the substrate applies when building \
7198         the caixa; the canonical `Caixa::template` scaffold every \
7199         `feira init` emits carries `:edicao \"2026\"` verbatim and \
7200         every renderer-side fixture (`caixa-helm`, `caixa-flux`, \
7201         `caixa-mesh`) carries `edicao: Some(\"2026\".into())` by \
7202         construction, so an empty `Some(\"\")` silently lands as a \
7203         bare `(:edicao \"\")` line in the rendered `caixa.lisp` and \
7204         a future renderer-side consumer that folds it through \
7205         `Option::unwrap_or_else` will skip the fallback and pass the \
7206         empty edition through to the substrate's build-time edition \
7207         selector far from the source caixa.lisp; omit the slot \
7208         entirely to defer to the substrate's default edition, or \
7209         carry a canonical edition like `\"2026\"`)"
7210    )]
7211    EdicaoEmpty,
7212    #[error(
7213        ":edicao {edicao:?} is not a valid edition: {reason} (every \
7214         documented tatara-lisp edition is a 4-digit ASCII decimal \
7215         year — `\"2026\"` is the only edition currently minted; \
7216         future-introduced siblings will follow the same shape, peer \
7217         with Cargo's `[package] edition` grammar which every value \
7218         Cargo has ever accepted matches: `\"2015\"`, `\"2018\"`, \
7219         `\"2021\"`, `\"2024\"`. Without this gate the canonical \
7220         paste-from-doc footguns silently passed: a trailing space \
7221         (`\"2026 \"`) from a paste-from-doc, a CRLF (`\"2026\\n\"`) \
7222         from a paste-from-multiline-doc, a fullwidth-keyboard \
7223         look-alike (`\"2026\"`), a free-form non-year value \
7224         (`\"x\"`, `\"latest\"`, `\"nightly\"`), a leading non-digit \
7225         version-tag prefix (`\"v2026\"`, `\"e2026\"`), a \
7226         decimal-shaped pseudo-version (`\"2026.1\"`), or a \
7227         wrong-length numeric value (`\"26\"`, `\"202\"`, \
7228         `\"20260\"`) all landed as `(:edicao \"<garbage>\")` in the \
7229         rendered caixa.lisp and broke at the substrate's \
7230         build-time edition selector far from the source caixa.lisp; \
7231         omit the slot entirely to defer to the substrate's default \
7232         edition, or carry a canonical 4-digit ASCII decimal year \
7233         like `\"2026\"`)"
7234    )]
7235    EdicaoInvalid { edicao: String, reason: String },
7236}
7237
7238// Fold the five `Err(ManifestError::CodePath{Absolute,ParentEscape,
7239// NonLispExtension,NonComputeUnitYamlExtension,Duplicate} { slot,
7240// path: path.to_path_buf() })` four-line struct-variant wire-up sites at
7241// [`Caixa::validate_code_path_lists`]'s per-slot per-entry cascade onto
7242// one substrate-primitive family on the `ManifestError` envelope — the
7243// five open-coded ctor sites remaining on the `:bibliotecas` / `:exe` /
7244// `:servicos` code-path-list value-shape trajectory this envelope carries,
7245// and the family sibling of the peer [`crate::behavior::behavior_slot_path_ctors!`]
7246// (67c31ec) two-slot `{ slot: &'static str, path: PathBuf }` envelope on
7247// the [`crate::BehaviorError`] surface that keys off the exact same
7248// `(slot: &'static str, path: &Path)` argument tuple.
7249//
7250// The five wire-up sites this fold closes are the sandbox-shape
7251// absolute-path arm (`return Err(ManifestError::CodePathAbsolute { slot,
7252// path: path.to_path_buf() })` on the [`is_sandboxed_relative_path`]
7253// `PathShapeViolation::Absolute` branch), the sandbox-shape
7254// parent-escape arm (`return Err(ManifestError::CodePathParentEscape {
7255// slot, path: path.to_path_buf() })` on the sibling
7256// `PathShapeViolation::ParentEscape` branch), the LispSource
7257// terminating-extension arm (`return Err(ManifestError::CodePathNonLispExtension {
7258// slot, path: path.to_path_buf() })` on the `!is_lisp_extension(path)`
7259// branch of the `:bibliotecas` file-type gate), the ComputeUnitYaml
7260// compound-suffix arm (`return Err(ManifestError::CodePathNonComputeUnitYamlExtension
7261// { slot, path: path.to_path_buf() })` on the
7262// `!is_computeunit_yaml_extension(path)` branch of the `:servicos`
7263// file-type gate), and the cross-entry duplicate arm
7264// (`ManifestError::CodePathDuplicate { slot, path: path.to_path_buf() }`
7265// inside the closure passed to [`crate::render::insert_first_seen`]) —
7266// each opened the identical `ManifestError::CodePath* { slot,
7267// path: path.to_path_buf() }` four-line struct-literal against the same
7268// `(slot: &'static str, path: &Path)` local tuple, the exact "same
7269// block re-inlined at every consumer" shape the PRIME DIRECTIVE names
7270// as a bug. The variant discriminator is the only thing that varies
7271// between the five sites; the rest of the struct-literal is a
7272// byte-for-byte re-inline.
7273//
7274// The macro below generates one `#[must_use]` inherent constructor per
7275// variant of shape `fn <ctor>(slot: &'static str, path: &std::path::Path)
7276// -> Self`, so every wire-up site collapses onto one dispatch:
7277// `ManifestError::<ctor>(slot, path)`, byte-equal to the pre-lift
7278// struct-literal on the same `(&'static str, &Path)` fixture. The
7279// uniform two-field construction (`slot` verbatim as `&'static str`,
7280// `path.to_path_buf()`) is spelled once — inside the macro — rather
7281// than at every wire-up site. The `slot` parameter stays `&'static str`
7282// (not `&str`) so every arm continues to carry a program-lifetime
7283// `:bibliotecas` / `:exe` / `:servicos` author-key label — one of the
7284// three `&'static str` literals threaded through the outer per-slot
7285// iterator at [`Caixa::validate_code_path_lists`] — matching the
7286// enum-field type. A runtime-borrowed `&str` would silently downgrade
7287// the label lifetime and let a caller stash a non-`'static` borrow into
7288// the returned error. The `&Path` parameter accepts both
7289// `&Path` and `&PathBuf` (via Deref coercion), so every existing
7290// wire-up — each already binds `let path = Path::new(entry);` from the
7291// per-entry loop — threads through the ctor without a pre-conversion.
7292//
7293// Every future consumer that wants to construct one of these five
7294// variants outside the five in-crate wire-up sites (a deferred
7295// `feira validate --code-paths` per-caixa admission verb re-checking
7296// each declared `:bibliotecas` / `:exe` / `:servicos` entry against the
7297// same sandbox-shape + file-type + duplicate cascade, a future
7298// caixa-registry per-lacre code-path re-validator at lacre-resolve
7299// time, a per-`Caixa` overlay resolver rejecting an author-supplied
7300// code-path against a cluster-local snapshot) now reaches each variant
7301// through one call rather than re-inlining the four-line struct-literal
7302// in lockstep with the five in-crate wire-up sites.
7303macro_rules! manifest_code_path_slot_path_ctors {
7304    ($($ctor:ident => $variant:ident),* $(,)?) => {
7305        impl ManifestError {
7306            $(
7307                #[doc = concat!(
7308                    "Construct a [`ManifestError::",
7309                    stringify!($variant),
7310                    "`] naming the offending `:bibliotecas` / `:exe` / ",
7311                    "`:servicos` code-path list `slot` label and the ",
7312                    "offending entry `path`. Folds the uniform `Self::",
7313                    stringify!($variant),
7314                    " { slot, path: path.to_path_buf() }` two-field ",
7315                    "struct-literal onto one substrate primitive so ",
7316                    "every wire-up on this variant at ",
7317                    "[`Caixa::validate_code_path_lists`] reads through ",
7318                    "one dispatch rather than the pre-lift four-line ",
7319                    "open-coded block. The `slot` label threads verbatim ",
7320                    "from the outer per-slot iterator (one of the three ",
7321                    "code-path author-key `&'static str` consts) and the ",
7322                    "`path` from the per-entry inner iterator's ",
7323                    "`Path::new(entry)` binding."
7324                )]
7325                #[must_use]
7326                pub fn $ctor(slot: &'static str, path: &std::path::Path) -> Self {
7327                    Self::$variant {
7328                        slot,
7329                        path: path.to_path_buf(),
7330                    }
7331                }
7332            )*
7333        }
7334    };
7335}
7336
7337manifest_code_path_slot_path_ctors! {
7338    code_path_absolute => CodePathAbsolute,
7339    code_path_parent_escape => CodePathParentEscape,
7340    code_path_non_lisp_extension => CodePathNonLispExtension,
7341    code_path_non_computeunit_yaml_extension => CodePathNonComputeUnitYamlExtension,
7342    code_path_duplicate => CodePathDuplicate,
7343}
7344
7345// Fold the nine `ManifestError::{Nome, NomeChartNameBudgetExceeded, Versao,
7346// Etiqueta, Autor, Repositorio, Descricao, Licenca, Edicao}Invalid
7347// { <field>: <val>.to_string() | <val>.clone(), reason: <expr> }` wire-up
7348// sites at the per-axis [`Caixa::validate_*`] cascade onto one substrate-
7349// primitive family per typed variant — the direct sibling on the
7350// [`ManifestError`] envelope of the peer
7351// [`crate::aplicacao::aplicacao_field_reason_ctors!`] (981060b, 7 variants
7352// on `AplicacaoError` at `MembroCaixaInvalid` / `EntradaParaInvalid` /
7353// `EntradaHostInvalid` / `EntradaPathInvalid` / `PlacementClusterInvalid` /
7354// `PlacementAffinityInvalid` / `ShardKeyInvalid`) on the M3 mesh side, and
7355// of the peer [`crate::dep::dep_nome_axis_reason_ctors!`] (5621f8a,
7356// 3 variants on `DepError` at `VersaoInvalid` / `FonteRepoShape` /
7357// `CaracteristicaInvalid`) on the sibling `:deps` envelope's mirror-
7358// symmetric `{ nome: String, <axis>: String, reason: String }` three-slot
7359// shape (the `nome` axis added at the per-dep-owned altitude). Every one
7360// of the peer four-family `LayoutError` ctor set
7361// ([`crate::layout::layout_violation_ctors!`] 131ca0d — 16 variants on
7362// `{ caixa, issue }`, [`crate::layout::layout_slot_kind_ctors!`] 0419438
7363// — 4 variants on `{ caixa, kind, slots }`,
7364// [`crate::LayoutError::missing_entry`] 1b09f9d — 1 variant on
7365// `{ kind, path }`, [`crate::layout::layout_nome_only_ctors!`] 3fe3dd7 —
7366// 6 variants on `<Variant>(String)`) and the peer three
7367// [`crate::limits::limits_codec_value_*_ctors!`] codec families (81c856c)
7368// each carry the same discipline on their sibling envelopes.
7369//
7370// The nine variants share the identical `{ <field>: String,
7371// reason: String }` two-slot shape:
7372//   - `NomeInvalid { nome, reason }` at [`Caixa::validate_nome`]
7373//     (`|reason| ManifestError::NomeInvalid { nome: nome.to_string(),
7374//     reason }` inside [`crate::render::require_valid_dns_1123_label`]'s
7375//     `on_invalid` bracket-closure slot);
7376//   - `NomeChartNameBudgetExceeded { nome, reason }` at
7377//     [`Caixa::validate_nome_chart_name_budget`]
7378//     (`|reason| ManifestError::NomeChartNameBudgetExceeded { nome:
7379//     nome.to_string(), reason }` after
7380//     [`crate::render::is_lareira_chart_name_shape`] rejects the offending
7381//     `:nome`);
7382//   - `VersaoInvalid { versao, reason }` at [`Caixa::validate_versao`]
7383//     (`|e| ManifestError::VersaoInvalid { versao: versao.to_string(),
7384//     reason: e.to_string() }` after [`semver::Version::parse`] rejects
7385//     the offending `:versao`);
7386//   - `EtiquetaInvalid { etiqueta, reason }` at
7387//     [`Caixa::validate_etiquetas`]
7388//     (`|reason| ManifestError::EtiquetaInvalid { etiqueta:
7389//     etiqueta.clone(), reason }` after
7390//     [`crate::render::is_chart_keyword_shape`] rejects the offending
7391//     `:etiquetas` entry);
7392//   - `AutorInvalid { autor, reason }` at [`Caixa::validate_autores`]
7393//     (`|reason| ManifestError::AutorInvalid { autor: autor.clone(),
7394//     reason }` after [`crate::render::is_chart_maintainer_name_shape`]
7395//     rejects the offending `:autores` entry);
7396//   - `RepositorioInvalid { repositorio, reason }` at
7397//     [`Caixa::validate_repositorio`]
7398//     (`|reason| ManifestError::RepositorioInvalid { repositorio:
7399//     s.to_string(), reason }` after
7400//     [`crate::render::is_git_repo_url`] rejects the offending
7401//     `:repositorio`);
7402//   - `DescricaoInvalid { descricao, reason }` at
7403//     [`Caixa::validate_descricao`]
7404//     (`|reason| ManifestError::DescricaoInvalid { descricao:
7405//     s.to_string(), reason }` after
7406//     [`crate::render::is_chart_description_shape`] rejects the offending
7407//     `:descricao`);
7408//   - `LicencaInvalid { licenca, reason }` at [`Caixa::validate_licenca`]
7409//     (`|reason| ManifestError::LicencaInvalid { licenca: s.to_string(),
7410//     reason }` after [`crate::render::is_spdx_expression_shape`] rejects
7411//     the offending `:licenca`);
7412//   - `EdicaoInvalid { edicao, reason }` at [`Caixa::validate_edicao`]
7413//     (`return Err(ManifestError::EdicaoInvalid { edicao: s.to_string(),
7414//     reason: "must be a 4-digit ASCII decimal year (canonical
7415//     \"2026\")".to_string() })` on the direct year-shape arm).
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 nine
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 nine 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}
7516
7517#[cfg(test)]
7518mod tests {
7519    use super::*;
7520
7521    #[test]
7522    fn template_round_trips() {
7523        let src = Caixa::template("demo");
7524        let c = Caixa::from_lisp(&src).expect("template must parse");
7525        assert_eq!(c.nome, "demo");
7526        assert_eq!(c.versao, "0.1.0");
7527        assert_eq!(c.kind, CaixaKind::Biblioteca);
7528        assert_eq!(c.bibliotecas, vec!["lib/demo.lisp".to_string()]);
7529        assert!(c.deps.is_empty());
7530        assert!(c.deps_dev.is_empty());
7531    }
7532
7533    #[test]
7534    fn caixa_universal_axis_scalar_accessor_pair_is_const_fn() {
7535        // Fail-before-pass-after pin on [`Caixa::nome`] +
7536        // [`Caixa::versao`]'s `const`-eval-surface posture. Each
7537        // accessor projects the top-level manifest's per-`:nome` /
7538        // per-`:versao` [`String`] storage through the `pub const fn`
7539        // [`String::as_str`] (const-stable since Rust 1.87, well within
7540        // the workspace MSRV) — any future accidental downgrade to
7541        // non-`const` fails the corresponding `<name>_via_const_fn`
7542        // wrapper at caixa-core build time with E0015 (`cannot call
7543        // non-const method`), strictly stronger than a runtime
7544        // `assert!`. Sibling of the peer per-M2/M3-slot `String → &str`
7545        // scalar-accessor family pins on the sibling `const`-eval-
7546        // surface passes ([`crate::CaixaVersion::as_str`] at the
7547        // typed-newtype wrapper, [`crate::aplicacao::Membro::nome`] /
7548        // [`crate::aplicacao::Membro::versao_requirement`] at the M3
7549        // membership axis, [`crate::aplicacao::Entrada::hostname`] /
7550        // [`crate::aplicacao::Entrada::destination`] at the M3 ingress
7551        // axis, [`crate::supervisor::ChildSpec::nome`] /
7552        // [`crate::supervisor::ChildSpec::versao_requirement`] at the
7553        // M2 supervisor-tree axis,
7554        // [`crate::upgrade::UpgradeFromEntry::prior_versao`] at the M2
7555        // upgrade axis, [`crate::dep::Dep::nome`] /
7556        // [`crate::dep::Dep::versao_requirement`] at the dep-graph
7557        // axis, and the per-`:contratos`
7558        // [`crate::aplicacao::WitContract::source`] /
7559        // [`crate::aplicacao::WitContract::destination`] /
7560        // [`crate::aplicacao::WitContract::world_ref`] trio the
7561        // sibling pin at 279823b already anchors).
7562        const fn nome_via_const_fn(c: &Caixa) -> &str {
7563            c.nome()
7564        }
7565        const fn versao_via_const_fn(c: &Caixa) -> &str {
7566            c.versao()
7567        }
7568        let src = Caixa::template("demo");
7569        let c = Caixa::from_lisp(&src).expect("template must parse");
7570        assert_eq!(nome_via_const_fn(&c), c.nome());
7571        assert_eq!(versao_via_const_fn(&c), c.versao());
7572        assert_eq!(c.nome(), "demo");
7573        assert_eq!(c.versao(), "0.1.0");
7574    }
7575
7576    #[test]
7577    fn caixa_option_string_scalar_accessor_family_is_const_fn() {
7578        // Fail-before-pass-after pin on the five per-`Caixa`
7579        // `Option<String> → Option<&str>` scalar accessors
7580        // ([`Caixa::licenca`] / [`Caixa::repositorio`] /
7581        // [`Caixa::descricao`] / [`Caixa::edicao`] on the top-level
7582        // manifest's optional universal-axis surface, plus
7583        // [`Caixa::restart_window`] on the M2 supervisor-tree
7584        // per-`SupervisorSpec` peer raw-window-string projection axis).
7585        // Each accessor destructures the typed slot's `Option<String>`
7586        // storage through the `match &self.<field> { Some(s) =>
7587        // Some(s.as_str()), None => None }` shape — routing through
7588        // [`String::as_str`] (const-stable since Rust 1.87, well within
7589        // the workspace MSRV) rather than the non-const
7590        // [`Option::as_deref`] the pre-lift bodies carried — and any
7591        // future accidental downgrade to non-`const` fails the
7592        // corresponding `<name>_via_const_fn` wrapper at caixa-core
7593        // build time with E0015 (`cannot call non-const method`),
7594        // strictly stronger than a runtime `assert!` and strictly
7595        // stronger than a module-scope `const _: () = assert!(…)` pin
7596        // (which cannot be formed on a `&Caixa` fixture because the
7597        // type's `String` / `Option<String>` carriers rule out
7598        // `const`-context value construction; the `const fn` wrapper
7599        // is the load-bearing shape that side-steps the destructor-in-
7600        // const restriction on the value axis while still pinning the
7601        // `const`-fn posture on the callee — mirror of the sibling
7602        // [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7603        // pin's discipline verbatim on the peer non-`Option`
7604        // `String → &str` axis at the same struct).
7605        //
7606        // Peer of the sibling per-M2/M3-slot `Option<String> →
7607        // Option<&str>` accessor family pin
7608        // [`m3_option_string_scalar_accessor_family_is_const_fn`] on
7609        // the M3 mesh-slot atom axes ([`WitContract::endpoint`] /
7610        // [`WitContract::subject`] / [`WitContract::slot`] on the
7611        // per-`:contratos` payload-carrier trio,
7612        // [`Placement::shard_key`] / [`Placement::affinity`] on the
7613        // per-`:placement` optional-scalar pair).
7614        const fn licenca_via_const_fn(c: &Caixa) -> Option<&str> {
7615            c.licenca()
7616        }
7617        const fn repositorio_via_const_fn(c: &Caixa) -> Option<&str> {
7618            c.repositorio()
7619        }
7620        const fn descricao_via_const_fn(c: &Caixa) -> Option<&str> {
7621            c.descricao()
7622        }
7623        const fn edicao_via_const_fn(c: &Caixa) -> Option<&str> {
7624            c.edicao()
7625        }
7626        const fn restart_window_via_const_fn(c: &Caixa) -> Option<&str> {
7627            c.restart_window()
7628        }
7629        // Sweep both the `Some`-carrying arm (author-declared slot,
7630        // the byte-string projection payload) and the `None`-carrying
7631        // arm (author-omitted slot, the default-path projection) on
7632        // every accessor so the `const fn` wrapper family pins each
7633        // axis's canonical two-arm partition through the same const
7634        // dispatch as the runtime path.
7635        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7636        c1.licenca = Some("MIT".to_string());
7637        c1.repositorio = Some("https://github.com/pleme-io/demo".to_string());
7638        c1.descricao = Some("demo caixa".to_string());
7639        c1.edicao = Some("2024".to_string());
7640        c1.restart_window = Some("60s".to_string());
7641        assert_eq!(licenca_via_const_fn(&c1), c1.licenca());
7642        assert_eq!(repositorio_via_const_fn(&c1), c1.repositorio());
7643        assert_eq!(descricao_via_const_fn(&c1), c1.descricao());
7644        assert_eq!(edicao_via_const_fn(&c1), c1.edicao());
7645        assert_eq!(restart_window_via_const_fn(&c1), c1.restart_window());
7646        assert_eq!(c1.licenca(), Some("MIT"));
7647        assert_eq!(c1.repositorio(), Some("https://github.com/pleme-io/demo"));
7648        assert_eq!(c1.descricao(), Some("demo caixa"));
7649        assert_eq!(c1.edicao(), Some("2024"));
7650        assert_eq!(c1.restart_window(), Some("60s"));
7651        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7652        c2.licenca = None;
7653        c2.repositorio = None;
7654        c2.descricao = None;
7655        c2.edicao = None;
7656        c2.restart_window = None;
7657        assert_eq!(licenca_via_const_fn(&c2), None);
7658        assert_eq!(repositorio_via_const_fn(&c2), None);
7659        assert_eq!(descricao_via_const_fn(&c2), None);
7660        assert_eq!(edicao_via_const_fn(&c2), None);
7661        assert_eq!(restart_window_via_const_fn(&c2), None);
7662    }
7663
7664    #[test]
7665    fn caixa_outer_copy_return_accessor_pair_is_const_fn() {
7666        // Fail-before-pass-after pin on the two outer-[`Caixa`]
7667        // `Copy`-return accessors — [`Caixa::kind`] on the required
7668        // [`CaixaKind`] enum-discriminant axis and [`Caixa::estrategia`]
7669        // on the M2 supervisor-tree flat-spread `Option<RestartStrategy>`
7670        // axis. Both accessors project a `Copy`-carrier field
7671        // (`CaixaKind: Copy` at caixa-core/src/kind.rs:17,
7672        // `RestartStrategy: Copy` at caixa-core/src/supervisor.rs:33 →
7673        // `Option<RestartStrategy>: Copy`) by value through a bare
7674        // `self.<field>` field-access — no dispatch, no destructor, no
7675        // heap. Any future accidental downgrade to non-`const` fails
7676        // the corresponding `<name>_via_const_fn` wrapper at caixa-core
7677        // build time with E0015 (`cannot call non-const method`),
7678        // strictly stronger than a runtime `assert!` and strictly
7679        // stronger than a module-scope `const _: () = assert!(…)` pin
7680        // (which cannot be formed on a `&Caixa` fixture because the
7681        // type's `String` / `Vec` / `Option<Composite>` carriers rule
7682        // out `const`-context value construction; the `const fn`
7683        // wrapper is the load-bearing shape that side-steps the
7684        // destructor-in-const restriction on the value axis while still
7685        // pinning the `const`-fn posture on the callee — mirror of the
7686        // sibling [`caixa_universal_axis_scalar_accessor_pair_is_const_fn`]
7687        // + [`caixa_option_string_scalar_accessor_family_is_const_fn`]
7688        // pins' discipline verbatim on the peer outer-`Caixa`
7689        // `String → &str` + `Option<String> → Option<&str>` axes at the
7690        // same struct).
7691        //
7692        // Peer of the sibling per-M2/M3-slot `Copy`-return accessor pin
7693        // family on the inner-altitude nested-spec typed-slot
7694        // discriminator axes: [`crate::supervisor::SupervisorSpec::estrategia`]
7695        // + [`crate::supervisor::ChildSpec::restart`] on the M2
7696        // supervisor-tree axis (pinned at 152c868), and
7697        // [`crate::aplicacao::Placement::estrategia`] +
7698        // [`crate::aplicacao::Entrada::port`] on the M3 mesh-slot axis
7699        // (pinned at bafa004) — the outer-`Caixa` altitude is the last
7700        // unlifted altitude for the `Copy`-return-accessor family.
7701        const fn kind_via_const_fn(c: &Caixa) -> CaixaKind {
7702            c.kind()
7703        }
7704        const fn estrategia_via_const_fn(c: &Caixa) -> Option<crate::supervisor::RestartStrategy> {
7705            c.estrategia()
7706        }
7707        // Sweep every arm of both discriminant partitions the accessors
7708        // fan on — every [`CaixaKind`] variant the six-arm required
7709        // discriminant carries (Biblioteca / Binario / Servico /
7710        // Supervisor / Aplicacao / Acao) and both arms of the
7711        // [`Option<RestartStrategy>`] flat-spread supervisor-tree slot
7712        // (`Some(<strategy>)` on an author-declared supervisor and
7713        // `None` on the author-omitted default arm every non-Supervisor
7714        // caixa carries by `#[serde(default)]`) — so the `const fn`
7715        // wrapper family pins the closed-set partition through the
7716        // same const dispatch as the runtime path.
7717        let mut c1 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7718        c1.kind = CaixaKind::Servico;
7719        c1.estrategia = Some(crate::supervisor::RestartStrategy::OneForAll);
7720        assert_eq!(kind_via_const_fn(&c1), c1.kind());
7721        assert_eq!(estrategia_via_const_fn(&c1), c1.estrategia());
7722        assert_eq!(c1.kind(), CaixaKind::Servico);
7723        assert_eq!(
7724            c1.estrategia(),
7725            Some(crate::supervisor::RestartStrategy::OneForAll)
7726        );
7727        let mut c2 = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7728        c2.kind = CaixaKind::Aplicacao;
7729        c2.estrategia = None;
7730        assert_eq!(kind_via_const_fn(&c2), CaixaKind::Aplicacao);
7731        assert_eq!(estrategia_via_const_fn(&c2), None);
7732        // Anchor the remaining discriminant arms so any future
7733        // reordering of [`CaixaKind`]'s six-variant enum surfaces
7734        // through the wrapper dispatch, not just through the direct
7735        // method call.
7736        for kind in [
7737            CaixaKind::Biblioteca,
7738            CaixaKind::Binario,
7739            CaixaKind::Servico,
7740            CaixaKind::Supervisor,
7741            CaixaKind::Aplicacao,
7742            CaixaKind::Acao,
7743        ] {
7744            let mut c = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7745            c.kind = kind;
7746            assert_eq!(kind_via_const_fn(&c), kind);
7747        }
7748    }
7749
7750    #[test]
7751    fn caixa_outer_string_slice_return_accessor_family_is_const_fn() {
7752        // Fail-before-pass-after pin on the five outer-[`Caixa`]
7753        // `Vec<String> → &[String]` slice-return accessors on the
7754        // universal-axis surface — [`Caixa::autores`] / [`Caixa::etiquetas`]
7755        // / [`Caixa::bibliotecas`] / [`Caixa::exe`] / [`Caixa::servicos`].
7756        // Each body is a bare `self.<field>.as_slice()` dispatch through
7757        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7758        // the workspace MSRV). Any future accidental downgrade to
7759        // non-`const` fails the corresponding `<name>_via_const_fn`
7760        // wrapper at caixa-core build time with E0015 (`cannot call
7761        // non-const method`) — mirror of the sibling
7762        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] pin's
7763        // discipline on the peer outer-`Caixa` `Copy`-return accessor
7764        // axis, and peer of the sibling composite-carrier slice-return
7765        // pin below on the peer outer-`Caixa` composite-slice axis.
7766        const fn autores_via_const_fn(c: &Caixa) -> &[String] {
7767            c.autores()
7768        }
7769        const fn etiquetas_via_const_fn(c: &Caixa) -> &[String] {
7770            c.etiquetas()
7771        }
7772        const fn bibliotecas_via_const_fn(c: &Caixa) -> &[String] {
7773            c.bibliotecas()
7774        }
7775        const fn exe_via_const_fn(c: &Caixa) -> &[String] {
7776            c.exe()
7777        }
7778        const fn servicos_via_const_fn(c: &Caixa) -> &[String] {
7779            c.servicos()
7780        }
7781        // Sweep the empty arm (`autores` / `etiquetas` / `exe` /
7782        // `servicos` — the template's `Vec::new()` default) and the
7783        // populated arm (mutated below) on every accessor so the
7784        // `const fn` wrapper family pins each axis's two-arm partition
7785        // through the same const dispatch as the runtime path.
7786        // [`Caixa::template`] seeds `lib/demo.lisp` into `:bibliotecas`,
7787        // so that arm's "empty" fixture is the populated arm the
7788        // mutation sweep covers.
7789        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7790        assert!(autores_via_const_fn(&c_empty).is_empty());
7791        assert!(etiquetas_via_const_fn(&c_empty).is_empty());
7792        assert!(exe_via_const_fn(&c_empty).is_empty());
7793        assert!(servicos_via_const_fn(&c_empty).is_empty());
7794        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7795        c_full.autores = vec!["ada".to_string(), "erlang".to_string()];
7796        c_full.etiquetas = vec!["compounding".to_string()];
7797        c_full.bibliotecas = vec!["lib/one.lisp".to_string(), "lib/two.lisp".to_string()];
7798        c_full.exe = vec!["exe/cli.lisp".to_string()];
7799        c_full.servicos = vec!["servicos/one.computeunit.yaml".to_string()];
7800        assert_eq!(autores_via_const_fn(&c_full), c_full.autores());
7801        assert_eq!(autores_via_const_fn(&c_full), &["ada", "erlang"]);
7802        assert_eq!(etiquetas_via_const_fn(&c_full), c_full.etiquetas());
7803        assert_eq!(etiquetas_via_const_fn(&c_full), &["compounding"]);
7804        assert_eq!(bibliotecas_via_const_fn(&c_full), c_full.bibliotecas());
7805        assert_eq!(
7806            bibliotecas_via_const_fn(&c_full),
7807            &["lib/one.lisp", "lib/two.lisp"]
7808        );
7809        assert_eq!(exe_via_const_fn(&c_full), c_full.exe());
7810        assert_eq!(exe_via_const_fn(&c_full), &["exe/cli.lisp"]);
7811        assert_eq!(servicos_via_const_fn(&c_full), c_full.servicos());
7812        assert_eq!(
7813            servicos_via_const_fn(&c_full),
7814            &["servicos/one.computeunit.yaml"]
7815        );
7816    }
7817
7818    #[test]
7819    fn caixa_outer_composite_slice_return_accessor_family_is_const_fn() {
7820        // Fail-before-pass-after pin on the six outer-[`Caixa`] composite-
7821        // carrier `Vec<T> → &[T]` slice-return accessors — [`Caixa::deps`]
7822        // / [`Caixa::deps_dev`] on the dep-graph axis,
7823        // [`Caixa::upgrade_from`] on the M2 appup axis, [`Caixa::children`]
7824        // on the M2 supervisor-tree axis, and [`Caixa::membros`] /
7825        // [`Caixa::contratos`] on the M3 mesh-slot axis. Each body is a
7826        // bare `self.<field>.as_slice()` dispatch through
7827        // [`Vec::as_slice`] (const-stable since Rust 1.87, well within
7828        // the workspace MSRV) — peer of the sibling `String`-payload
7829        // slice-return pin above on the peer outer-`Caixa` universal-
7830        // axis surface, and peer of the sibling inner-composite-
7831        // altitude reference-return pin family
7832        // [`crate::aplicacao::tests::m3_aplicacao_spec_reference_return_accessor_family_is_const_fn`]
7833        // + [`crate::supervisor::tests::supervisor_children_slice_return_accessor_is_const_fn`]
7834        // + [`crate::upgrade::tests::upgrade_from_entry_instructions_slice_return_accessor_is_const_fn`]
7835        // (all pinned at 0b23e0f).
7836        const fn deps_via_const_fn(c: &Caixa) -> &[Dep] {
7837            c.deps()
7838        }
7839        const fn deps_dev_via_const_fn(c: &Caixa) -> &[Dep] {
7840            c.deps_dev()
7841        }
7842        const fn upgrade_from_via_const_fn(c: &Caixa) -> &[UpgradeFromEntry] {
7843            c.upgrade_from()
7844        }
7845        const fn children_via_const_fn(c: &Caixa) -> &[crate::supervisor::ChildSpec] {
7846            c.children()
7847        }
7848        const fn membros_via_const_fn(c: &Caixa) -> &[crate::aplicacao::Membro] {
7849            c.membros()
7850        }
7851        const fn contratos_via_const_fn(c: &Caixa) -> &[crate::aplicacao::WitContract] {
7852            c.contratos()
7853        }
7854        // Empty-arm sweep on all six composite-carrier axes — every
7855        // `Caixa::template` starts with `Vec::new()` on each.
7856        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7857        assert!(deps_via_const_fn(&c_empty).is_empty());
7858        assert!(deps_dev_via_const_fn(&c_empty).is_empty());
7859        assert!(upgrade_from_via_const_fn(&c_empty).is_empty());
7860        assert!(children_via_const_fn(&c_empty).is_empty());
7861        assert!(membros_via_const_fn(&c_empty).is_empty());
7862        assert!(contratos_via_const_fn(&c_empty).is_empty());
7863        // Populate `:membros` / `:contratos` directly via struct literals
7864        // — the parser-side validation path fans on `:kind`-gated cross-
7865        // slot invariants irrelevant to the accessor dispatch under test.
7866        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7867        c_full.membros = vec![
7868            crate::aplicacao::Membro {
7869                caixa: "demo-a".to_string(),
7870                versao: "^0.1.0".to_string(),
7871            },
7872            crate::aplicacao::Membro {
7873                caixa: "demo-b".to_string(),
7874                versao: "^0.2.0".to_string(),
7875            },
7876        ];
7877        c_full.contratos = vec![crate::aplicacao::WitContract {
7878            de: "demo-a".to_string(),
7879            para: "demo-b".to_string(),
7880            wit: "wasi:http/proxy".to_string(),
7881            endpoint: Some("/edge".to_string()),
7882            subject: None,
7883            slot: None,
7884        }];
7885        assert_eq!(membros_via_const_fn(&c_full), c_full.membros());
7886        assert_eq!(contratos_via_const_fn(&c_full), c_full.contratos());
7887        assert_eq!(membros_via_const_fn(&c_full).len(), 2);
7888        assert_eq!(contratos_via_const_fn(&c_full).len(), 1);
7889        // Alias-borrow check on the four remaining composite-carrier
7890        // slice-return arms — the wrapper's return borrow must alias the
7891        // caller's borrow so any future accessor re-routing that skips
7892        // the storage field surfaces through the assertion.
7893        assert!(std::ptr::eq(deps_via_const_fn(&c_full), c_full.deps()));
7894        assert!(std::ptr::eq(
7895            deps_dev_via_const_fn(&c_full),
7896            c_full.deps_dev()
7897        ));
7898        assert!(std::ptr::eq(
7899            upgrade_from_via_const_fn(&c_full),
7900            c_full.upgrade_from()
7901        ));
7902        assert!(std::ptr::eq(
7903            children_via_const_fn(&c_full),
7904            c_full.children()
7905        ));
7906    }
7907
7908    #[test]
7909    fn caixa_outer_option_composite_reference_return_accessor_family_is_const_fn() {
7910        // Fail-before-pass-after pin on the six outer-[`Caixa`]
7911        // `Option<Composite> → Option<&Composite>` reference-return
7912        // accessors — [`Caixa::limits`] / [`Caixa::behavior`] on the M2
7913        // Servico-runtime typed-slot axis, [`Caixa::politicas`] /
7914        // [`Caixa::placement`] / [`Caixa::entrada`] on the M3 mesh-slot
7915        // axis, and [`Caixa::ci`] on the Acao-kind typed-CI-run axis.
7916        // Each body is a bare `self.<field>.as_ref()` dispatch through
7917        // [`Option::as_ref`] (const-stable since Rust 1.83, well within
7918        // the workspace MSRV of 1.89). Any future accidental downgrade
7919        // to non-`const` fails the corresponding `<name>_via_const_fn`
7920        // wrapper at caixa-core build time with E0015 (`cannot call
7921        // non-const method`), strictly stronger than a runtime `assert!`
7922        // and strictly stronger than a module-scope `const _: () =
7923        // assert!(…)` pin (which cannot be formed on a `&Caixa` fixture
7924        // because the type's `String` / `Vec` / `Option<Composite>`
7925        // carriers rule out `const`-context value construction; the
7926        // `const fn` wrapper is the load-bearing shape that side-steps
7927        // the destructor-in-const restriction on the value axis while
7928        // still pinning the `const`-fn posture on the callee — mirror
7929        // of the sibling
7930        // [`caixa_outer_copy_return_accessor_pair_is_const_fn`] +
7931        // [`caixa_outer_string_slice_return_accessor_family_is_const_fn`] +
7932        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
7933        // pins' discipline verbatim on the peer outer-`Caixa` axes at
7934        // the same struct).
7935        //
7936        // Closes the outer-`Caixa` `Option<&Composite>` composite-
7937        // reference-return sub-family — the last unlifted altitude on
7938        // the outer-`Caixa` accessor-family const-eval surface after
7939        // the sibling `Copy`-return / universal-axis-`&str` /
7940        // `Option<&str>` / `&[String]` / composite-`&[T]` pins already
7941        // closed the sibling arms at 866d1d5 / 29c5d7e / 0650f64 /
7942        // 231a968 (the last of these pins the `Vec<T> → &[T]`
7943        // composite-slice arm the six accessors here close as their
7944        // `Option<Composite> → Option<&Composite>` peer). Peer of the
7945        // sibling inner-altitude nested-spec composite-reference-return
7946        // pin family — [`crate::AplicacaoSpec::politicas`] /
7947        // [`crate::AplicacaoSpec::placement`] /
7948        // [`crate::AplicacaoSpec::entrada`] on the inner
7949        // [`crate::AplicacaoSpec`] altitude (already `pub const fn`
7950        // per 0b23e0f), and the outer-`Caixa` altitude here now carries
7951        // the same shape so both altitudes of the reference-return
7952        // discipline (per-`Caixa` outer-slot presence + per-
7953        // `AplicacaoSpec` inner-slot presence) route through one typed
7954        // const dispatch on the substrate primitive.
7955        const fn limits_via_const_fn(c: &Caixa) -> Option<&LimitsSpec> {
7956            c.limits()
7957        }
7958        const fn behavior_via_const_fn(c: &Caixa) -> Option<&crate::BehaviorSpec> {
7959            c.behavior()
7960        }
7961        const fn politicas_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::MeshPolicy> {
7962            c.politicas()
7963        }
7964        const fn placement_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Placement> {
7965            c.placement()
7966        }
7967        const fn entrada_via_const_fn(c: &Caixa) -> Option<&crate::aplicacao::Entrada> {
7968            c.entrada()
7969        }
7970        const fn ci_via_const_fn(c: &Caixa) -> Option<&canteiro_types::CiRun> {
7971            c.ci()
7972        }
7973        // Both-arm sweep on every accessor: the `None` author-omitted
7974        // arm (template default — no M2/M3/CI slot declared) and the
7975        // `Some(<composite>)` authored arm (mutated below via struct-
7976        // literal seeds, side-stepping the parser-side `:kind`-gated
7977        // cross-slot invariants irrelevant to the accessor dispatch
7978        // under test). Both arms route through the `const fn` wrapper
7979        // family so the two-arm `Option` partition is pinned through
7980        // the same const dispatch as the runtime path.
7981        let c_empty = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7982        assert!(limits_via_const_fn(&c_empty).is_none());
7983        assert!(behavior_via_const_fn(&c_empty).is_none());
7984        assert!(politicas_via_const_fn(&c_empty).is_none());
7985        assert!(placement_via_const_fn(&c_empty).is_none());
7986        assert!(entrada_via_const_fn(&c_empty).is_none());
7987        assert!(ci_via_const_fn(&c_empty).is_none());
7988        let mut c_full = Caixa::from_lisp(&Caixa::template("demo")).expect("template must parse");
7989        c_full.limits = Some(LimitsSpec::default());
7990        c_full.behavior = Some(crate::BehaviorSpec::default());
7991        c_full.politicas = Some(crate::aplicacao::MeshPolicy::default());
7992        c_full.placement = Some(crate::aplicacao::Placement::default());
7993        c_full.entrada = Some(crate::aplicacao::Entrada {
7994            host: "demo.quero.cloud".to_string(),
7995            para: "demo".to_string(),
7996            paths: Vec::new(),
7997            port: crate::aplicacao::DEFAULT_SERVICO_PORT,
7998        });
7999        c_full.ci = Some(canteiro_types::CiRun {
8000            workspace: "pleme-io".into(),
8001            repo: "caixa".into(),
8002            nodes: vec![],
8003        });
8004        assert!(limits_via_const_fn(&c_full).is_some());
8005        assert!(behavior_via_const_fn(&c_full).is_some());
8006        assert!(politicas_via_const_fn(&c_full).is_some());
8007        assert!(placement_via_const_fn(&c_full).is_some());
8008        assert!(entrada_via_const_fn(&c_full).is_some());
8009        assert!(ci_via_const_fn(&c_full).is_some());
8010        // Alias-borrow check on every arm: the wrapper's inner-`Option`
8011        // reference must alias the caller's borrow so any future accessor
8012        // re-routing that skips the storage field surfaces through the
8013        // assertion.
8014        assert!(std::ptr::eq(
8015            limits_via_const_fn(&c_full).unwrap(),
8016            c_full.limits().unwrap()
8017        ));
8018        assert!(std::ptr::eq(
8019            behavior_via_const_fn(&c_full).unwrap(),
8020            c_full.behavior().unwrap()
8021        ));
8022        assert!(std::ptr::eq(
8023            politicas_via_const_fn(&c_full).unwrap(),
8024            c_full.politicas().unwrap()
8025        ));
8026        assert!(std::ptr::eq(
8027            placement_via_const_fn(&c_full).unwrap(),
8028            c_full.placement().unwrap()
8029        ));
8030        assert!(std::ptr::eq(
8031            entrada_via_const_fn(&c_full).unwrap(),
8032            c_full.entrada().unwrap()
8033        ));
8034        assert!(std::ptr::eq(
8035            ci_via_const_fn(&c_full).unwrap(),
8036            c_full.ci().unwrap()
8037        ));
8038    }
8039
8040    #[test]
8041    fn register_populates_registry() {
8042        Caixa::register().expect("first register call in this test process must succeed");
8043        let kws = tatara_lisp::domain::registered_keywords();
8044        assert!(kws.contains(&"defcaixa"));
8045    }
8046
8047    #[test]
8048    fn to_lisp_round_trips() {
8049        let src = Caixa::template("demo");
8050        let c1 = Caixa::from_lisp(&src).unwrap();
8051        let emitted = c1.to_lisp();
8052        let c2 = Caixa::from_lisp(&emitted).expect("emitted lisp parses back");
8053        assert_eq!(c1, c2);
8054    }
8055
8056    // ── DialetoEstrangeiro carries a single typed axis ────────────────────
8057    //
8058    // The compounding pin: the variant stores only the typed
8059    // [`crate::dialeto::CaixaDialeto`], and every user-facing byte-string
8060    // (canonical keyword, description, consumer) routes through the enum's
8061    // own accessors at Display time. Prior to that closure the variant
8062    // carried each accessor's return value as a stored `&'static str`
8063    // snapshot alongside `dialeto`; a caller could construct the variant
8064    // with a snapshot that drifted from what `dialeto`'s accessors would
8065    // return, and every downstream user-facing projection would silently
8066    // disagree with the classification. Storing only the axis makes the
8067    // drift structurally impossible.
8068
8069    #[test]
8070    fn dialeto_estrangeiro_variant_carries_only_the_typed_dialeto_axis() {
8071        // Single-field construction is the whole compounding shape — a
8072        // future re-introduction of a snapshot field (a `palavra_canonica:
8073        // &'static str`, a stored `descricao:`, a stored `consumidor:`)
8074        // would re-open the drift surface and this construction would fail
8075        // to compile with "missing field" until every snapshot was seeded
8076        // at the call site again. The compile-time guarantee is the
8077        // invariant; the assertion below only witnesses that the
8078        // construction is well-formed after the closure.
8079        let err = LeituraError::DialetoEstrangeiro {
8080            dialeto: crate::dialeto::CaixaDialeto::Molde,
8081        };
8082        assert!(matches!(
8083            err,
8084            LeituraError::DialetoEstrangeiro {
8085                dialeto: crate::dialeto::CaixaDialeto::Molde,
8086            }
8087        ));
8088    }
8089
8090    #[test]
8091    fn dialeto_estrangeiro_display_routes_through_typed_dialeto_accessors() {
8092        // For every foreign-dialect classification the variant surfaces —
8093        // [`crate::dialeto::CaixaDialeto::Molde`] and
8094        // [`crate::dialeto::CaixaDialeto::MoldePosicional`], the two
8095        // variants [`Caixa::from_lisp`] raises this error for — the
8096        // rendered [`std::fmt::Display`] byte-string must interpolate each
8097        // typed accessor's return verbatim. A future re-introduction of a
8098        // stored `&'static str` snapshot alongside `dialeto` that Display
8099        // read instead of the accessor would fail this pin as soon as the
8100        // two disagreed; a future accessor rebrand (a per-dialect
8101        // consumer rename, a canonical-keyword shift once the substrate
8102        // migration named in [`crate::dialeto`] completes) reaches every
8103        // consumer through one typed dispatch and this pin verifies the
8104        // display path is one of them.
8105        for d in [
8106            crate::dialeto::CaixaDialeto::Molde,
8107            crate::dialeto::CaixaDialeto::MoldePosicional,
8108        ] {
8109            let rendered = LeituraError::DialetoEstrangeiro { dialeto: d }.to_string();
8110            assert!(
8111                rendered.contains(d.palavra_canonica()),
8112                "Display must interpolate `dialeto.palavra_canonica()` \
8113                 verbatim — a stored snapshot would silently drift from \
8114                 the typed accessor. dialect: {d}, rendered: {rendered:?}"
8115            );
8116            assert!(
8117                rendered.contains(d.descricao()),
8118                "Display must interpolate `dialeto.descricao()` verbatim. \
8119                 dialect: {d}, rendered: {rendered:?}"
8120            );
8121            assert!(
8122                rendered.contains(d.consumidor()),
8123                "Display must interpolate `dialeto.consumidor()` verbatim. \
8124                 dialect: {d}, rendered: {rendered:?}"
8125            );
8126        }
8127    }
8128
8129    #[test]
8130    fn from_lisp_rejects_molde_dialect_via_typed_variant() {
8131        // The end-to-end pin the compounding closure defends: a
8132        // Molde-dialect source lands as [`LeituraError::DialetoEstrangeiro`]
8133        // carrying [`crate::dialeto::CaixaDialeto::Molde`], and the
8134        // rendered Display byte-string names the Molde accessors'
8135        // returns verbatim. Any future path that constructed the variant
8136        // with a mismatched snapshot (a stored `palavra_canonica:
8137        // "defcaixa"` on a `Molde` classification) would land Display
8138        // pointing at `defcaixa` while the typed axis said `Molde` — the
8139        // exact drift the closure removes.
8140        let src = r#"
8141          (defcaixa
8142            :name "x"
8143            :kind :Biblioteca
8144            :ecosystem :rust-single-crate
8145            :package {:name "x" :version "0.1.0"})
8146        "#;
8147        let err = Caixa::from_lisp(src).expect_err("Molde dialect must not parse as Pacote");
8148        match err {
8149            LeituraError::DialetoEstrangeiro { dialeto } => {
8150                assert_eq!(dialeto, crate::dialeto::CaixaDialeto::Molde);
8151                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
8152                assert!(rendered.contains(dialeto.palavra_canonica()));
8153                assert!(rendered.contains(dialeto.consumidor()));
8154                assert!(rendered.contains(dialeto.descricao()));
8155            }
8156            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
8157        }
8158    }
8159
8160    #[test]
8161    fn from_lisp_rejects_molde_posicional_dialect_via_typed_variant() {
8162        // Coverage pin for the [`crate::dialeto::CaixaDialeto::MoldePosicional`]
8163        // arm of the [`Caixa::from_lisp`] foreign-dialect gate — the
8164        // positional-arity `defmolde` form written under a `(defcaixa …)`
8165        // head (`(defcaixa todoku-go :kind :Biblioteca :ecosystem :go
8166        // …)`). Pre-lift this arm rode the same `foreign =>` wildcard
8167        // the [`crate::dialeto::CaixaDialeto::Molde`] sibling arm rode,
8168        // so no test exercised the positional-arity path through
8169        // `Caixa::from_lisp` specifically; the sibling
8170        // [`from_lisp_rejects_molde_dialect_via_typed_variant`] only
8171        // covered [`crate::dialeto::CaixaDialeto::Molde`]. Post-lift the
8172        // two arms route through the lifted
8173        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
8174        // typed predicate — the same predicate the pre-lift `foreign =>`
8175        // wildcard resolved to today — and this pin makes the
8176        // positional-arity arm's byte-shape at the gate explicit rather
8177        // than implied by wildcard-absorption. A future regression that
8178        // silently reordered [`crate::dialeto::CaixaDialeto::is_molde_family`]'s
8179        // arm-set (dropped [`crate::dialeto::CaixaDialeto::MoldePosicional`]
8180        // from the two-arity closure) would fail this pin at caixa-core
8181        // test time rather than surfacing far from the change as a
8182        // `caixa.lisp` carrying a `(defcaixa todoku-go :ecosystem :go
8183        // …)` silently parsing past the derive.
8184        let src = r#"
8185          (defcaixa todoku-go
8186            :kind :Biblioteca
8187            :ecosystem :go
8188            :package {:name "todoku-go" :version "0.3.0"})
8189        "#;
8190        let err =
8191            Caixa::from_lisp(src).expect_err("MoldePosicional dialect must not parse as Pacote");
8192        match err {
8193            LeituraError::DialetoEstrangeiro { dialeto } => {
8194                assert_eq!(
8195                    dialeto,
8196                    crate::dialeto::CaixaDialeto::MoldePosicional,
8197                    "DialetoEstrangeiro must carry the MoldePosicional \
8198                     variant verbatim — the positional-arity `defmolde` \
8199                     form under a `(defcaixa …)` head is the \
8200                     `MoldePosicional` arm's canonical byte-shape"
8201                );
8202                let rendered = LeituraError::DialetoEstrangeiro { dialeto }.to_string();
8203                assert!(
8204                    rendered.contains(dialeto.palavra_canonica()),
8205                    "Display must interpolate `dialeto.palavra_canonica()` \
8206                     verbatim on the MoldePosicional arm; rendered: \
8207                     {rendered:?}"
8208                );
8209                assert!(
8210                    rendered.contains(dialeto.consumidor()),
8211                    "Display must interpolate `dialeto.consumidor()` \
8212                     verbatim on the MoldePosicional arm; rendered: \
8213                     {rendered:?}"
8214                );
8215                assert!(
8216                    rendered.contains(dialeto.descricao()),
8217                    "Display must interpolate `dialeto.descricao()` \
8218                     verbatim on the MoldePosicional arm; rendered: \
8219                     {rendered:?}"
8220                );
8221            }
8222            other => panic!("expected DialetoEstrangeiro, got {other:?}"),
8223        }
8224    }
8225
8226    #[test]
8227    fn from_lisp_dialect_gate_dispatches_through_caixa_dialeto_is_molde_family_predicate() {
8228        // Load-bearing byte-parity pin: for every arm in
8229        // [`crate::dialeto::CaixaDialeto::ALL`], the
8230        // [`Caixa::from_lisp`] foreign-dialect gate's DialetoEstrangeiro
8231        // partition must agree with the lifted
8232        // [`crate::dialeto::CaixaDialeto::is_molde_family`] (e9d2315)
8233        // typed predicate — i.e. from_lisp raises
8234        // [`LeituraError::DialetoEstrangeiro`] carrying `d` iff
8235        // `d.is_molde_family()` returns `true`, and does NOT raise
8236        // [`LeituraError::DialetoEstrangeiro`] on any arm where the
8237        // predicate returns `false` (the arm's source falls through to
8238        // the derive — parses cleanly on
8239        // [`crate::dialeto::CaixaDialeto::Pacote`], surfaces a
8240        // [`LeituraError::Leitura`] on
8241        // [`crate::dialeto::CaixaDialeto::Desconhecido`]).
8242        //
8243        // Pre-lift the gate hand-rolled a three-arm match
8244        // (`Pacote => {}`, `Desconhecido => {}`, `foreign => Err(…)`)
8245        // whose `foreign =>` wildcard expressed no compile-time link
8246        // back to the substrate primitive's arm-family; a future fifth
8247        // dialect the [`crate::dialeto`] module doc's "third dialect"
8248        // hazard actualises would fall silently onto the wildcard
8249        // regardless of whether it belonged to the `defmolde` family or
8250        // to a distinct `defcaixa`-family. Post-lift the partition
8251        // resolves through
8252        // [`crate::dialeto::CaixaDialeto::is_molde_family`]'s single
8253        // typed dispatch, and this pin refuses any future regression
8254        // that silently split the from_lisp partition from the typed
8255        // predicate — the two paths now migrate as one on any future
8256        // arm addition.
8257        //
8258        // Sibling in shape to the peer
8259        // [`crate::dialeto::tests::caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection`]
8260        // (e9d2315) that pins the same byte-parity between
8261        // [`crate::dialeto::CaixaDialeto::is_molde_family`] and the
8262        // sibling [`crate::dialeto::CaixaDialeto::palavra_canonica`]
8263        // `== "defmolde"` classifier — extends the discipline from the
8264        // two paths within the [`crate::dialeto`] primitive onto the
8265        // third external consumer of the `defmolde`-family partition
8266        // (the [`Caixa::from_lisp`] gate that raises
8267        // [`LeituraError::DialetoEstrangeiro`]).
8268        let fixtures: &[(crate::dialeto::CaixaDialeto, &str)] = &[
8269            (
8270                crate::dialeto::CaixaDialeto::Pacote,
8271                r#"
8272                  (defcaixa
8273                    :nome   "checkout"
8274                    :versao "0.1.0"
8275                    :kind   Biblioteca
8276                    :edicao "2026"
8277                    :descricao "canonical Pacote source"
8278                    :autores ()
8279                    :etiquetas ()
8280                    :deps ()
8281                    :deps-dev ()
8282                    :bibliotecas ("lib/checkout.lisp"))
8283                "#,
8284            ),
8285            (
8286                crate::dialeto::CaixaDialeto::Molde,
8287                r#"
8288                  (defcaixa
8289                    :name "base64"
8290                    :kind :Biblioteca
8291                    :ecosystem :rust-single-crate
8292                    :package {:name "base64" :version "0.22.1"}
8293                    :workflows [:auto-release])
8294                "#,
8295            ),
8296            (
8297                crate::dialeto::CaixaDialeto::MoldePosicional,
8298                r#"
8299                  (defcaixa todoku-go
8300                    :kind :Biblioteca
8301                    :ecosystem :go
8302                    :package {:name "todoku-go" :version "0.3.0"})
8303                "#,
8304            ),
8305            (
8306                crate::dialeto::CaixaDialeto::Desconhecido,
8307                r#"(defcaixa :licenca "MIT")"#,
8308            ),
8309        ];
8310
8311        // Coverage: every arm in [`crate::dialeto::CaixaDialeto::ALL`]
8312        // must appear in the fixture table so the pin's arm-set stays
8313        // synchronised with the enum's arm-set. Fails at test time if a
8314        // future fifth arm added to [`crate::dialeto::CaixaDialeto`]
8315        // (with a corresponding `is_molde_family` return) forgot to
8316        // extend this fixture table with a canonical source for the new
8317        // arm — the pin cannot cover an arm it has no source for.
8318        for &expected in crate::dialeto::CaixaDialeto::ALL {
8319            assert!(
8320                fixtures.iter().any(|(d, _)| *d == expected),
8321                "fixture table must carry a canonical source for every \
8322                 CaixaDialeto arm; missing: {expected:?}"
8323            );
8324        }
8325
8326        for &(expected_dialect, src) in fixtures {
8327            let classified = crate::dialeto::classify(src.trim()).unwrap_or_else(|err| {
8328                panic!(
8329                    "fixture source for {expected_dialect:?} must classify \
8330                     cleanly, got err: {err:?}"
8331                )
8332            });
8333            assert_eq!(
8334                classified, expected_dialect,
8335                "fixture source for {expected_dialect:?} must classify as \
8336                 {expected_dialect:?} (drift here defeats the byte-parity \
8337                 pin below — a source labelled for one arm but classifying \
8338                 as another would silently satisfy or violate the pin for \
8339                 the wrong reason)"
8340            );
8341
8342            let outcome = Caixa::from_lisp(src);
8343            match (expected_dialect.is_molde_family(), &outcome) {
8344                (true, Err(LeituraError::DialetoEstrangeiro { dialeto })) => {
8345                    assert_eq!(
8346                        *dialeto, expected_dialect,
8347                        "DialetoEstrangeiro must carry the same typed arm \
8348                         the classifier returned — a drift here would let \
8349                         from_lisp raise the error while pointing at the \
8350                         wrong dialect (e.g. rejecting a \
8351                         MoldePosicional source as Molde). arm: \
8352                         {expected_dialect:?}"
8353                    );
8354                }
8355                (true, other) => panic!(
8356                    "arm {expected_dialect:?} has is_molde_family() = true \
8357                     so from_lisp must raise DialetoEstrangeiro carrying \
8358                     {expected_dialect:?}; got: {other:?}"
8359                ),
8360                (false, Err(LeituraError::DialetoEstrangeiro { dialeto })) => panic!(
8361                    "arm {expected_dialect:?} has is_molde_family() = false \
8362                     so from_lisp must NOT raise DialetoEstrangeiro; got \
8363                     one carrying: {dialeto:?}. This means the typed \
8364                     predicate and the from_lisp partition disagree on \
8365                     this arm — exactly the drift this pin refuses."
8366                ),
8367                (false, _) => {
8368                    // A non-molde arm's source falls through to the
8369                    // derive: Pacote sources parse to Ok(_); Desconhecido
8370                    // sources surface as LeituraError::Leitura from the
8371                    // derive's own unknown-keyword rejection. Either
8372                    // shape is acceptable here — the pin's promise is
8373                    // narrower: "no DialetoEstrangeiro on
8374                    // is_molde_family() == false".
8375                }
8376            }
8377        }
8378    }
8379
8380    // ── M2 typed-substrate slot tests (limits, behavior, upgrade-from, supervisor) ──
8381
8382    #[test]
8383    fn limits_round_trip_via_json() {
8384        use crate::LimitsSpec;
8385        use std::time::Duration;
8386        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8387        c.limits = Some(LimitsSpec {
8388            memory: Some(64 * 1024 * 1024),
8389            fuel: Some(1_000_000),
8390            wall_clock: Some(Duration::from_secs(30)),
8391            cpu: Some(500),
8392        });
8393        let json = serde_json::to_string(&c).unwrap();
8394        assert!(json.contains("\"limits\""));
8395        assert!(json.contains("\"64MiB\""));
8396        assert!(json.contains("\"30s\""));
8397        assert!(json.contains("\"500m\""));
8398        let back: Caixa = serde_json::from_str(&json).unwrap();
8399        assert_eq!(c.limits, back.limits);
8400    }
8401
8402    #[test]
8403    fn behavior_round_trip_via_json() {
8404        use crate::BehaviorSpec;
8405        use std::path::PathBuf;
8406        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8407        c.behavior = Some(BehaviorSpec {
8408            on_init: Some(PathBuf::from("lib/init.lisp")),
8409            on_call: Some(PathBuf::from("lib/handlers.lisp")),
8410            ..Default::default()
8411        });
8412        let json = serde_json::to_string(&c).unwrap();
8413        let back: Caixa = serde_json::from_str(&json).unwrap();
8414        assert_eq!(c.behavior, back.behavior);
8415    }
8416
8417    #[test]
8418    fn upgrade_from_round_trip_via_json() {
8419        use crate::{UpgradeFromEntry, UpgradeInstruction};
8420        use std::path::PathBuf;
8421        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8422        c.upgrade_from = vec![UpgradeFromEntry {
8423            from: "0.1.0".into(),
8424            instructions: vec![
8425                UpgradeInstruction::LoadModule {
8426                    module: "demo".into(),
8427                },
8428                UpgradeInstruction::StateChange {
8429                    script: PathBuf::from("lib/migrations/v01-to-v02.lisp"),
8430                },
8431                UpgradeInstruction::SoftPurge {
8432                    module: "demo-old".into(),
8433                },
8434            ],
8435        }];
8436        let json = serde_json::to_string(&c).unwrap();
8437        let back: Caixa = serde_json::from_str(&json).unwrap();
8438        assert_eq!(c.upgrade_from, back.upgrade_from);
8439    }
8440
8441    #[test]
8442    fn supervisor_view_returns_typed_shape() {
8443        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8444        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
8445        c.kind = CaixaKind::Supervisor;
8446        c.bibliotecas.clear();
8447        c.estrategia = Some(RestartStrategy::OneForOne);
8448        c.max_restarts = Some(5);
8449        c.restart_window = Some("60s".into());
8450        c.children = vec![ChildSpec {
8451            caixa: "worker".into(),
8452            versao: "^0.1".into(),
8453            restart: RestartPolicy::Permanent,
8454        }];
8455        let view = c.supervisor_view().expect("Supervisor kind has a view");
8456        assert_eq!(view.estrategia, RestartStrategy::OneForOne);
8457        assert_eq!(view.max_restarts, 5);
8458        assert_eq!(
8459            view.restart_window,
8460            Some(std::time::Duration::from_secs(60))
8461        );
8462        assert_eq!(view.children.len(), 1);
8463        view.validate().unwrap();
8464    }
8465
8466    #[test]
8467    fn supervisor_view_none_for_non_supervisor_kinds() {
8468        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8469        assert!(c.supervisor_view().is_none());
8470    }
8471
8472    #[test]
8473    fn declared_mesh_slots_empty_for_bare_caixa() {
8474        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8475        assert!(c.declared_mesh_slots().is_empty());
8476    }
8477
8478    #[test]
8479    fn declared_mesh_slots_reports_only_set_slots_in_canonical_order() {
8480        use crate::{Entrada, Membro};
8481        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8482        // Set a non-adjacent pair (:membros + :entrada) to pin that the
8483        // canonical declaration order is preserved regardless of which
8484        // subset is populated.
8485        c.membros = vec![Membro {
8486            caixa: "a".into(),
8487            versao: "^0.1".into(),
8488        }];
8489        c.entrada = Some(Entrada {
8490            host: "x.example.com".into(),
8491            para: "a".into(),
8492            paths: vec![],
8493            port: 8080,
8494        });
8495        assert_eq!(
8496            c.declared_mesh_slots(),
8497            vec![
8498                crate::render::M3_AUTHOR_KEY_MEMBROS,
8499                crate::render::M3_AUTHOR_KEY_ENTRADA,
8500            ]
8501        );
8502    }
8503
8504    #[test]
8505    fn m3_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8506        // Scalar-value pin: the five author-facing kebab-case labels the
8507        // `(defcaixa … :<slot> (…))` surface admits on the M3 top-level
8508        // mesh slot axis, one arm per typed slot. Mirrors the peer
8509        // scalar-value pin the sibling
8510        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8511        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8512        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] M2 top-level slot consts
8513        // carry (f49c8b0), so both altitudes of the typed-slot algebra
8514        // (per-Servico M2 + per-Aplicacao M3) share the same
8515        // "one canonical byte-string per arm" discipline. A future
8516        // rebrand (`:membros` → `:members`, `:contratos` → `:contracts`,
8517        // `:politicas` → `:policies`, `:placement` → `:distribution`,
8518        // `:entrada` → `:ingress`) lands as an edit to exactly one const,
8519        // and every consumer that reaches for the label picks it up at
8520        // build time rather than at runtime as a downstream mismatch.
8521        assert_eq!(crate::render::M3_AUTHOR_KEY_MEMBROS, ":membros");
8522        assert_eq!(crate::render::M3_AUTHOR_KEY_CONTRATOS, ":contratos");
8523        assert_eq!(crate::render::M3_AUTHOR_KEY_POLITICAS, ":politicas");
8524        assert_eq!(crate::render::M3_AUTHOR_KEY_PLACEMENT, ":placement");
8525        assert_eq!(crate::render::M3_AUTHOR_KEY_ENTRADA, ":entrada");
8526    }
8527
8528    #[test]
8529    fn declared_mesh_slots_route_through_lifted_m3_author_key_consts() {
8530        // Production-through-const pin: the five per-arm labels the
8531        // [`Caixa::declared_mesh_slots`] tagger pushes onto its return
8532        // `Vec` route through the lifted
8533        // [`crate::M3_AUTHOR_KEY_MEMBROS`] /
8534        // [`crate::M3_AUTHOR_KEY_CONTRATOS`] /
8535        // [`crate::M3_AUTHOR_KEY_POLITICAS`] /
8536        // [`crate::M3_AUTHOR_KEY_PLACEMENT`] /
8537        // [`crate::M3_AUTHOR_KEY_ENTRADA`] consts, in canonical
8538        // declaration order. A future re-order or drift at the tagger
8539        // (a rename that reaches the tagger but not the const, or vice
8540        // versa) surfaces here at build time rather than at runtime as
8541        // a [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
8542        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8543        // commit. Mirror of the peer
8544        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8545        // pin (f49c8b0) on the sibling per-Servico M2 top-level slot
8546        // axis.
8547        use crate::{Entrada, Membro, MeshPolicy, Placement, PlacementStrategy, WitContract};
8548        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8549        c.membros = vec![Membro {
8550            caixa: "a".into(),
8551            versao: "^0.1".into(),
8552        }];
8553        c.contratos = vec![WitContract {
8554            de: "a".into(),
8555            para: "a".into(),
8556            wit: "wasi:http/proxy".into(),
8557            endpoint: Some("/x".into()),
8558            subject: None,
8559            slot: None,
8560        }];
8561        c.politicas = Some(MeshPolicy::default());
8562        c.placement = Some(Placement {
8563            estrategia: PlacementStrategy::Replicated,
8564            clusters: vec!["rio".into()],
8565            affinity: None,
8566            shard_key: None,
8567        });
8568        c.entrada = Some(Entrada {
8569            host: "x.example.com".into(),
8570            para: "a".into(),
8571            paths: vec![],
8572            port: 8080,
8573        });
8574        assert_eq!(
8575            c.declared_mesh_slots(),
8576            vec![
8577                crate::render::M3_AUTHOR_KEY_MEMBROS,
8578                crate::render::M3_AUTHOR_KEY_CONTRATOS,
8579                crate::render::M3_AUTHOR_KEY_POLITICAS,
8580                crate::render::M3_AUTHOR_KEY_PLACEMENT,
8581                crate::render::M3_AUTHOR_KEY_ENTRADA,
8582            ]
8583        );
8584    }
8585
8586    #[test]
8587    fn declared_supervisor_slots_empty_for_bare_caixa() {
8588        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8589        assert!(c.declared_supervisor_slots().is_empty());
8590    }
8591
8592    #[test]
8593    fn declared_supervisor_slots_reports_only_set_slots_in_canonical_order() {
8594        use crate::RestartStrategy;
8595        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8596        // Set a non-adjacent pair (:estrategia + :restart-window) to pin
8597        // that the canonical declaration order is preserved regardless
8598        // of which subset is populated.
8599        c.estrategia = Some(RestartStrategy::OneForOne);
8600        c.restart_window = Some("60s".into());
8601        assert_eq!(
8602            c.declared_supervisor_slots(),
8603            vec![
8604                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8605                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8606            ]
8607        );
8608    }
8609
8610    #[test]
8611    fn supervisor_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8612        // Scalar-value pin: the four author-facing kebab-case labels the
8613        // `(defcaixa … :<slot> (…))` surface admits on the Supervisor
8614        // supervision-tree slot axis, one arm per typed slot. Mirrors the
8615        // peer scalar-value pins the sibling
8616        // [`crate::render::M2_AUTHOR_KEY_LIMITS`] /
8617        // [`crate::render::M2_AUTHOR_KEY_BEHAVIOR`] /
8618        // [`crate::render::M2_AUTHOR_KEY_UPGRADE_FROM`] top-level M2 slot
8619        // consts and [`crate::render::M3_AUTHOR_KEY_MEMBROS`] etc.
8620        // top-level M3 slot consts carry, so all three kind-scoped
8621        // typed-slot-family author-facing-label axes route through one
8622        // canonical per-arm declaration. A future rebrand
8623        // (`:estrategia` → `:strategy` for English uniformity,
8624        // `:max-restarts` → `:max-intensity` matching Erlang/OTP's
8625        // `MaxIntensity` name, `:restart-window` → `:period` matching
8626        // OTP's `Period` name, `:children` → `:workers` matching Elixir
8627        // idiom) lands as an edit to exactly one const, and every
8628        // consumer that reaches for the label picks it up at build time
8629        // rather than at runtime as a downstream mismatch.
8630        assert_eq!(
8631            crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8632            ":estrategia"
8633        );
8634        assert_eq!(
8635            crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8636            ":max-restarts"
8637        );
8638        assert_eq!(
8639            crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8640            ":restart-window"
8641        );
8642        assert_eq!(crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN, ":children");
8643    }
8644
8645    #[test]
8646    fn declared_supervisor_slots_route_through_lifted_supervisor_author_key_consts() {
8647        // Production-through-const pin: the four per-arm labels the
8648        // [`Caixa::declared_supervisor_slots`] tagger pushes onto its
8649        // return `Vec` route through the lifted
8650        // [`crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA`] /
8651        // [`crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS`] /
8652        // [`crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW`] /
8653        // [`crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN`] consts, in
8654        // canonical declaration order. A future re-order or drift at the
8655        // tagger (a rename that reaches the tagger but not the const, or
8656        // vice versa) surfaces here at build time rather than at runtime
8657        // as a [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
8658        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8659        // commit. Mirror of the peer
8660        // [`declared_servico_slots_route_through_lifted_m2_author_key_consts`]
8661        // (f49c8b0) and
8662        // [`declared_mesh_slots_route_through_lifted_m3_author_key_consts`]
8663        // (882f498) pins on the sibling M2 / M3 top-level slot axes.
8664        use crate::{ChildSpec, RestartPolicy, RestartStrategy};
8665        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8666        c.estrategia = Some(RestartStrategy::OneForOne);
8667        c.max_restarts = Some(5);
8668        c.restart_window = Some("60s".into());
8669        c.children = vec![ChildSpec {
8670            caixa: "worker".into(),
8671            versao: "^0.1".into(),
8672            restart: RestartPolicy::Permanent,
8673        }];
8674        assert_eq!(
8675            c.declared_supervisor_slots(),
8676            vec![
8677                crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA,
8678                crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS,
8679                crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW,
8680                crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN,
8681            ]
8682        );
8683    }
8684
8685    #[test]
8686    fn declared_servico_slots_empty_for_bare_caixa() {
8687        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8688        assert!(c.declared_servico_slots().is_empty());
8689    }
8690
8691    #[test]
8692    fn declared_servico_slots_reports_only_set_slots_in_canonical_order() {
8693        use crate::{UpgradeFromEntry, UpgradeInstruction};
8694        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8695        // Set a non-adjacent pair (:limits + :upgrade-from) to pin that
8696        // the canonical declaration order is preserved regardless of
8697        // which subset is populated.
8698        c.limits = Some(crate::LimitsSpec {
8699            fuel: Some(1_000_000),
8700            ..Default::default()
8701        });
8702        c.upgrade_from = vec![UpgradeFromEntry {
8703            from: "0.1.0".into(),
8704            instructions: vec![UpgradeInstruction::Restart],
8705        }];
8706        assert_eq!(
8707            c.declared_servico_slots(),
8708            vec![
8709                crate::render::M2_AUTHOR_KEY_LIMITS,
8710                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8711            ]
8712        );
8713    }
8714
8715    #[test]
8716    fn m2_top_level_author_key_consts_pin_canonical_kebab_case_labels() {
8717        // Scalar-value pin: the three author-facing kebab-case labels
8718        // the `(defcaixa … :<slot> (…))` surface admits on the M2
8719        // top-level slot axis, one arm per typed slot. Mirrors the peer
8720        // scalar-value pin the sibling renderer-side
8721        // [`crate::M2_KEY_LIMITS`] / [`crate::M2_KEY_BEHAVIOR`] /
8722        // [`crate::M2_KEY_UPGRADE_FROM`] camelCase overlay-container
8723        // consts carry, so both halves of the M2 top-level slot dual
8724        // axis (author-facing kebab-case label + renderer-side
8725        // camelCase overlay-container wire key) route through one
8726        // canonical per-arm declaration. A future rebrand
8727        // (`:limits` → `:sandbox` matching Lunatic per-process
8728        // terminology INSPIRATIONS §III.1, `:behavior` → `:gen-server`
8729        // matching Erlang's verbatim name, `:upgrade-from` → `:appup`
8730        // matching Erlang's verbatim appup name) lands as an edit to
8731        // exactly one const, and every consumer that reaches for the
8732        // label picks it up at build time rather than at runtime as a
8733        // downstream mismatch.
8734        assert_eq!(crate::render::M2_AUTHOR_KEY_LIMITS, ":limits");
8735        assert_eq!(crate::render::M2_AUTHOR_KEY_BEHAVIOR, ":behavior");
8736        assert_eq!(crate::render::M2_AUTHOR_KEY_UPGRADE_FROM, ":upgrade-from");
8737    }
8738
8739    #[test]
8740    fn declared_servico_slots_route_through_lifted_m2_author_key_consts() {
8741        // Production-through-const pin: the three per-arm labels the
8742        // [`Caixa::declared_servico_slots`] tagger pushes onto its
8743        // return `Vec` route through the lifted
8744        // [`crate::M2_AUTHOR_KEY_LIMITS`] /
8745        // [`crate::M2_AUTHOR_KEY_BEHAVIOR`] /
8746        // [`crate::M2_AUTHOR_KEY_UPGRADE_FROM`] consts, in canonical
8747        // declaration order. A future re-order or drift at the tagger
8748        // (a rename that reaches the tagger but not the const, or vice
8749        // versa) surfaces here at build time rather than at runtime as
8750        // a [`crate::LayoutError::ServicoSlotsOnNonServico`]
8751        // `slots: <stale-kebab-case>` diagnostic far from the rename's
8752        // commit. Mirror of the peer
8753        // [`crate::behavior::BehaviorSpec::declared_slots`] production
8754        // tagger pin (889dc18) on the sibling per-callback axis.
8755        use crate::{BehaviorSpec, UpgradeFromEntry, UpgradeInstruction};
8756        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8757        c.limits = Some(crate::LimitsSpec {
8758            fuel: Some(1_000_000),
8759            ..Default::default()
8760        });
8761        c.behavior = Some(BehaviorSpec {
8762            on_init: Some(PathBuf::from("lib/init.lisp")),
8763            ..Default::default()
8764        });
8765        c.upgrade_from = vec![UpgradeFromEntry {
8766            from: "0.1.0".into(),
8767            instructions: vec![UpgradeInstruction::Restart],
8768        }];
8769        assert_eq!(
8770            c.declared_servico_slots(),
8771            vec![
8772                crate::render::M2_AUTHOR_KEY_LIMITS,
8773                crate::render::M2_AUTHOR_KEY_BEHAVIOR,
8774                crate::render::M2_AUTHOR_KEY_UPGRADE_FROM,
8775            ]
8776        );
8777    }
8778
8779    #[test]
8780    fn existing_manifests_unaffected_by_new_optional_slots() {
8781        // Regression test: a caixa.lisp authored before M2 typed slots
8782        // should still parse + serialize cleanly. The bare `defcaixa`
8783        // emitted by `Caixa::template` has none of the new fields.
8784        let src = Caixa::template("legacy");
8785        let c = Caixa::from_lisp(&src).unwrap();
8786        assert!(c.limits.is_none());
8787        assert!(c.behavior.is_none());
8788        assert!(c.upgrade_from.is_empty());
8789        assert!(c.estrategia.is_none());
8790        assert!(c.children.is_empty());
8791
8792        // And to_lisp emits a manifest with the new slots in the
8793        // empty/default state — round-trippable.
8794        let emitted = c.to_lisp();
8795        let back = Caixa::from_lisp(&emitted).unwrap();
8796        assert_eq!(c, back);
8797    }
8798
8799    #[test]
8800    fn validate_deps_accepts_canonical_caixa() {
8801        // Positive control: the bare template — zero deps, zero
8802        // deps_dev — passes the gate trivially. A future axis added to
8803        // `Dep::validate` mustn't regress an empty-deps caixa to a
8804        // build error.
8805        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8806        c.validate_deps().unwrap();
8807    }
8808
8809    #[test]
8810    fn validate_deps_rejects_invalid_versao_in_deps() {
8811        // Fail-before-pass-after pin: a malformed `:deps :versao`
8812        // surfaces at validate_deps() time, not at lacre-resolve time.
8813        // Mirrors `rejects_invalid_membro_versao_requirement` and
8814        // `validate_rejects_invalid_child_versao_requirement` on the
8815        // other two `:versao` axes.
8816        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8817        c.deps = vec![Dep::simple("caixa-teia", "^bad-version")];
8818        let err = c.validate_deps().unwrap_err();
8819        assert!(
8820            matches!(
8821                err,
8822                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8823                    if nome == "caixa-teia" && versao == "^bad-version"
8824            ),
8825            "got {err:?}"
8826        );
8827    }
8828
8829    #[test]
8830    fn validate_deps_rejects_invalid_versao_in_deps_dev() {
8831        // Parity pin: `:deps-dev` must run through the same per-entry
8832        // validator as `:deps` — a typo in either axis surfaces the
8833        // same diagnostic. Without this leg, `:deps-dev` would be a
8834        // second-class citizen of the typed surface and an author
8835        // could land a build that passes validate_deps but fails at
8836        // `feira lock`-time when the dev-dep is resolved for a test
8837        // build.
8838        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8839        c.deps_dev = vec![Dep::simple("tatara-check", "^^0.1")];
8840        let err = c.validate_deps().unwrap_err();
8841        assert!(
8842            matches!(
8843                err,
8844                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
8845                    if nome == "tatara-check" && versao == "^^0.1"
8846            ),
8847            "got {err:?}"
8848        );
8849    }
8850
8851    #[test]
8852    fn validate_deps_runs_deps_before_deps_dev() {
8853        // Order pin: when both lists carry typos, the `:deps`
8854        // diagnostic surfaces first. The author's mental model is
8855        // "runtime deps are load-bearing; dev deps are scaffolding";
8856        // surfacing the runtime axis first matches that hierarchy.
8857        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8858        c.deps = vec![Dep::simple("runtime-dep", "^bad-runtime")];
8859        c.deps_dev = vec![Dep::simple("dev-dep", "^bad-dev")];
8860        let err = c.validate_deps().unwrap_err();
8861        assert!(
8862            matches!(
8863                err,
8864                crate::dep::DepError::VersaoInvalid { ref nome, .. }
8865                    if nome == "runtime-dep"
8866            ),
8867            "expected `:deps` typo to surface first, got {err:?}"
8868        );
8869    }
8870
8871    #[test]
8872    fn validate_deps_accepts_canonical_versao_forms_in_both_lists() {
8873        // Positive control sweep across both lists. Pin every
8874        // canonical Cargo-shaped form so a future tightening of the
8875        // accepted set surfaces here as a test failure (parity with
8876        // `accepts_canonical_membro_versao_forms` and
8877        // `validate_accepts_canonical_child_versao_forms`).
8878        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8879        c.deps = vec![
8880            Dep::simple("caret", "^0.1"),
8881            Dep::simple("tilde", "~0.1.2"),
8882            Dep::simple("exact", "0.1.0"),
8883            Dep::simple("wildcard", "*"),
8884            Dep::simple("multi-range", ">=0.1, <2"),
8885        ];
8886        c.deps_dev = vec![
8887            Dep::simple("dev-caret", "^0.1"),
8888            Dep::simple("dev-wildcard", "*"),
8889        ];
8890        c.validate_deps().unwrap();
8891    }
8892
8893    #[test]
8894    fn validate_deps_diagnostic_carries_offending_dep() {
8895        // Diagnostic-shape pin: the error names the offending entry's
8896        // `:nome` + `:versao` verbatim and carries a non-empty
8897        // `reason` from `semver::VersionReq::parse`, so a `feira lint`
8898        // run can render the diagnostic without re-parsing.
8899        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8900        c.deps = vec![Dep::simple("caixa-teia", "not-a-req")];
8901        let err = c.validate_deps().unwrap_err();
8902        let crate::dep::DepError::VersaoInvalid {
8903            nome,
8904            versao,
8905            reason,
8906        } = err
8907        else {
8908            panic!("expected VersaoInvalid, got other variant");
8909        };
8910        assert_eq!(nome, "caixa-teia");
8911        assert_eq!(versao, "not-a-req");
8912        assert!(
8913            !reason.is_empty(),
8914            "VersaoInvalid `reason` must carry the parser's wording verbatim"
8915        );
8916    }
8917
8918    #[test]
8919    fn validate_deps_rejects_ambiguous_fonte_in_deps_dev() {
8920        // Cross-axis pin: `validate_deps` walks both :deps and
8921        // :deps-dev through `Dep::validate`, and the new fonte gate
8922        // (`:tag` + `:branch` both set — the canonical "pin drift"
8923        // footgun) must surface from the :deps-dev arm with the
8924        // offending entry's :nome named. Pin the :deps-dev arm
8925        // explicitly so a future shortcut that only walks :deps
8926        // surfaces here as a regression.
8927        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8928        c.deps_dev = vec![Dep {
8929            nome: "dev-only".into(),
8930            versao: "^0.1".into(),
8931            fonte: Some(crate::DepSource::Git {
8932                repo: "github:p/x".into(),
8933                tag: Some("v1".into()),
8934                rev: None,
8935                branch: Some("main".into()),
8936            }),
8937            opcional: false,
8938            caracteristicas: vec![],
8939        }];
8940        let err = c.validate_deps().unwrap_err();
8941        let crate::dep::DepError::FontePinAmbiguous { nome, pins } = err else {
8942            panic!("expected FontePinAmbiguous from :deps-dev walk");
8943        };
8944        assert_eq!(nome, "dev-only");
8945        assert!(pins.contains(":tag") && pins.contains(":branch"));
8946    }
8947
8948    #[test]
8949    fn validate_deps_rejects_empty_repo_in_deps() {
8950        // Parity pin on the :deps arm: an empty :repo on the runtime
8951        // deps list surfaces the same FonteRepoEmpty diagnostic the
8952        // dep.rs per-entry tests pin, naming the offending entry.
8953        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8954        c.deps = vec![Dep {
8955            nome: "runtime".into(),
8956            versao: "^0.1".into(),
8957            fonte: Some(crate::DepSource::Git {
8958                repo: String::new(),
8959                tag: Some("v1".into()),
8960                rev: None,
8961                branch: None,
8962            }),
8963            opcional: false,
8964            caracteristicas: vec![],
8965        }];
8966        let err = c.validate_deps().unwrap_err();
8967        assert!(
8968            matches!(
8969                err,
8970                crate::dep::DepError::FonteRepoEmpty { ref nome }
8971                    if nome == "runtime"
8972            ),
8973            "got {err:?}"
8974        );
8975    }
8976
8977    // ── validate_deps: within-list :nome set-not-multiset gate ─────────
8978
8979    #[test]
8980    fn validate_deps_rejects_duplicate_nome_in_deps() {
8981        // Fail-before-pass-after pin: two `:deps` entries naming the same
8982        // caixa carry two `:versao` / `:fonte` / feature triples that the
8983        // caixa-resolver's lacre pipeline collapses (the second silently
8984        // overwrites the first at `concrete_versao`-resolve time). The
8985        // gate surfaces the duplicate at validate-time, naming the
8986        // offending caixa + the list, before the resolver-side silent
8987        // drop. Mirrors the peer typed-graph duplicate gates
8988        // (`DuplicateChildCaixa`, `MembroDuplicate`, `DuplicateFrom`, …).
8989        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
8990        c.deps = vec![
8991            Dep::simple("caixa-teia", "^0.1"),
8992            Dep::simple("caixa-teia", "^0.2"),
8993        ];
8994        let err = c.validate_deps().unwrap_err();
8995        assert!(
8996            matches!(
8997                err,
8998                crate::dep::DepError::DuplicateNome { ref nome, list }
8999                    if nome == "caixa-teia" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9000            ),
9001            "got {err:?}"
9002        );
9003    }
9004
9005    #[test]
9006    fn validate_deps_rejects_duplicate_nome_in_deps_dev() {
9007        // Parity pin: `:deps-dev` runs through the same per-list
9008        // duplicate check as `:deps` — neither axis is a second-class
9009        // citizen of the set-not-multiset discipline.
9010        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9011        c.deps_dev = vec![
9012            Dep::simple("tatara-check", "*"),
9013            Dep::simple("tatara-check", "^0.1"),
9014        ];
9015        let err = c.validate_deps().unwrap_err();
9016        assert!(
9017            matches!(
9018                err,
9019                crate::dep::DepError::DuplicateNome { ref nome, list }
9020                    if nome == "tatara-check" && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
9021            ),
9022            "got {err:?}"
9023        );
9024    }
9025
9026    #[test]
9027    fn validate_deps_accepts_cross_list_same_nome() {
9028        // The Cargo `[dependencies]` + `[dev-dependencies]` override
9029        // convention is preserved: a name appearing in *both* lists is
9030        // valid (the dev-pin overrides at test/dev time). Only
9031        // within-list duplicates are structurally incoherent — pin the
9032        // permissive cross-list semantics so a future shortcut that
9033        // collapses the two seen-sets into one surfaces here as a test
9034        // failure.
9035        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9036        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
9037        c.deps_dev = vec![Dep::simple("caixa-teia", "^0.2")];
9038        c.validate_deps().unwrap();
9039    }
9040
9041    #[test]
9042    fn validate_deps_accepts_distinct_nome_in_both_lists() {
9043        // Positive control: distinct names within each list pass — the
9044        // gate's identity element on the canonical authoring shape.
9045        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9046        c.deps = vec![
9047            Dep::simple("caixa-teia", "^0.1"),
9048            Dep::simple("pleme-mesh", "*"),
9049        ];
9050        c.deps_dev = vec![
9051            Dep::simple("tatara-check", "*"),
9052            Dep::simple("dev-shim", "^0.1"),
9053        ];
9054        c.validate_deps().unwrap();
9055    }
9056
9057    #[test]
9058    fn validate_deps_per_entry_validate_fires_before_duplicate_in_deps() {
9059        // Diagnostic-precedence pin: a malformed `:versao` on the
9060        // duplicating entry surfaces its narrower `VersaoInvalid`
9061        // diagnostic first, before the cross-entry duplicate gate fires
9062        // — the canonical "per-entry shape before cross-entry uniqueness"
9063        // precedence every peer set-not-multiset gate establishes
9064        // (`*_invalid_fires_before_duplicate_check` pins on
9065        // `SupervisorSpec::validate`, `AplicacaoSpec::validate_membros`,
9066        // `validate_upgrade_from`).
9067        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9068        c.deps = vec![
9069            Dep::simple("caixa-teia", "^0.1"),
9070            Dep::simple("caixa-teia", "^bad-version"),
9071        ];
9072        let err = c.validate_deps().unwrap_err();
9073        assert!(
9074            matches!(
9075                err,
9076                crate::dep::DepError::VersaoInvalid { ref nome, ref versao, .. }
9077                    if nome == "caixa-teia" && versao == "^bad-version"
9078            ),
9079            "expected VersaoInvalid to surface before DuplicateNome, got {err:?}"
9080        );
9081    }
9082
9083    #[test]
9084    fn validate_deps_duplicate_diagnostic_names_first_collision() {
9085        // First-collision determinism pin: with three entries naming the
9086        // same caixa, the first colliding pair surfaces — not the last.
9087        // Mirrors the peer first-collision posture on every
9088        // duplicate-target gate
9089        // (`validate_upgrade_from_duplicate_diagnostic_names_second_collision`
9090        // — the second entry is the first collision; this gate uses the
9091        // same shape: the second entry's `:nome` lands in the diagnostic
9092        // because `seen.insert(first.nome)` already populated the set).
9093        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9094        c.deps = vec![
9095            Dep::simple("caixa-teia", "^0.1"),
9096            Dep::simple("caixa-teia", "^0.2"),
9097            Dep::simple("caixa-teia", "^0.3"),
9098        ];
9099        let err = c.validate_deps().unwrap_err();
9100        // The diagnostic carries the offending caixa name; the
9101        // implementation surfaces on the *second* entry (the first
9102        // collision), so the test pins the `:nome` value.
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_duplicate_in_deps_fires_before_duplicate_in_deps_dev() {
9115        // Cross-list precedence pin: when both lists carry duplicates,
9116        // the `:deps` diagnostic surfaces first — same author-mental-
9117        // model ordering the `validate_deps_runs_deps_before_deps_dev`
9118        // pin establishes for malformed `:versao` (runtime axis before
9119        // dev axis).
9120        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9121        c.deps = vec![
9122            Dep::simple("runtime-dep", "^0.1"),
9123            Dep::simple("runtime-dep", "^0.2"),
9124        ];
9125        c.deps_dev = vec![Dep::simple("dev-dep", "*"), Dep::simple("dev-dep", "^0.1")];
9126        let err = c.validate_deps().unwrap_err();
9127        assert!(
9128            matches!(
9129                err,
9130                crate::dep::DepError::DuplicateNome { ref nome, list }
9131                    if nome == "runtime-dep" && list == crate::render::DEP_AUTHOR_KEY_DEPS
9132            ),
9133            "expected :deps duplicate to surface before :deps-dev duplicate, got {err:?}"
9134        );
9135    }
9136
9137    #[test]
9138    fn validate_deps_empty_lists_pass_duplicate_gate() {
9139        // Empty-set identity pin: the bare template (zero deps, zero
9140        // deps_dev) passes the duplicate gate as the gate's identity
9141        // element. A future tighten that conflates "empty" with
9142        // "missing" would regress this baseline.
9143        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9144        c.validate_deps().unwrap();
9145    }
9146
9147    #[test]
9148    fn validate_deps_duplicate_diagnostic_carries_list_tag() {
9149        // Diagnostic-shape pin: the `list:` field tags which list the
9150        // duplicate landed in (`:deps` vs `:deps-dev`) verbatim, so a
9151        // `feira lint` run can route the author to the right block in
9152        // their caixa.lisp without re-deriving the list from context.
9153        // Same self-locating shape every peer per-axis diagnostic
9154        // already exposes.
9155        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9156        c.deps_dev = vec![
9157            Dep::simple("dev-thing", "*"),
9158            Dep::simple("dev-thing", "^0.1"),
9159        ];
9160        let err = c.validate_deps().unwrap_err();
9161        let crate::dep::DepError::DuplicateNome { nome, list } = err else {
9162            panic!("expected DuplicateNome from :deps-dev walk");
9163        };
9164        assert_eq!(nome, "dev-thing");
9165        assert_eq!(list, crate::render::DEP_AUTHOR_KEY_DEPS_DEV);
9166    }
9167
9168    // ── validate_deps: per-entry :caracteristicas set-discipline gate ──
9169
9170    #[test]
9171    fn validate_deps_surfaces_caracteristicas_duplicate_in_deps_list() {
9172        // Thread-through pin on `:deps`: the per-entry
9173        // `Dep::validate_caracteristicas` gate fires inside
9174        // `Caixa::validate_deps`'s linear walk, so a malformed feature
9175        // list on any `:deps` entry surfaces as a `DepError` from
9176        // `validate_deps` — the same reachability shape every per-entry
9177        // `Dep::validate` arm threads through. Without this pin a future
9178        // shortcut that skips the per-entry `Dep::validate` call on the
9179        // cross-entry-uniqueness path would mask the within-entry
9180        // `:caracteristicas` gates.
9181        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9182        c.deps = vec![Dep {
9183            nome: "caixa-teia".into(),
9184            versao: "^0.1".into(),
9185            fonte: None,
9186            opcional: false,
9187            caracteristicas: vec!["http".into(), "http".into()],
9188        }];
9189        let err = c.validate_deps().unwrap_err();
9190        let crate::dep::DepError::CaracteristicaDuplicate {
9191            nome,
9192            caracteristica,
9193        } = err
9194        else {
9195            panic!("expected CaracteristicaDuplicate from :deps walk, got {err:?}");
9196        };
9197        assert_eq!(nome, "caixa-teia");
9198        assert_eq!(caracteristica, "http");
9199    }
9200
9201    #[test]
9202    fn validate_deps_surfaces_caracteristicas_empty_in_deps_dev_list() {
9203        // Peer thread-through pin on `:deps-dev`: same reachability as
9204        // the `:deps` arm above, on the dev-only authoring axis. Pins
9205        // that the `validate_deps` walk visits both lists' per-entry
9206        // gates uniformly. The empty-feature arm carries here so both
9207        // new `:caracteristicas` arms are surfaced via at least one
9208        // `validate_deps` thread-through.
9209        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9210        c.deps_dev = vec![Dep {
9211            nome: "caixa-teia".into(),
9212            versao: "^0.1".into(),
9213            fonte: None,
9214            opcional: false,
9215            caracteristicas: vec![String::new()],
9216        }];
9217        let err = c.validate_deps().unwrap_err();
9218        let crate::dep::DepError::CaracteristicaEmpty { nome } = err else {
9219            panic!("expected CaracteristicaEmpty from :deps-dev walk, got {err:?}");
9220        };
9221        assert_eq!(nome, "caixa-teia");
9222    }
9223
9224    #[test]
9225    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_list() {
9226        // Thread-through pin on `:deps`: the per-entry
9227        // `Dep::validate_caracteristicas` value-shape gate (lifted via
9228        // `crate::render::is_cargo_feature_name`) fires inside
9229        // `Caixa::validate_deps`'s linear walk on the `:deps` list, so
9230        // a structurally invalid feature name on any `:deps` entry
9231        // surfaces as `DepError::CaracteristicaInvalid` from
9232        // `validate_deps` — the same reachability shape every per-entry
9233        // `Dep::validate` arm threads through. Without this pin a
9234        // future shortcut that skips the per-entry `Dep::validate` call
9235        // on the cross-entry-uniqueness path would mask the within-
9236        // entry `:caracteristicas` value-shape gate.
9237        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9238        c.deps = vec![Dep {
9239            nome: "caixa-teia".into(),
9240            versao: "^0.1".into(),
9241            fonte: None,
9242            opcional: false,
9243            caracteristicas: vec!["+http".into()],
9244        }];
9245        let err = c.validate_deps().unwrap_err();
9246        let crate::dep::DepError::CaracteristicaInvalid {
9247            nome,
9248            caracteristica,
9249            ..
9250        } = err
9251        else {
9252            panic!("expected CaracteristicaInvalid from :deps walk, got {err:?}");
9253        };
9254        assert_eq!(nome, "caixa-teia");
9255        assert_eq!(caracteristica, "+http");
9256    }
9257
9258    #[test]
9259    fn validate_deps_surfaces_caracteristicas_invalid_in_deps_dev_list() {
9260        // Peer thread-through pin on `:deps-dev`: same reachability as
9261        // the `:deps` arm above, on the dev-only authoring axis. The
9262        // `http/json` shape carries here so the segment-separator
9263        // diagnostic (the canonical Cargo `dep/feat` namespaced-dep
9264        // confusion footgun) is surfaced via the cross-entry walk too —
9265        // pinning that the `:deps-dev` list visits the same per-entry
9266        // value-shape gate as the `:deps` list.
9267        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9268        c.deps_dev = vec![Dep {
9269            nome: "caixa-teia".into(),
9270            versao: "^0.1".into(),
9271            fonte: None,
9272            opcional: false,
9273            caracteristicas: vec!["http/json".into()],
9274        }];
9275        let err = c.validate_deps().unwrap_err();
9276        let crate::dep::DepError::CaracteristicaInvalid {
9277            nome,
9278            caracteristica,
9279            ..
9280        } = err
9281        else {
9282            panic!("expected CaracteristicaInvalid from :deps-dev walk, got {err:?}");
9283        };
9284        assert_eq!(nome, "caixa-teia");
9285        assert_eq!(caracteristica, "http/json");
9286    }
9287
9288    #[test]
9289    fn to_lisp_preserves_deps() {
9290        let src = r#"
9291(defcaixa
9292  :nome "x"
9293  :versao "0.1.0"
9294  :kind Biblioteca
9295  :deps ((:nome "a" :versao "^0.1")
9296         (:nome "b" :versao "*" :fonte (:tipo git :repo "github:o/b" :tag "v1"))))
9297"#;
9298        let c1 = Caixa::from_lisp(src).unwrap();
9299        let emitted = c1.to_lisp();
9300        let c2 = Caixa::from_lisp(&emitted).expect("round trip");
9301        assert_eq!(c1.deps, c2.deps);
9302    }
9303
9304    // ── Caixa::validate_nome — top-level :nome value-shape gate ─────────
9305
9306    fn caixa_with_nome(nome: &str) -> Caixa {
9307        let mut c = Caixa::from_lisp(&Caixa::template("placeholder")).unwrap();
9308        c.nome = nome.to_string();
9309        c
9310    }
9311
9312    #[test]
9313    fn validate_nome_accepts_canonical_template() {
9314        // Positive control: the bare `feira init`-style template's
9315        // `:nome` ("demo") is a canonical DNS-1123 label; the gate must
9316        // not regress this baseline shape. A future tightening of the
9317        // accepted set surfaces here as a test failure first.
9318        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9319        c.validate_nome().unwrap();
9320    }
9321
9322    #[test]
9323    fn validate_nome_accepts_canonical_forms() {
9324        // Positive-set sweep: each realistic caixa-name shape the K8s
9325        // apiserver accepts as a `metadata.name` label must pass —
9326        // single-word, hyphen-joined, version-suffixed, single-char,
9327        // two-char, digit-start (DNS-1123 allows this; the stricter
9328        // DNS-1035 Service-name rule doesn't), version-suffix-bearing.
9329        // Mirrors `accepts_canonical_membro_caixa_forms` (3f9d7a0) on
9330        // the peer member-name axis.
9331        for nome in [
9332            "checkout",
9333            "cart-v2",
9334            "a",
9335            "db",
9336            "3rd-party-shim",
9337            "payment-retry",
9338            "0",
9339        ] {
9340            caixa_with_nome(nome)
9341                .validate_nome()
9342                .unwrap_or_else(|e| panic!("canonical :nome {nome:?} must validate, got {e:?}"));
9343        }
9344    }
9345
9346    #[test]
9347    fn validate_nome_rejects_empty() {
9348        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9349        // an empty `:nome` (the derive macro stores the raw String);
9350        // the gate's empty arm names the offending axis with a narrower
9351        // diagnostic than the `NomeInvalid` parse arm would emit.
9352        let c = caixa_with_nome("");
9353        let err = c.validate_nome().unwrap_err();
9354        assert_eq!(err, ManifestError::NomeEmpty);
9355    }
9356
9357    #[test]
9358    fn validate_nome_rejects_uppercase() {
9359        // The canonical "I copied the TitleCase display name verbatim"
9360        // footgun. The K8s apiserver rejects `metadata.name: MyApp` at
9361        // admission on every derived artifact (Helm chart, ComputeUnit,
9362        // CNP, HTTPRoute, label values); the gate moves the diagnostic
9363        // to the source `caixa.lisp` and the reason suggests the
9364        // lowercased fix verbatim.
9365        let c = caixa_with_nome("MyApp");
9366        let err = c.validate_nome().unwrap_err();
9367        let ManifestError::NomeInvalid { nome, reason } = err else {
9368            panic!("expected NomeInvalid for uppercase :nome");
9369        };
9370        assert_eq!(nome, "MyApp");
9371        assert!(
9372            reason.contains("uppercase") && reason.contains("myapp"),
9373            "diagnostic must name the violation + the lowercased fix, got {reason:?}"
9374        );
9375    }
9376
9377    #[test]
9378    fn validate_nome_rejects_underscore() {
9379        // The Python-/Postgres-style `snake_case` leak. DNS-1123 forbids
9380        // `_`; the apiserver rejects on admission across every derived
9381        // artifact. Same fixture pinned for `:membros :caixa` (3f9d7a0)
9382        // and `:children :caixa` (31bfa43).
9383        let c = caixa_with_nome("my_app");
9384        let err = c.validate_nome().unwrap_err();
9385        assert!(
9386            matches!(
9387                err,
9388                ManifestError::NomeInvalid { ref nome, ref reason }
9389                    if nome == "my_app" && reason.contains('_')
9390            ),
9391            "got {err:?}"
9392        );
9393    }
9394
9395    #[test]
9396    fn validate_nome_rejects_dot() {
9397        // A `:nome` is a single DNS-1123 label, not a subdomain. The
9398        // "I want to namespace with `.`" footgun the gate redirects to
9399        // `-` via the shared predicate's reason wording.
9400        let c = caixa_with_nome("team.app");
9401        let err = c.validate_nome().unwrap_err();
9402        assert!(
9403            matches!(
9404                err,
9405                ManifestError::NomeInvalid { ref nome, ref reason }
9406                    if nome == "team.app" && reason.contains('.')
9407            ),
9408            "got {err:?}"
9409        );
9410    }
9411
9412    #[test]
9413    fn validate_nome_rejects_leading_hyphen() {
9414        // DNS-1123 boundary rule: the label must start with an ASCII
9415        // alphanumeric. Pin the leading-`-` arm explicitly.
9416        let c = caixa_with_nome("-app");
9417        let err = c.validate_nome().unwrap_err();
9418        assert!(
9419            matches!(
9420                err,
9421                ManifestError::NomeInvalid { ref nome, .. } if nome == "-app"
9422            ),
9423            "got {err:?}"
9424        );
9425    }
9426
9427    #[test]
9428    fn validate_nome_rejects_trailing_hyphen() {
9429        // Symmetric arm of the boundary rule, pinned separately so a
9430        // future relaxation that only checks the leading position
9431        // surfaces here. Mirrors `rejects_membro_caixa_with_trailing_hyphen`
9432        // and `_with_trailing_hyphen` on the supervisor / aplicacao
9433        // axes.
9434        let c = caixa_with_nome("app-");
9435        let err = c.validate_nome().unwrap_err();
9436        assert!(
9437            matches!(
9438                err,
9439                ManifestError::NomeInvalid { ref nome, .. } if nome == "app-"
9440            ),
9441            "got {err:?}"
9442        );
9443    }
9444
9445    #[test]
9446    fn validate_nome_rejects_unicode() {
9447        // IDN must be pre-encoded as Punycode (`xn--…`); raw Unicode
9448        // bytes are rejected by the K8s apiserver on every name axis.
9449        let c = caixa_with_nome("café");
9450        let err = c.validate_nome().unwrap_err();
9451        assert!(
9452            matches!(
9453                err,
9454                ManifestError::NomeInvalid { ref nome, .. } if nome == "café"
9455            ),
9456            "got {err:?}"
9457        );
9458    }
9459
9460    #[test]
9461    fn validate_nome_rejects_whitespace() {
9462        // The paste-from-sketch / paste-from-spec footgun. Internal
9463        // whitespace is rejected by every K8s name axis.
9464        let c = caixa_with_nome("my app");
9465        let err = c.validate_nome().unwrap_err();
9466        assert!(
9467            matches!(
9468                err,
9469                ManifestError::NomeInvalid { ref nome, .. } if nome == "my app"
9470            ),
9471            "got {err:?}"
9472        );
9473    }
9474
9475    #[test]
9476    fn validate_nome_rejects_too_long() {
9477        // 64-byte boundary pin: the K8s apiserver rejects any
9478        // `metadata.name` over 63 bytes at admission; the diagnostic
9479        // names both the 63-byte cap and the actual length so the
9480        // author can shorten in one edit. Mirrors `_too_long` on the
9481        // peer member-/cluster-/child-name axes.
9482        let over = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN + 1);
9483        let c = caixa_with_nome(&over);
9484        let err = c.validate_nome().unwrap_err();
9485        let ManifestError::NomeInvalid { nome, reason } = err else {
9486            panic!("expected NomeInvalid for over-cap :nome");
9487        };
9488        assert_eq!(nome.len(), crate::DNS_1123_LABEL_MAX_LEN + 1);
9489        assert!(
9490            reason.contains("63") && reason.contains("64"),
9491            "diagnostic must name the cap + actual length, got {reason:?}"
9492        );
9493    }
9494
9495    #[test]
9496    fn nome_max_length_validates() {
9497        // The 63-byte cap exactly — the boundary-accepting case pinned
9498        // alongside `validate_nome_rejects_too_long` so a future cap
9499        // shift surfaces both arms simultaneously. Mirrors
9500        // `membro_caixa_max_length_validates`,
9501        // `placement_cluster_max_length_validates`,
9502        // `child_caixa_max_length_validates`.
9503        let at_cap = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9504        caixa_with_nome(&at_cap).validate_nome().unwrap();
9505    }
9506
9507    #[test]
9508    fn nome_empty_takes_precedence_over_invalid() {
9509        // Order pin: the empty arm fires before the predicate is
9510        // consulted. Empty < invalid in self-locating-ness — the
9511        // narrower `NomeEmpty` diagnostic doesn't carry a useless
9512        // `nome: ""` reference into the parser-shaped reason. Mirrors
9513        // `membro_caixa_empty_takes_precedence_over_invalid` on the
9514        // peer axis (3f9d7a0).
9515        let c = caixa_with_nome("");
9516        assert_eq!(c.validate_nome().unwrap_err(), ManifestError::NomeEmpty);
9517    }
9518
9519    #[test]
9520    fn nome_invalid_diagnostic_carries_offending_nome() {
9521        // Diagnostic-shape pin: the error names the offending `:nome`
9522        // verbatim with a non-empty parser-shaped reason, so a `feira
9523        // lint` run can render the diagnostic without re-parsing.
9524        // Mirrors `membro_caixa_invalid_diagnostic_carries_offending_caixa`.
9525        let c = caixa_with_nome("MyApp");
9526        let err = c.validate_nome().unwrap_err();
9527        let ManifestError::NomeInvalid { nome, reason } = err else {
9528            panic!("expected NomeInvalid variant");
9529        };
9530        assert_eq!(nome, "MyApp");
9531        assert!(
9532            !reason.is_empty(),
9533            "NomeInvalid `reason` must carry the predicate's wording verbatim"
9534        );
9535    }
9536
9537    // ── Caixa::validate_nome_chart_name_budget — joint-length on `:nome` ──
9538    //
9539    // The bare-`:nome` axis [`Caixa::validate_nome`] caps at 63 bytes
9540    // via DNS-1123; this second-axis gate caps the joint
9541    // `lareira-<nome>` chart name at the same 63-byte ceiling. The
9542    // canonical [`crate::lareira_chart_name`] helper's doc comment
9543    // (f7320d7, caixa-core/src/render.rs:3198) explicitly deferred:
9544    // "the M4 admission webhook will pin the joint-length invariant
9545    // when it lands". These tests pin it at the manifest-validate
9546    // layer instead, fail-before-pass-after on the 56-byte boundary.
9547
9548    #[test]
9549    fn validate_nome_chart_name_budget_accepts_canonical_template() {
9550        // Positive control: the bare `feira init`-style template's
9551        // `:nome` ("demo") sits far below the cap; the gate must not
9552        // regress this baseline. Same shape every peer
9553        // value-shape-gate baseline pin uses.
9554        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9555        c.validate_nome_chart_name_budget().unwrap();
9556    }
9557
9558    #[test]
9559    fn validate_nome_chart_name_budget_accepts_canonical_fixtures() {
9560        // Positive-set sweep across the canonical author surface every
9561        // in-tree fixture uses (`hello-rio`, `cart`, `checkout`,
9562        // `worker`, the `checkout-aplicacao` example members, the
9563        // `example-attest` caixa-tatara fixture). Every value sits
9564        // far below the 55-byte per-`:nome` budget. Same shape every
9565        // peer per-axis baseline pin uses.
9566        for nome in [
9567            "hello-rio",
9568            "cart",
9569            "checkout",
9570            "worker",
9571            "example-attest",
9572            "demo",
9573            "a",
9574        ] {
9575            caixa_with_nome(nome)
9576                .validate_nome_chart_name_budget()
9577                .unwrap_or_else(|e| {
9578                    panic!("canonical :nome {nome:?} must pass chart-name budget, got {e:?}")
9579                });
9580        }
9581    }
9582
9583    #[test]
9584    fn validate_nome_chart_name_budget_accepts_nome_at_cap() {
9585        // Boundary-accepting case at the 55-byte per-`:nome` budget —
9586        // the joint chart name is exactly 63 bytes, the DNS-1123 label
9587        // cap. Pinned alongside the rejecting-arm test so a future cap
9588        // shift surfaces both arms simultaneously. Mirrors
9589        // `nome_max_length_validates` on the peer bare-`:nome` axis.
9590        let at_cap = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN);
9591        caixa_with_nome(&at_cap)
9592            .validate_nome_chart_name_budget()
9593            .unwrap();
9594    }
9595
9596    #[test]
9597    fn validate_nome_chart_name_budget_rejects_nome_one_over_cap() {
9598        // Fail-before-pass-after pin on the 56-byte boundary: the
9599        // smallest `:nome` length that overflows the joint chart-name
9600        // cap. The inner [`is_dns_1123_label`] gate
9601        // (`Caixa::validate_nome`) accepts it (56 ≤ 63), so prior to
9602        // this gate it silently passed the manifest-validate cascade
9603        // and surfaced as a `helm lint` / apiserver rejection on the
9604        // rendered chart name far from the source `caixa.lisp`, with
9605        // no field naming the overflow. With this gate the diagnostic
9606        // names the offending `:nome` verbatim alongside the rendered
9607        // chart name and the budget, so the author can shorten in one
9608        // edit. Mirrors `validate_nome_rejects_too_long` on the peer
9609        // bare-`:nome` axis.
9610        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9611        let c = caixa_with_nome(&over);
9612        let err = c.validate_nome_chart_name_budget().unwrap_err();
9613        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9614            panic!("expected NomeChartNameBudgetExceeded for over-budget :nome");
9615        };
9616        assert_eq!(nome.len(), crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9617        assert_eq!(nome, over);
9618        assert!(
9619            reason.contains("63") && reason.contains("64") && reason.contains("55"),
9620            "diagnostic must name the DNS-1123 cap (63), the actual chart-name length (64), \
9621             and the per-`:nome` budget (55), got {reason:?}"
9622        );
9623    }
9624
9625    #[test]
9626    fn validate_nome_chart_name_budget_rejects_nome_at_bare_dns_cap() {
9627        // The 63-byte `:nome` boundary — passes the bare-`:nome`
9628        // [`is_dns_1123_label`] cap exactly, but produces a 71-byte
9629        // joint chart name that overflows the DNS-1123 label cap
9630        // structurally. The most stringent fail-before-pass-after
9631        // surface: every `:nome` in the 56..=63-byte range passed the
9632        // prior cascade and broke at admission.
9633        let bare_max = "a".repeat(crate::DNS_1123_LABEL_MAX_LEN);
9634        let c = caixa_with_nome(&bare_max);
9635        // The bare-`:nome` gate accepts the 63-byte length.
9636        c.validate_nome().unwrap();
9637        // The new joint-length gate rejects it.
9638        let err = c.validate_nome_chart_name_budget().unwrap_err();
9639        assert!(
9640            matches!(
9641                err,
9642                ManifestError::NomeChartNameBudgetExceeded { ref nome, .. }
9643                    if nome.len() == crate::DNS_1123_LABEL_MAX_LEN
9644            ),
9645            "got {err:?}"
9646        );
9647    }
9648
9649    #[test]
9650    fn validate_nome_chart_name_budget_diagnostic_carries_offending_chart_name() {
9651        // Diagnostic-shape pin: the rendered `lareira-<nome>` chart
9652        // name appears verbatim in the diagnostic so the author sees
9653        // exactly the string the apiserver / `helm lint` would have
9654        // rejected — no re-derivation required to grep the source.
9655        // Peer with `nome_invalid_diagnostic_carries_offending_nome`
9656        // on the bare-`:nome` axis.
9657        let over = "x".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 5);
9658        let c = caixa_with_nome(&over);
9659        let err = c.validate_nome_chart_name_budget().unwrap_err();
9660        let ManifestError::NomeChartNameBudgetExceeded { nome, reason } = err else {
9661            panic!("expected NomeChartNameBudgetExceeded variant");
9662        };
9663        assert_eq!(nome, over);
9664        let expected_chart = crate::lareira_chart_name(&over);
9665        assert!(
9666            reason.contains(&expected_chart),
9667            "diagnostic must carry the rendered chart name {expected_chart:?} verbatim, \
9668             got {reason:?}"
9669        );
9670        assert!(
9671            reason.contains("lareira-"),
9672            "diagnostic must name the canonical chart-name prefix verbatim, got {reason:?}"
9673        );
9674    }
9675
9676    #[test]
9677    fn validate_nome_chart_name_budget_runs_after_nome_shape_via_layout_verify() {
9678        // Order pin on the layout cascade: the narrower
9679        // `NomeInvalid` (bare-DNS-1123 shape) fires before the
9680        // joint-length budget. A structurally-malformed `:nome` (here:
9681        // uppercase) surfaces its specific shape error rather than
9682        // the chart-name-budget error, even when the joint length
9683        // would also overflow — the narrower diagnostic is more
9684        // self-locating. Mirrors the cascade-precedence pins peer
9685        // gates already use (e.g. `EntradaParaEmpty` before
9686        // `EntradaParaInvalid`).
9687        let over = "A".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9688        let c = caixa_with_nome(&over);
9689        // The bare-shape gate fires first.
9690        let err = c.validate_nome().unwrap_err();
9691        assert!(
9692            matches!(err, ManifestError::NomeInvalid { .. }),
9693            "bare-shape gate must fire before chart-name-budget gate; got {err:?}"
9694        );
9695        // And the layout verify cascade surfaces that diagnostic, not
9696        // the budget arm. Inject a path-exists oracle so the cascade
9697        // gets past the manifest-presence check and into the
9698        // value-shape gates.
9699        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9700        let err = crate::LayoutInvariants::verify(
9701            &layout,
9702            &c,
9703            std::path::Path::new("/tmp/caixa-test-fake-root"),
9704        )
9705        .unwrap_err();
9706        let issue = err.to_string();
9707        assert!(
9708            issue.contains("DNS-1123") || issue.contains("uppercase"),
9709            "layout cascade must surface the bare-DNS-1123 diagnostic on a \
9710             structurally-malformed :nome, not the chart-name-budget diagnostic; got {issue:?}"
9711        );
9712    }
9713
9714    #[test]
9715    fn layout_verify_routes_chart_name_budget_through_nome_violation() {
9716        // Cross-axis envelope pin: the layout cascade wraps both
9717        // bare-`:nome` and joint-length-`:nome` failures through the
9718        // same [`LayoutError::NomeViolation`] envelope, since both
9719        // arms are on the `:nome` axis. The user's diagnostic stays
9720        // self-locating ("which axis"), and a future consumer that
9721        // dispatches on the layout-error variant (e.g. a `feira lint`
9722        // exit-code mapping) sees a single per-axis envelope. The
9723        // wrapped `issue:` carries the full inner diagnostic.
9724        let over = "a".repeat(crate::LAREIRA_CHART_NAME_NOME_MAX_LEN + 1);
9725        let c = caixa_with_nome(&over);
9726        // The bare-shape gate accepts.
9727        c.validate_nome().unwrap();
9728        let layout = crate::StandardLayout::new().with_path_exists(|_| true);
9729        let err = crate::LayoutInvariants::verify(
9730            &layout,
9731            &c,
9732            std::path::Path::new("/tmp/caixa-test-fake-root"),
9733        )
9734        .unwrap_err();
9735        let crate::LayoutError::NomeViolation { caixa, issue } = err else {
9736            panic!("expected LayoutError::NomeViolation, got {err:?}");
9737        };
9738        assert_eq!(caixa, over);
9739        assert!(
9740            issue.contains("lareira-") && issue.contains("63") && issue.contains("55"),
9741            "wrapped issue must carry the joint-length diagnostic verbatim, got {issue:?}"
9742        );
9743    }
9744
9745    // ── Caixa::validate_versao — top-level :versao value-shape gate ─────
9746
9747    fn caixa_with_versao(versao: &str) -> Caixa {
9748        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9749        c.versao = versao.to_string();
9750        c
9751    }
9752
9753    #[test]
9754    fn validate_versao_accepts_canonical_template() {
9755        // Positive control: the bare `feira init`-style template's
9756        // `:versao` ("0.1.0") is a canonical SemVer-2 literal; the gate
9757        // must not regress this baseline shape. A future tightening of
9758        // the accepted set surfaces here as a test failure first.
9759        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
9760        c.validate_versao().unwrap();
9761    }
9762
9763    #[test]
9764    fn validate_versao_accepts_canonical_forms() {
9765        // Positive-set sweep: each realistic SemVer-2 shape the
9766        // substrate's downstream consumers accept must pass — bare
9767        // MAJOR.MINOR.PATCH, pre-release tags (`-rc.1`, `-alpha.0`),
9768        // build metadata (`+build.42`), the combined form, and the
9769        // `0.0.0` boundary case. Mirrors `accepts_canonical_forms` on
9770        // the peer `:nome` axis (6c992f8).
9771        for versao in [
9772            "0.1.0",
9773            "0.0.0",
9774            "1.0.0",
9775            "0.2.0-rc.1",
9776            "1.0.0-alpha.0",
9777            "1.0.0+build.42",
9778            "1.0.0-rc.1+build.42",
9779            "10.20.30",
9780        ] {
9781            caixa_with_versao(versao)
9782                .validate_versao()
9783                .unwrap_or_else(|e| {
9784                    panic!("canonical :versao {versao:?} must validate, got {e:?}")
9785                });
9786        }
9787    }
9788
9789    #[test]
9790    fn validate_versao_rejects_empty() {
9791        // Fail-before-pass-after pin: `Caixa::from_lisp` does not refuse
9792        // an empty `:versao` (the derive macro stores the raw String);
9793        // the gate's empty arm names the offending axis with a narrower
9794        // diagnostic than the `VersaoInvalid` parse arm would emit.
9795        // Mirrors `validate_nome_rejects_empty` (6c992f8).
9796        let c = caixa_with_versao("");
9797        let err = c.validate_versao().unwrap_err();
9798        assert_eq!(err, ManifestError::VersaoEmpty);
9799    }
9800
9801    #[test]
9802    fn validate_versao_rejects_git_tag_shape() {
9803        // The canonical "I copied the git tag verbatim" footgun —
9804        // `feira publish` *emits* `v<versao>` git tags, so a leaked
9805        // `v0.1.0` in `:versao` would render as `vv0.1.0` and silently
9806        // shift every downstream consumer's version axis. `semver`
9807        // rejects the leading `v` at parse time; the gate moves the
9808        // diagnostic to the source `caixa.lisp`.
9809        let c = caixa_with_versao("v0.1.0");
9810        let err = c.validate_versao().unwrap_err();
9811        let ManifestError::VersaoInvalid { versao, reason } = err else {
9812            panic!("expected VersaoInvalid for git-tag-shape :versao");
9813        };
9814        assert_eq!(versao, "v0.1.0");
9815        assert!(
9816            !reason.is_empty(),
9817            "VersaoInvalid `reason` must carry the parser's wording, got {reason:?}"
9818        );
9819    }
9820
9821    #[test]
9822    fn validate_versao_rejects_missing_patch() {
9823        // The canonical "I shortened it" footgun — SemVer-2 requires
9824        // three parts. Cargo's `version =` field accepts the shortened
9825        // form as a requirement, conflating the two leaks across the
9826        // typed `:deps :versao` vs top-level `:versao` axes; the gate
9827        // pins the top-level axis to the strict three-part shape.
9828        let c = caixa_with_versao("0.1");
9829        let err = c.validate_versao().unwrap_err();
9830        assert!(
9831            matches!(
9832                err,
9833                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1"
9834            ),
9835            "got {err:?}"
9836        );
9837    }
9838
9839    #[test]
9840    fn validate_versao_rejects_requirement_shape() {
9841        // The canonical "I leaked a requirement into a version" footgun —
9842        // the typed `:deps :versao` / `:membros :versao` axes accept
9843        // `^0.1` (a `VersionReq`); the top-level `:versao` requires a
9844        // concrete `Version`. Without this gate the two typed surfaces
9845        // would silently overlap, and a top-level `^0.1` would surface
9846        // at `helm install` time as a Chart.yaml version rejection far
9847        // from the source `caixa.lisp`.
9848        let c = caixa_with_versao("^0.1");
9849        let err = c.validate_versao().unwrap_err();
9850        assert!(
9851            matches!(
9852                err,
9853                ManifestError::VersaoInvalid { ref versao, .. } if versao == "^0.1"
9854            ),
9855            "got {err:?}"
9856        );
9857    }
9858
9859    #[test]
9860    fn validate_versao_rejects_docker_tag_shape() {
9861        // The "I confused it with a docker tag" footgun — `latest`,
9862        // `main`, `stable` parse as identifiers, not SemVer-2 versions.
9863        // SemVer rejects at parse time; the gate moves the diagnostic
9864        // to the source `caixa.lisp`.
9865        for bad in ["latest", "main", "stable"] {
9866            let c = caixa_with_versao(bad);
9867            let err = c.validate_versao().unwrap_err();
9868            assert!(
9869                matches!(
9870                    err,
9871                    ManifestError::VersaoInvalid { ref versao, .. } if versao == bad
9872                ),
9873                "got {err:?} for {bad:?}"
9874            );
9875        }
9876    }
9877
9878    #[test]
9879    fn validate_versao_rejects_four_part_form() {
9880        // The Java/Microsoft "MAJOR.MINOR.PATCH.BUILD" convention
9881        // SemVer-2 forbids. A leak from a non-SemVer ecosystem; the
9882        // semver crate rejects the extra `.0` at parse time.
9883        let c = caixa_with_versao("0.1.0.0");
9884        let err = c.validate_versao().unwrap_err();
9885        assert!(
9886            matches!(
9887                err,
9888                ManifestError::VersaoInvalid { ref versao, .. } if versao == "0.1.0.0"
9889            ),
9890            "got {err:?}"
9891        );
9892    }
9893
9894    #[test]
9895    fn versao_empty_takes_precedence_over_invalid() {
9896        // Order pin: the empty arm fires before the parser is consulted.
9897        // Empty < invalid in self-locating-ness — the narrower
9898        // `VersaoEmpty` diagnostic doesn't carry a useless `versao: ""`
9899        // reference into the parser-shaped reason. Mirrors
9900        // `nome_empty_takes_precedence_over_invalid` (6c992f8) on the
9901        // peer axis.
9902        let c = caixa_with_versao("");
9903        assert_eq!(c.validate_versao().unwrap_err(), ManifestError::VersaoEmpty);
9904    }
9905
9906    #[test]
9907    fn versao_invalid_diagnostic_carries_offending_versao() {
9908        // Diagnostic-shape pin: the error names the offending `:versao`
9909        // verbatim with a non-empty parser-shaped reason, so a `feira
9910        // lint` run can render the diagnostic without re-parsing.
9911        // Mirrors `nome_invalid_diagnostic_carries_offending_nome`.
9912        let c = caixa_with_versao("v0.1.0");
9913        let err = c.validate_versao().unwrap_err();
9914        let ManifestError::VersaoInvalid { versao, reason } = err else {
9915            panic!("expected VersaoInvalid variant");
9916        };
9917        assert_eq!(versao, "v0.1.0");
9918        assert!(
9919            !reason.is_empty(),
9920            "VersaoInvalid `reason` must carry the parser's wording verbatim"
9921        );
9922    }
9923
9924    #[test]
9925    fn validate_versao_accepts_what_upgrade_from_from_accepts() {
9926        // Parity pin: every shape `UpgradeFromEntry::validate` accepts
9927        // for `:upgrade-from :from` must also pass `validate_versao` —
9928        // the two `:versao`-typed surfaces (top-level `:versao`,
9929        // `:upgrade-from :from`) consume the *same* `semver::Version`
9930        // parser, so they must agree on the accepted set. Without this
9931        // pin, a future tightening of one axis could silently diverge
9932        // from the other. Mirrors the `:versao` requirement-axis
9933        // parity (`:deps`/`:deps-dev`/`:membros`/`:children`) the prior
9934        // commits established.
9935        for versao in ["0.1.0", "0.2.0-rc.1", "1.0.0+build.42"] {
9936            // From the canonical UpgradeFromEntry round-trip fixture
9937            // (`upgrade::tests::round_trip_load_module` peers).
9938            let entry = crate::UpgradeFromEntry {
9939                from: versao.to_string(),
9940                instructions: Vec::new(),
9941            };
9942            entry
9943                .validate()
9944                .unwrap_or_else(|e| panic!(":from {versao:?} must validate, got {e:?}"));
9945            caixa_with_versao(versao)
9946                .validate_versao()
9947                .unwrap_or_else(|e| {
9948                    panic!(":versao {versao:?} must validate, got {e:?} — peer axis diverges")
9949                });
9950        }
9951    }
9952
9953    // ── Caixa::validate_restart_window — supervisor restart-window
9954    //    folds through the shared `supervisor::duration_codec` ────────
9955
9956    fn caixa_with_restart_window(window: Option<&str>) -> Caixa {
9957        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
9958        c.kind = CaixaKind::Supervisor;
9959        c.restart_window = window.map(str::to_string);
9960        c
9961    }
9962
9963    #[test]
9964    fn validate_restart_window_accepts_none() {
9965        // The canonical "omit the slot to express no reset" shape — a
9966        // `None` raw string is the absence of the typed
9967        // `:restart-window` slot, which is exactly the SupervisorSpec
9968        // "never reset" semantics. The gate must be a no-op here; a
9969        // future tightening that rejected `None` would force every
9970        // supervisor caixa to authoring-time pin a window even when
9971        // the OTP semantics call for none.
9972        caixa_with_restart_window(None)
9973            .validate_restart_window()
9974            .unwrap();
9975    }
9976
9977    #[test]
9978    fn validate_restart_window_accepts_canonical_forms() {
9979        // Positive-set sweep across the canonical authoring units the
9980        // shared `supervisor::duration_codec::parse` accepts —
9981        // matches the codec-side `parse_accepts_integer_canonical_units`
9982        // pin in supervisor::tests so a future codec-side tightening
9983        // surfaces simultaneously on both axes.
9984        for window in ["60s", "5m", "1h", "500ms", "30", "0s"] {
9985            caixa_with_restart_window(Some(window))
9986                .validate_restart_window()
9987                .unwrap_or_else(|e| {
9988                    panic!("canonical :restart-window {window:?} must validate, got {e:?}")
9989                });
9990        }
9991    }
9992
9993    #[test]
9994    fn validate_restart_window_rejects_fractional_seconds() {
9995        // Fail-before-pass-after pin: the `"1.5s"` drift class (parses
9996        // as f64 to 1.5 → renders back as `"1500ms"` on first
9997        // serialize). Prior to the fold + this gate, the inline
9998        // `parse_window_inline` accepted f64 magnitudes and silently
9999        // produced a `Duration::from_secs_f64(1.5)`, divergent from
10000        // the shared codec's integer-magnitude discipline on the
10001        // serde-routed siblings. The gate now surfaces a self-locating
10002        // diagnostic at the manifest layer.
10003        let err = caixa_with_restart_window(Some("1.5s"))
10004            .validate_restart_window()
10005            .unwrap_err();
10006        let ManifestError::RestartWindowMalformed {
10007            restart_window,
10008            reason,
10009        } = err
10010        else {
10011            panic!("expected RestartWindowMalformed for fractional seconds");
10012        };
10013        assert_eq!(restart_window, "1.5s");
10014        assert!(
10015            reason.contains("\"1.5\"") && reason.contains("not a non-negative integer"),
10016            "diagnostic must carry shared-codec wording, got {reason:?}"
10017        );
10018    }
10019
10020    #[test]
10021    fn validate_restart_window_rejects_decimal_shaped_integer() {
10022        // The `"1.0s"` class — numerically `1s` exactly, but the
10023        // canonical form is `"1s"` not `"1.0s"`. Decimal-shape leak
10024        // gets the same canonical-form diagnostic.
10025        let err = caixa_with_restart_window(Some("1.0s"))
10026            .validate_restart_window()
10027            .unwrap_err();
10028        assert!(
10029            matches!(
10030                err,
10031                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10032                    if restart_window == "1.0s"
10033            ),
10034            "got {err:?}"
10035        );
10036    }
10037
10038    #[test]
10039    fn validate_restart_window_rejects_half_unit_minute() {
10040        // `"0.5m"` is the unit-fraction footgun — author writes a
10041        // human-readable half-minute, the prior inline parser silently
10042        // produced `Duration::from_secs_f64(30.0)` and serde
10043        // re-emitted as `"30s"`, rewriting author intent. The gate
10044        // closes the loop at the manifest layer.
10045        let err = caixa_with_restart_window(Some("0.5m"))
10046            .validate_restart_window()
10047            .unwrap_err();
10048        let ManifestError::RestartWindowMalformed {
10049            restart_window,
10050            reason,
10051        } = err
10052        else {
10053            panic!("expected RestartWindowMalformed");
10054        };
10055        assert_eq!(restart_window, "0.5m");
10056        assert!(
10057            reason.contains("\"30s\""),
10058            "diagnostic must point at the canonical-form remediation, got {reason:?}"
10059        );
10060    }
10061
10062    #[test]
10063    fn validate_restart_window_rejects_leading_sign() {
10064        // `"+30s"` and `"-30s"` both round-tripped through f64 cleanly
10065        // on the prior parser (`+30` parses as `30.0`; `-30` parsed
10066        // and was caught by the `num < 0.0` arm which silently
10067        // returned `None`, dropping the author-supplied window). The
10068        // shared codec's digit-only gate rejects both with a unified
10069        // canonical-form diagnostic; the manifest-layer wrapper names
10070        // the offending value.
10071        for bad in ["+30s", "-30s"] {
10072            let err = caixa_with_restart_window(Some(bad))
10073                .validate_restart_window()
10074                .unwrap_err();
10075            assert!(
10076                matches!(
10077                    err,
10078                    ManifestError::RestartWindowMalformed { ref restart_window, .. }
10079                        if restart_window == bad
10080                ),
10081                "got {err:?} for {bad:?}"
10082            );
10083        }
10084    }
10085
10086    #[test]
10087    fn validate_restart_window_rejects_unknown_unit() {
10088        // `"30x"` — the typo / wrong-unit footgun. The shared codec's
10089        // unit dispatch surfaces an `unknown duration unit` reason;
10090        // the manifest-layer wrapper names the offending value.
10091        let err = caixa_with_restart_window(Some("30x"))
10092            .validate_restart_window()
10093            .unwrap_err();
10094        let ManifestError::RestartWindowMalformed {
10095            restart_window,
10096            reason,
10097        } = err
10098        else {
10099            panic!("expected RestartWindowMalformed for unknown unit");
10100        };
10101        assert_eq!(restart_window, "30x");
10102        assert!(
10103            reason.contains("unknown duration unit"),
10104            "diagnostic must carry shared-codec unit-rejection wording, got {reason:?}"
10105        );
10106    }
10107
10108    #[test]
10109    fn validate_restart_window_rejects_garbage() {
10110        // Pure non-numeric magnitude (`"abc"`) falls through to the
10111        // shared codec's narrower `"bad duration magnitude"` arm. Same
10112        // diagnostic shape as the codec-side
10113        // `parse_garbage_still_falls_through_to_bad_magnitude` pin.
10114        let err = caixa_with_restart_window(Some("abc"))
10115            .validate_restart_window()
10116            .unwrap_err();
10117        let ManifestError::RestartWindowMalformed {
10118            restart_window,
10119            reason,
10120        } = err
10121        else {
10122            panic!("expected RestartWindowMalformed for garbage");
10123        };
10124        assert_eq!(restart_window, "abc");
10125        assert!(
10126            reason.contains("bad duration magnitude"),
10127            "diagnostic must carry shared-codec garbage-rejection wording, got {reason:?}"
10128        );
10129    }
10130
10131    #[test]
10132    fn validate_restart_window_rejects_empty_string() {
10133        // The empty-after-trim edge case — distinct from the `None`
10134        // canonical "omit the slot" shape. The shared codec's
10135        // digit-only gate refuses an empty magnitude; the manifest
10136        // layer names the offending `""` so the author can grep for
10137        // the literal empty value in their `caixa.lisp` and either
10138        // remove the slot (the canonical "no reset" shape) or pin a
10139        // positive duration.
10140        let err = caixa_with_restart_window(Some(""))
10141            .validate_restart_window()
10142            .unwrap_err();
10143        assert!(
10144            matches!(
10145                err,
10146                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10147                    if restart_window.is_empty()
10148            ),
10149            "got {err:?}"
10150        );
10151    }
10152
10153    #[test]
10154    fn validate_restart_window_diagnostic_carries_offending_value() {
10155        // Diagnostic-shape pin (peer with
10156        // `nome_invalid_diagnostic_carries_offending_nome` /
10157        // `versao_invalid_diagnostic_carries_offending_versao`): the
10158        // error names the offending raw `:restart-window` verbatim
10159        // with a non-empty shared-codec-shaped reason, so a `feira
10160        // lint` run can render the diagnostic without re-parsing.
10161        let err = caixa_with_restart_window(Some("1.5s"))
10162            .validate_restart_window()
10163            .unwrap_err();
10164        let ManifestError::RestartWindowMalformed {
10165            restart_window,
10166            reason,
10167        } = err
10168        else {
10169            panic!("expected RestartWindowMalformed variant");
10170        };
10171        assert_eq!(restart_window, "1.5s");
10172        assert!(
10173            !reason.is_empty(),
10174            "RestartWindowMalformed `reason` must carry the codec's wording verbatim"
10175        );
10176    }
10177
10178    #[test]
10179    fn supervisor_view_folds_through_shared_codec_on_canonical_form() {
10180        // Behavioral parity pin after the fold (`parse_window_inline`
10181        // deletion): the canonical `"60s"` still produces
10182        // `Duration::from_secs(60)` on the typed view — the fold is
10183        // semantically equivalent to the prior inline parser on the
10184        // accepted set. Mirrors the pre-fold `supervisor_view_returns_typed_shape`
10185        // pin, narrowed to the parser-side contract.
10186        let c = caixa_with_restart_window(Some("60s"));
10187        let view = c.supervisor_view().expect("Supervisor kind has a view");
10188        assert_eq!(
10189            view.restart_window,
10190            Some(std::time::Duration::from_secs(60))
10191        );
10192    }
10193
10194    #[test]
10195    fn supervisor_view_soft_swallows_what_validate_rejects() {
10196        // Parity pin between the view-construction path and the
10197        // manifest-level validator: the same `"1.5s"` that surfaces
10198        // `RestartWindowMalformed` at `validate_restart_window` time
10199        // becomes `restart_window: None` on the typed view (the fold
10200        // preserves the existing best-effort shape of `supervisor_view`).
10201        // The contract is: a layout-verifier / `feira lint` flow that
10202        // cares about the malformed-window axis MUST consult
10203        // `validate_restart_window` — relying solely on the view's
10204        // `None` swallows the diagnostic silently. This pin makes the
10205        // expectation a typed invariant.
10206        let c = caixa_with_restart_window(Some("1.5s"));
10207        let view = c.supervisor_view().expect("Supervisor kind has a view");
10208        assert_eq!(
10209            view.restart_window, None,
10210            "view-construction path soft-swallows the parse error to None"
10211        );
10212        // And the manifest-level validator does NOT soft-swallow:
10213        assert!(
10214            matches!(
10215                c.validate_restart_window().unwrap_err(),
10216                ManifestError::RestartWindowMalformed { ref restart_window, .. }
10217                    if restart_window == "1.5s"
10218            ),
10219            "validator must surface the offending value",
10220        );
10221    }
10222
10223    // ── validate_code_paths — per-entry shape on :bibliotecas / :exe / :servicos ──
10224
10225    fn caixa_with_code_paths(bibliotecas: Vec<&str>, exe: Vec<&str>, servicos: Vec<&str>) -> Caixa {
10226        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10227        c.bibliotecas = bibliotecas.into_iter().map(String::from).collect();
10228        c.exe = exe.into_iter().map(String::from).collect();
10229        c.servicos = servicos.into_iter().map(String::from).collect();
10230        c
10231    }
10232
10233    #[test]
10234    fn validate_code_paths_accepts_canonical_template() {
10235        // The bare `Caixa::template` shape is the gate's identity element
10236        // on the canonical authoring shape — `:bibliotecas
10237        // ("lib/demo.lisp")` + empty `:exe` + empty `:servicos`. Pins
10238        // that the gate is non-disruptive against every existing caixa.
10239        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
10240        c.validate_code_paths().unwrap();
10241    }
10242
10243    #[test]
10244    fn validate_code_paths_accepts_explicit_relative_paths_on_every_slot() {
10245        // Positive control sweep: a canonical-shaped path on every slot
10246        // passes. Mirrors the peer
10247        // `behavior::validate_every_slot_relative_is_ok` pin.
10248        let c = caixa_with_code_paths(
10249            vec!["lib/demo.lisp", "lib/helpers.lisp"],
10250            vec!["exe/demo", "exe/tool"],
10251            vec!["servicos/demo.computeunit.yaml"],
10252        );
10253        c.validate_code_paths().unwrap();
10254    }
10255
10256    #[test]
10257    fn validate_code_paths_accepts_all_empty_lists() {
10258        // The empty-list identity element: every Caixa with no declared
10259        // code paths trivially passes (Supervisor / Aplicacao kinds rely
10260        // on this — the OwnCode gate already rejected them before the
10261        // path-shape gate runs in the layout, but the validator itself
10262        // must accept the empty shape).
10263        let c = caixa_with_code_paths(vec![], vec![], vec![]);
10264        c.validate_code_paths().unwrap();
10265    }
10266
10267    #[test]
10268    fn validate_code_paths_rejects_empty_bibliotecas_entry() {
10269        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10270        let err = c.validate_code_paths().unwrap_err();
10271        assert!(
10272            matches!(
10273                err,
10274                ManifestError::CodePathEmpty {
10275                    slot: ":bibliotecas"
10276                }
10277            ),
10278            "got {err:?}",
10279        );
10280    }
10281
10282    #[test]
10283    fn validate_code_paths_rejects_empty_exe_entry() {
10284        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
10285        let err = c.validate_code_paths().unwrap_err();
10286        assert!(
10287            matches!(err, ManifestError::CodePathEmpty { slot: ":exe" }),
10288            "got {err:?}",
10289        );
10290    }
10291
10292    #[test]
10293    fn validate_code_paths_rejects_empty_servicos_entry() {
10294        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
10295        let err = c.validate_code_paths().unwrap_err();
10296        assert!(
10297            matches!(err, ManifestError::CodePathEmpty { slot: ":servicos" }),
10298            "got {err:?}",
10299        );
10300    }
10301
10302    #[test]
10303    fn validate_code_paths_rejects_absolute_bibliotecas_entry() {
10304        // `:bibliotecas` has no `starts_with(<dir>)` fence downstream,
10305        // so an absolute path that resolves on disk silently passes the
10306        // layout's existence check — the canonical sandbox-escape on
10307        // the biblioteca axis.
10308        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10309        let err = c.validate_code_paths().unwrap_err();
10310        let ManifestError::CodePathAbsolute { slot, path } = err else {
10311            panic!("expected CodePathAbsolute, got {err:?}");
10312        };
10313        assert_eq!(slot, ":bibliotecas");
10314        assert_eq!(path, PathBuf::from("/etc/passwd"));
10315    }
10316
10317    #[test]
10318    fn validate_code_paths_rejects_absolute_exe_entry() {
10319        let c = caixa_with_code_paths(vec![], vec!["/usr/bin/env"], vec![]);
10320        let err = c.validate_code_paths().unwrap_err();
10321        let ManifestError::CodePathAbsolute { slot, path } = err else {
10322            panic!("expected CodePathAbsolute, got {err:?}");
10323        };
10324        assert_eq!(slot, ":exe");
10325        assert_eq!(path, PathBuf::from("/usr/bin/env"));
10326    }
10327
10328    #[test]
10329    fn validate_code_paths_rejects_absolute_servicos_entry() {
10330        let c = caixa_with_code_paths(vec![], vec![], vec!["/var/servicos/x.yaml"]);
10331        let err = c.validate_code_paths().unwrap_err();
10332        let ManifestError::CodePathAbsolute { slot, path } = err else {
10333            panic!("expected CodePathAbsolute, got {err:?}");
10334        };
10335        assert_eq!(slot, ":servicos");
10336        assert_eq!(path, PathBuf::from("/var/servicos/x.yaml"));
10337    }
10338
10339    #[test]
10340    fn validate_code_paths_rejects_parent_escape_bibliotecas_leading() {
10341        // Canonical "I want a lib from a sibling caixa" footgun on the
10342        // biblioteca axis. `:bibliotecas` has no `starts_with` fence
10343        // downstream, so a leading `..` traverses to the parent of the
10344        // caixa root with no diagnostic at layout time if the resolved
10345        // target exists.
10346        let c = caixa_with_code_paths(vec!["../sibling/x.lisp"], vec![], vec![]);
10347        let err = c.validate_code_paths().unwrap_err();
10348        let ManifestError::CodePathParentEscape { slot, path } = err else {
10349            panic!("expected CodePathParentEscape, got {err:?}");
10350        };
10351        assert_eq!(slot, ":bibliotecas");
10352        assert_eq!(path, PathBuf::from("../sibling/x.lisp"));
10353    }
10354
10355    #[test]
10356    fn validate_code_paths_rejects_parent_escape_exe_mid_path() {
10357        // Mid-path `..` defeats the layout's component-aware
10358        // `starts_with(exe_dir)` fence — `root.join("exe/../../escape")`
10359        // `starts_with(<root>/exe)` is true, but the canonical resolution
10360        // lives outside the caixa root. Caught regardless of where the
10361        // `..` sits — mirrors the peer
10362        // `behavior::validate_rejects_parent_escape_mid_path` pin.
10363        let c = caixa_with_code_paths(vec![], vec!["exe/../../escape"], vec![]);
10364        let err = c.validate_code_paths().unwrap_err();
10365        let ManifestError::CodePathParentEscape { slot, path } = err else {
10366            panic!("expected CodePathParentEscape, got {err:?}");
10367        };
10368        assert_eq!(slot, ":exe");
10369        assert_eq!(path, PathBuf::from("exe/../../escape"));
10370    }
10371
10372    #[test]
10373    fn validate_code_paths_rejects_parent_escape_servicos_trailing() {
10374        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/foo/../../escape.yaml"]);
10375        let err = c.validate_code_paths().unwrap_err();
10376        let ManifestError::CodePathParentEscape { slot, path } = err else {
10377            panic!("expected CodePathParentEscape, got {err:?}");
10378        };
10379        assert_eq!(slot, ":servicos");
10380        assert_eq!(path, PathBuf::from("servicos/foo/../../escape.yaml"));
10381    }
10382
10383    #[test]
10384    fn validate_code_paths_cross_slot_precedence_bibliotecas_before_exe_before_servicos() {
10385        // Cross-slot precedence pin: `:bibliotecas` → `:exe` →
10386        // `:servicos`. A manifest with malformed entries on all three
10387        // surfaces surfaces the `:bibliotecas` defect first, mirroring
10388        // the canonical declaration order
10389        // `Caixa::declared_foreign_code_slots` already establishes for
10390        // the foreign-code-slot diagnostic.
10391        let c = caixa_with_code_paths(vec![""], vec![""], vec![""]);
10392        let err = c.validate_code_paths().unwrap_err();
10393        assert!(
10394            matches!(
10395                err,
10396                ManifestError::CodePathEmpty {
10397                    slot: ":bibliotecas"
10398                }
10399            ),
10400            "got {err:?}",
10401        );
10402    }
10403
10404    #[test]
10405    fn validate_code_paths_within_slot_precedence_empty_before_absolute_before_parent_escape() {
10406        // Within-slot precedence pin: empty → absolute → parent-escape,
10407        // matching the [`PathShapeViolation`] arm-ordering every peer
10408        // `is_sandboxed_relative_path` caller follows (b0c8389
10409        // BehaviorSpec, 26da2c7 UpgradeInstruction::StateChange). A
10410        // `:bibliotecas` list whose first entry is empty *and* whose
10411        // later entries are absolute/parent-escape surfaces the empty
10412        // arm first, on the lexicographically-earliest offending entry.
10413        let c = caixa_with_code_paths(vec!["", "/etc/passwd", "../escape.lisp"], vec![], vec![]);
10414        let err = c.validate_code_paths().unwrap_err();
10415        assert!(
10416            matches!(
10417                err,
10418                ManifestError::CodePathEmpty {
10419                    slot: ":bibliotecas"
10420                }
10421            ),
10422            "got {err:?}",
10423        );
10424    }
10425
10426    #[test]
10427    fn validate_code_paths_first_offender_per_slot_wins() {
10428        // Within a single slot, the first declaration-order offender
10429        // surfaces — pins that the gate is left-to-right deterministic
10430        // (peer of every `*_first_collision_*` pin on duplicate gates).
10431        let c = caixa_with_code_paths(
10432            vec!["lib/ok.lisp", "/etc/escape", "../also-escape"],
10433            vec![],
10434            vec![],
10435        );
10436        let err = c.validate_code_paths().unwrap_err();
10437        let ManifestError::CodePathAbsolute { slot, path } = err else {
10438            panic!("expected CodePathAbsolute, got {err:?}");
10439        };
10440        assert_eq!(slot, ":bibliotecas");
10441        assert_eq!(path, PathBuf::from("/etc/escape"));
10442    }
10443
10444    #[test]
10445    fn validate_code_paths_diagnostic_carries_offending_slot_and_path() {
10446        // Diagnostic-shape pin (peer with
10447        // `nome_invalid_diagnostic_carries_offending_nome` /
10448        // `versao_invalid_diagnostic_carries_offending_versao`): the
10449        // error's Display surfaces both the offending `:slot` tag and
10450        // the offending path verbatim, so a `feira lint` run can render
10451        // the diagnostic without re-parsing.
10452        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10453        let rendered = c.validate_code_paths().unwrap_err().to_string();
10454        assert!(
10455            rendered.contains(":bibliotecas"),
10456            "diagnostic must name the offending slot: {rendered}",
10457        );
10458        assert!(
10459            rendered.contains("/etc/passwd"),
10460            "diagnostic must quote the offending path: {rendered}",
10461        );
10462    }
10463
10464    #[test]
10465    fn validate_code_paths_rejects_duplicate_bibliotecas_entry() {
10466        // Canonical copy-paste-the-wrong-file footgun on the biblioteca
10467        // axis. Without the gate `feira build` re-parses the same lib
10468        // twice, wasting work and silently masking the author's intent
10469        // to declare a *second* biblioteca.
10470        let c = caixa_with_code_paths(vec!["lib/demo.lisp", "lib/demo.lisp"], vec![], vec![]);
10471        let err = c.validate_code_paths().unwrap_err();
10472        let ManifestError::CodePathDuplicate { slot, path } = err else {
10473            panic!("expected CodePathDuplicate, got {err:?}");
10474        };
10475        assert_eq!(slot, ":bibliotecas");
10476        assert_eq!(path, PathBuf::from("lib/demo.lisp"));
10477    }
10478
10479    #[test]
10480    fn validate_code_paths_rejects_duplicate_exe_entry() {
10481        // Same footgun on the Binario surface. The future `caixa-flake`
10482        // emitter that materializes each `:exe` entry as a flake
10483        // `packages.<name>` derivation would collide on the duplicate
10484        // package key — surfaced here at the typed-validate layer with a
10485        // self-locating diagnostic instead.
10486        let c = caixa_with_code_paths(vec![], vec!["exe/cli", "exe/cli"], vec![]);
10487        let err = c.validate_code_paths().unwrap_err();
10488        let ManifestError::CodePathDuplicate { slot, path } = err else {
10489            panic!("expected CodePathDuplicate, got {err:?}");
10490        };
10491        assert_eq!(slot, ":exe");
10492        assert_eq!(path, PathBuf::from("exe/cli"));
10493    }
10494
10495    #[test]
10496    fn validate_code_paths_rejects_duplicate_servicos_entry() {
10497        // Same footgun on the Servico surface. The peer caixa-helm /
10498        // caixa-flux renderers refuse `:servicos.len() != 1` with the
10499        // narrower `UnsupportedServicoCount` diagnostic, but that
10500        // diagnostic surfaces "too many servicos" without naming
10501        // "duplicate entry" — the typed self-locating framing only lands
10502        // at this gate.
10503        let c = caixa_with_code_paths(
10504            vec![],
10505            vec![],
10506            vec![
10507                "servicos/demo.computeunit.yaml",
10508                "servicos/demo.computeunit.yaml",
10509            ],
10510        );
10511        let err = c.validate_code_paths().unwrap_err();
10512        let ManifestError::CodePathDuplicate { slot, path } = err else {
10513            panic!("expected CodePathDuplicate, got {err:?}");
10514        };
10515        assert_eq!(slot, ":servicos");
10516        assert_eq!(path, PathBuf::from("servicos/demo.computeunit.yaml"));
10517    }
10518
10519    #[test]
10520    fn validate_code_paths_accepts_same_path_across_slots() {
10521        // Per-list scope pin: a `:bibliotecas` entry that happens to
10522        // collide with an `:exe` or `:servicos` entry as a *string* is
10523        // not a duplicate by this gate (each list gets its own HashSet),
10524        // mirroring the peer `:deps` ↔ `:deps-dev` per-list scope
10525        // (a `:nome` present in both lists is a legitimate dev-vs-runtime
10526        // shape on the dep axis). The structural `starts_with(<exe |
10527        // servicos>_dir)` fence at layout time prevents the realistic
10528        // cross-slot collision case from existing on disk, but the gate's
10529        // per-list scope is correct independent of that downstream fence.
10530        let c = caixa_with_code_paths(
10531            vec!["lib/x.lisp"],
10532            vec!["exe/x"],
10533            vec!["servicos/x.computeunit.yaml"],
10534        );
10535        c.validate_code_paths().unwrap();
10536    }
10537
10538    #[test]
10539    fn validate_code_paths_duplicate_fires_after_structural_checks_on_same_slot() {
10540        // Within-slot ordering pin: structural defects (empty / absolute
10541        // / parent-escape) fire before the duplicate gate on the same
10542        // slot. A `:bibliotecas ("" "lib/x.lisp" "lib/x.lisp")` shape
10543        // surfaces the narrower `CodePathEmpty` for the empty entry
10544        // first, not the duplicate on the later pair — same arm-ordering
10545        // every peer per-list duplicate gate uses (`:etiquetas` 360a499,
10546        // `:autores` 86c769b, `:deps` 359fba5).
10547        let c = caixa_with_code_paths(vec!["", "lib/x.lisp", "lib/x.lisp"], vec![], vec![]);
10548        let err = c.validate_code_paths().unwrap_err();
10549        assert!(
10550            matches!(
10551                err,
10552                ManifestError::CodePathEmpty {
10553                    slot: ":bibliotecas"
10554                }
10555            ),
10556            "got {err:?}",
10557        );
10558    }
10559
10560    #[test]
10561    fn validate_code_paths_duplicate_in_bibliotecas_fires_before_duplicate_in_exe() {
10562        // Cross-slot ordering pin on the duplicate arm: `:bibliotecas`
10563        // duplicates surface before `:exe` duplicates, matching the
10564        // canonical `:bibliotecas` → `:exe` → `:servicos` declaration
10565        // order every peer per-slot diagnostic on this surface follows.
10566        let c = caixa_with_code_paths(
10567            vec!["lib/x.lisp", "lib/x.lisp"],
10568            vec!["exe/y", "exe/y"],
10569            vec![],
10570        );
10571        let err = c.validate_code_paths().unwrap_err();
10572        let ManifestError::CodePathDuplicate { slot, path } = err else {
10573            panic!("expected CodePathDuplicate, got {err:?}");
10574        };
10575        assert_eq!(slot, ":bibliotecas");
10576        assert_eq!(path, PathBuf::from("lib/x.lisp"));
10577    }
10578
10579    #[test]
10580    fn validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path() {
10581        // Diagnostic-shape pin (peer with
10582        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10583        // on the structural arm): the duplicate-arm Display surfaces both
10584        // the offending `:slot` tag and the offending path verbatim, so a
10585        // `feira lint` run can render the diagnostic without re-parsing.
10586        let c = caixa_with_code_paths(
10587            vec![],
10588            vec![],
10589            vec![
10590                "servicos/demo.computeunit.yaml",
10591                "servicos/demo.computeunit.yaml",
10592            ],
10593        );
10594        let rendered = c.validate_code_paths().unwrap_err().to_string();
10595        assert!(
10596            rendered.contains(":servicos"),
10597            "diagnostic must name the offending slot: {rendered}",
10598        );
10599        assert!(
10600            rendered.contains("servicos/demo.computeunit.yaml"),
10601            "diagnostic must quote the offending path: {rendered}",
10602        );
10603    }
10604
10605    // ── validate_code_paths — `.lisp` extension gate on :bibliotecas ──
10606    //
10607    // The lifted [`crate::render::is_lisp_extension`] predicate (33cc830)
10608    // now gates `:bibliotecas` entries on the tatara-lisp-source file-type
10609    // contract. The `feira build` loop (`caixa-feira/src/cmd/build.rs:33`)
10610    // reads every declared `:bibliotecas` entry through `tatara_lisp::read`
10611    // at parse time — the same downstream consumer the peer `:behavior
10612    // :on-*` (c97815a, [`crate::BehaviorError::NonLispExtension`]) and
10613    // `:upgrade-from :state-change :script` (33cc830,
10614    // [`crate::UpgradeError::NonLispExtensionScript`]) axes route through.
10615    // `:exe` and `:servicos` are deliberately excluded — `:exe` is the
10616    // nix-built executable surface (`"exe/<name>"` shape per the canonical
10617    // [`crate::LayoutError::ExeOutsideDir`] error message and every
10618    // in-tree `caixa_with_code_paths` positive control), and `:servicos`
10619    // is the `.computeunit.yaml` ComputeUnit-CR axis.
10620
10621    #[test]
10622    fn validate_code_paths_rejects_no_extension_bibliotecas_entry() {
10623        // Canonical "I dragged the wrong file from the workspace tree"
10624        // footgun on the biblioteca axis. Without the gate `feira build`
10625        // hands the extensionless path to `tatara_lisp::read` and fails
10626        // with a parser-shaped diagnostic far from the source caixa.lisp,
10627        // with no field naming the offending `:bibliotecas` entry.
10628        for relpath in ["lib/demo", "demo", "lib/handlers/inner"] {
10629            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10630            let err = c.validate_code_paths().unwrap_err();
10631            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10632                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10633            };
10634            assert_eq!(slot, ":bibliotecas");
10635            assert_eq!(path, PathBuf::from(relpath));
10636        }
10637    }
10638
10639    #[test]
10640    fn validate_code_paths_rejects_wrong_extension_bibliotecas_entry() {
10641        // Wrong-extension sweep across common authoring footguns. Same
10642        // sweep posture as the peer
10643        // `behavior::validate_rejects_wrong_extension` (c97815a) and
10644        // `upgrade::tests::state_change_rejects_wrong_extension_script`
10645        // (33cc830) cases.
10646        for relpath in [
10647            "lib/demo.rs",
10648            "lib/demo.txt",
10649            "lib/demo.md",
10650            "lib/demo.json",
10651            "lib/demo.yaml",
10652            "lib/demo.toml",
10653            "lib/demo.lisp.bak",
10654            "lib/demo.lispx",
10655            "lib/demo.lis",
10656        ] {
10657            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10658            let err = c.validate_code_paths().unwrap_err();
10659            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10660                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10661            };
10662            assert_eq!(slot, ":bibliotecas");
10663            assert_eq!(path, PathBuf::from(relpath));
10664        }
10665    }
10666
10667    #[test]
10668    fn validate_code_paths_rejects_case_folded_extension_bibliotecas_entry() {
10669        // Case-sensitivity sweep — pins the strict lowercase `.lisp`
10670        // contract. An uppercase `.LISP` shape that the layout's existence
10671        // check would (case-insensitively, on case-insensitive volumes)
10672        // match the on-disk file still mismatches the canonical form the
10673        // codec emits, breaking the THEORY.md §V.2.7 render-determinism
10674        // contract. Mirrors the peer
10675        // `behavior::validate_rejects_case_folded_extension` (c97815a) and
10676        // `upgrade::tests::state_change_rejects_case_folded_extension_script`
10677        // (33cc830) sweeps.
10678        for relpath in [
10679            "lib/demo.LISP",
10680            "lib/demo.Lisp",
10681            "lib/demo.LiSp",
10682            "lib/demo.lISP",
10683        ] {
10684            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10685            let err = c.validate_code_paths().unwrap_err();
10686            let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10687                panic!("expected CodePathNonLispExtension for {relpath:?}, got {err:?}");
10688            };
10689            assert_eq!(slot, ":bibliotecas");
10690            assert_eq!(path, PathBuf::from(relpath));
10691        }
10692    }
10693
10694    #[test]
10695    fn validate_code_paths_accepts_canonical_lisp_shapes() {
10696        // Positive-control sweep through every canonical authoring shape
10697        // every in-tree fixture and the `Caixa::template` scaffold use.
10698        // Mirrors the peer `behavior::validate_accepts_canonical_lisp_paths`
10699        // (c97815a) and the lifted predicate's own
10700        // `is_lisp_extension_accepts_canonical_shapes` sweep in render.rs
10701        // (33cc830).
10702        for relpath in [
10703            "lib/demo.lisp",
10704            "lib/handlers.lisp",
10705            "lib/migrations/v01-to-v02.lisp",
10706            "demo.lisp",
10707            "a.lisp",
10708            "./lib/demo.lisp",
10709            "lib/./handlers.lisp",
10710            "lib/migrations/v.0.1.lisp",
10711        ] {
10712            let c = caixa_with_code_paths(vec![relpath], vec![], vec![]);
10713            c.validate_code_paths()
10714                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10715        }
10716    }
10717
10718    #[test]
10719    fn validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos() {
10720        // The file-type gate is per-slot — only `:bibliotecas` carries the
10721        // tatara-lisp-source contract. An extensionless `:exe` entry
10722        // (`exe/demo`) and a `.computeunit.yaml` `:servicos` entry are the
10723        // canonical shapes every in-tree fixture uses, and must continue
10724        // to pass validate. Pins that a future tightening that broadens
10725        // the `.lisp` gate to either axis surfaces as a test failure
10726        // rather than as a silent breaking change to existing valid
10727        // manifests.
10728        let c = caixa_with_code_paths(
10729            vec![],
10730            vec!["exe/demo", "exe/tool"],
10731            vec!["servicos/demo.computeunit.yaml"],
10732        );
10733        c.validate_code_paths().unwrap();
10734    }
10735
10736    #[test]
10737    fn validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension() {
10738        // Cross-arm precedence pin: a `:bibliotecas` entry that is *both*
10739        // sandbox-escaping and non-`.lisp` surfaces the more fundamental
10740        // sandbox-shape diagnostic first (the `.lisp` remediation would
10741        // be misleading when the offending path can never resolve under
10742        // the caixa root anyway). Mirrors the peer
10743        // `EmptyPath` → `AbsolutePath` → `ParentEscape` → `NonLispExtension`
10744        // ordering on `:behavior :on-*` (c97815a) and `EmptyScript` →
10745        // `AbsoluteScript` → `ParentEscapeScript` → `NonLispExtensionScript`
10746        // on `:upgrade-from :state-change :script` (33cc830).
10747        //
10748        // Empty wins (the strictly-smaller-scope structural arm).
10749        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
10750        assert!(
10751            matches!(
10752                c.validate_code_paths().unwrap_err(),
10753                ManifestError::CodePathEmpty {
10754                    slot: ":bibliotecas"
10755                }
10756            ),
10757            "empty must win over non-lisp-extension",
10758        );
10759        // Absolute wins (the path can't resolve under the caixa root).
10760        let c = caixa_with_code_paths(vec!["/etc/passwd"], vec![], vec![]);
10761        let err = c.validate_code_paths().unwrap_err();
10762        let ManifestError::CodePathAbsolute { slot, .. } = err else {
10763            panic!("absolute must win over non-lisp-extension, got {err:?}");
10764        };
10765        assert_eq!(slot, ":bibliotecas");
10766        // ParentEscape wins (the path escapes the caixa root).
10767        let c = caixa_with_code_paths(vec!["../sibling/x.txt"], vec![], vec![]);
10768        let err = c.validate_code_paths().unwrap_err();
10769        let ManifestError::CodePathParentEscape { slot, .. } = err else {
10770            panic!("parent-escape must win over non-lisp-extension, got {err:?}");
10771        };
10772        assert_eq!(slot, ":bibliotecas");
10773    }
10774
10775    #[test]
10776    fn validate_code_paths_non_lisp_extension_precedes_duplicate() {
10777        // Within-slot precedence pin: the per-entry file-type shape gate
10778        // fires before the cross-entry duplicate gate, so the narrower
10779        // structural defect dominates the uniqueness diagnostic. A
10780        // `("lib/x.txt" "lib/x.txt")` shape surfaces
10781        // `CodePathNonLispExtension` on the first entry rather than
10782        // `CodePathDuplicate` on the pair — same posture every per-entry
10783        // shape-gate-precedes-duplicate cascade follows on this surface
10784        // (the empty / absolute / parent-escape arms already precede the
10785        // duplicate arm; the lifted file-type arm joins that set).
10786        let c = caixa_with_code_paths(vec!["lib/x.txt", "lib/x.txt"], vec![], vec![]);
10787        let err = c.validate_code_paths().unwrap_err();
10788        let ManifestError::CodePathNonLispExtension { slot, path } = err else {
10789            panic!("expected CodePathNonLispExtension, got {err:?}");
10790        };
10791        assert_eq!(slot, ":bibliotecas");
10792        assert_eq!(path, PathBuf::from("lib/x.txt"));
10793    }
10794
10795    #[test]
10796    fn validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path() {
10797        // Diagnostic-shape pin (peer with
10798        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`
10799        // on the sandbox-shape arms and
10800        // `validate_code_paths_duplicate_diagnostic_carries_offending_slot_and_path`
10801        // on the duplicate arm): the file-type-arm Display surfaces both
10802        // the offending `:slot` tag, the offending path verbatim, and the
10803        // expected `.lisp` extension named in the remediation text, so a
10804        // `feira lint` run can render the diagnostic without re-parsing.
10805        let c = caixa_with_code_paths(vec!["lib/demo.rs"], vec![], vec![]);
10806        let rendered = c.validate_code_paths().unwrap_err().to_string();
10807        assert!(
10808            rendered.contains(":bibliotecas"),
10809            "diagnostic must name the offending slot: {rendered}",
10810        );
10811        assert!(
10812            rendered.contains("lib/demo.rs"),
10813            "diagnostic must quote the offending path: {rendered}",
10814        );
10815        assert!(
10816            rendered.contains(".lisp"),
10817            "diagnostic must name the expected extension: {rendered}",
10818        );
10819    }
10820
10821    // ── validate_code_paths — `.computeunit.yaml` compound-suffix gate on :servicos ──
10822    //
10823    // The lifted [`crate::render::is_computeunit_yaml_extension`] predicate
10824    // now gates `:servicos` entries on the ComputeUnit-CR YAML file-type
10825    // contract. The peer caixa-helm / caixa-flux renderers consume each
10826    // `:servicos` entry through `serde_yaml::from_str` as a typed
10827    // `ComputeUnit` CR — same downstream-consumer-shape lift as the peer
10828    // `:bibliotecas` `.lisp` gate (64772a9), here on the compound-suffix
10829    // axis `Path::extension` can't express on its own.
10830
10831    #[test]
10832    fn validate_code_paths_rejects_no_extension_servicos_entry() {
10833        // Canonical "I dragged the wrong file from the workspace tree"
10834        // footgun on the Servico axis. Without the gate the peer
10835        // caixa-helm / caixa-flux renderers hand the extensionless path
10836        // to `serde_yaml::from_str` and fail with a parser-shaped
10837        // diagnostic far from the source caixa.lisp, with no field
10838        // naming the offending `:servicos` entry.
10839        for relpath in ["servicos/demo", "demo", "servicos/sub/nested"] {
10840            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10841            let err = c.validate_code_paths().unwrap_err();
10842            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10843                panic!(
10844                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10845                     got {err:?}"
10846                );
10847            };
10848            assert_eq!(slot, ":servicos");
10849            assert_eq!(path, PathBuf::from(relpath));
10850        }
10851    }
10852
10853    #[test]
10854    fn validate_code_paths_rejects_wrong_extension_servicos_entry() {
10855        // Wrong-extension sweep across common authoring footguns on the
10856        // Servico axis. Bare `.yaml` is the canonical "I forgot the
10857        // `.computeunit` segment" typo; the off-by-one-segment shapes
10858        // (`.computeunit-yaml` / `.computeunit_yaml`) silently pass the
10859        // bare `Path::extension` view but mismatch the typed compound
10860        // suffix the renderers' `serde_yaml::from_str` consumer demands.
10861        // Same sweep-posture as the peer
10862        // `validate_code_paths_rejects_wrong_extension_bibliotecas_entry`
10863        // (64772a9) on the sibling tatara-lisp-source axis.
10864        for relpath in [
10865            "servicos/demo.yaml",
10866            "servicos/demo.yml",
10867            "servicos/demo.json",
10868            "servicos/demo.toml",
10869            "servicos/demo.txt",
10870            "servicos/demo.computeunit.yaml.bak",
10871            "servicos/demo.computeunit.yam",
10872            "servicos/demo.computeunit",
10873            "servicos/demo-computeunit.yaml",
10874            "servicos/demo_computeunit.yaml",
10875        ] {
10876            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10877            let err = c.validate_code_paths().unwrap_err();
10878            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10879                panic!(
10880                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10881                     got {err:?}"
10882                );
10883            };
10884            assert_eq!(slot, ":servicos");
10885            assert_eq!(path, PathBuf::from(relpath));
10886        }
10887    }
10888
10889    #[test]
10890    fn validate_code_paths_rejects_case_folded_extension_servicos_entry() {
10891        // Case-sensitivity sweep — pins the strict lowercase
10892        // `.computeunit.yaml` contract. A case-folded shape that the
10893        // layout's existence check would (case-insensitively, on
10894        // case-insensitive volumes) match the on-disk file still
10895        // mismatches the canonical form the codec emits, breaking the
10896        // THEORY.md §V.2.7 render-determinism contract. Mirrors the peer
10897        // `validate_code_paths_rejects_case_folded_extension_bibliotecas_entry`
10898        // (64772a9) sweep on the sibling tatara-lisp-source axis.
10899        for relpath in [
10900            "servicos/demo.ComputeUnit.yaml",
10901            "servicos/demo.COMPUTEUNIT.yaml",
10902            "servicos/demo.computeunit.YAML",
10903            "servicos/demo.computeunit.Yaml",
10904            "servicos/demo.COMPUTEUNIT.YAML",
10905        ] {
10906            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10907            let err = c.validate_code_paths().unwrap_err();
10908            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10909                panic!(
10910                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10911                     got {err:?}"
10912                );
10913            };
10914            assert_eq!(slot, ":servicos");
10915            assert_eq!(path, PathBuf::from(relpath));
10916        }
10917    }
10918
10919    #[test]
10920    fn validate_code_paths_rejects_empty_stem_servicos_entry() {
10921        // Degenerate hidden-file shape: a file name exactly equal to the
10922        // suffix (`.computeunit.yaml` — no stem preceding the suffix) is
10923        // the structural "Servico declared with no identity" footgun.
10924        // The substrate identifies each ComputeUnit by the file-stem
10925        // segment that precedes `.computeunit.yaml` (the rendered
10926        // `lareira-<stem>` Helm chart, the per-Servico `metadata.name`,
10927        // the M3 `:contratos` membership lookup), so an empty stem
10928        // leaves the Servico unidentifiable. Pinned at the typed-axis
10929        // level so a future regression that drops the `name.len() >
10930        // SUFFIX.len()` bound at the predicate surfaces here, not
10931        // piecemeal as a `lareira-` chart-name collision at render time.
10932        for relpath in ["servicos/.computeunit.yaml"] {
10933            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10934            let err = c.validate_code_paths().unwrap_err();
10935            let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
10936                panic!(
10937                    "expected CodePathNonComputeUnitYamlExtension for {relpath:?}, \
10938                     got {err:?}"
10939                );
10940            };
10941            assert_eq!(slot, ":servicos");
10942            assert_eq!(path, PathBuf::from(relpath));
10943        }
10944    }
10945
10946    #[test]
10947    fn validate_code_paths_accepts_canonical_computeunit_yaml_shapes() {
10948        // Positive-control sweep through every canonical authoring shape
10949        // every in-tree fixture and the `Caixa::template` scaffold use.
10950        // Mirrors the peer
10951        // `validate_code_paths_accepts_canonical_lisp_shapes` (64772a9)
10952        // and the lifted predicate's own
10953        // `computeunit_yaml_extension_accepts_canonical_shapes` sweep in
10954        // render.rs.
10955        for relpath in [
10956            "servicos/demo.computeunit.yaml",
10957            "servicos/hello-rio.computeunit.yaml",
10958            "servicos/my-service.computeunit.yaml",
10959            "servicos/a.computeunit.yaml",
10960            "./servicos/demo.computeunit.yaml",
10961            "servicos/./demo.computeunit.yaml",
10962            "servicos/sub/nested.computeunit.yaml",
10963            "servicos/v0.1.computeunit.yaml",
10964        ] {
10965            let c = caixa_with_code_paths(vec![], vec![], vec![relpath]);
10966            c.validate_code_paths()
10967                .unwrap_or_else(|e| panic!("canonical shape {relpath:?} must pass, got {e:?}"));
10968        }
10969    }
10970
10971    #[test]
10972    fn validate_code_paths_non_computeunit_yaml_extension_does_not_fire_on_bibliotecas_or_exe() {
10973        // The file-type gate is per-slot — only `:servicos` carries the
10974        // ComputeUnit-CR YAML contract. A canonical `.lisp` `:bibliotecas`
10975        // entry and an extensionless `:exe` entry are the canonical
10976        // shapes every in-tree fixture uses, and must continue to pass
10977        // validate. Peer of
10978        // `validate_code_paths_non_lisp_extension_does_not_fire_on_exe_or_servicos`
10979        // (64772a9) — together pin that the typed
10980        // [`CodePathFileType`] dispatch is exhaustively per-slot, with no
10981        // cross-axis leakage in either direction.
10982        let c = caixa_with_code_paths(
10983            vec!["lib/demo.lisp"],
10984            vec!["exe/demo", "exe/tool"],
10985            vec!["servicos/demo.computeunit.yaml"],
10986        );
10987        c.validate_code_paths().unwrap();
10988    }
10989
10990    #[test]
10991    fn validate_code_paths_sandbox_shape_arms_precede_non_computeunit_yaml_extension() {
10992        // Cross-arm precedence pin: a `:servicos` entry that is *both*
10993        // sandbox-escaping and wrong-extension surfaces the more
10994        // fundamental sandbox-shape diagnostic first (the
10995        // `.computeunit.yaml` remediation would be misleading when the
10996        // offending path can never resolve under the caixa root
10997        // anyway). Mirrors the peer
10998        // `validate_code_paths_sandbox_shape_arms_precede_non_lisp_extension`
10999        // (64772a9) ordering on the sibling `:bibliotecas` axis and the
11000        // peer `EmptyPath` → `AbsolutePath` → `ParentEscape` →
11001        // `NonComputeUnitYamlExtension` arm-ordering the dispatch
11002        // table establishes.
11003        //
11004        // Empty wins (the strictly-smaller-scope structural arm).
11005        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
11006        assert!(
11007            matches!(
11008                c.validate_code_paths().unwrap_err(),
11009                ManifestError::CodePathEmpty { slot: ":servicos" }
11010            ),
11011            "empty must win over non-computeunit-yaml-extension",
11012        );
11013        // Absolute wins (the path can't resolve under the caixa root).
11014        let c = caixa_with_code_paths(vec![], vec![], vec!["/etc/foo.yaml"]);
11015        let err = c.validate_code_paths().unwrap_err();
11016        let ManifestError::CodePathAbsolute { slot, .. } = err else {
11017            panic!("absolute must win over non-computeunit-yaml-extension, got {err:?}");
11018        };
11019        assert_eq!(slot, ":servicos");
11020        // ParentEscape wins (the path escapes the caixa root).
11021        let c = caixa_with_code_paths(vec![], vec![], vec!["../sibling/x.yaml"]);
11022        let err = c.validate_code_paths().unwrap_err();
11023        let ManifestError::CodePathParentEscape { slot, .. } = err else {
11024            panic!("parent-escape must win over non-computeunit-yaml-extension, got {err:?}");
11025        };
11026        assert_eq!(slot, ":servicos");
11027    }
11028
11029    #[test]
11030    fn validate_code_paths_non_computeunit_yaml_extension_precedes_duplicate() {
11031        // Within-slot precedence pin: the per-entry file-type shape gate
11032        // fires before the cross-entry duplicate gate, so the narrower
11033        // structural defect dominates the uniqueness diagnostic. A
11034        // `("servicos/x.yaml" "servicos/x.yaml")` shape surfaces
11035        // `CodePathNonComputeUnitYamlExtension` on the first entry
11036        // rather than `CodePathDuplicate` on the pair — same posture
11037        // every per-entry shape-gate-precedes-duplicate cascade follows
11038        // on this surface, peer of the 64772a9 `:bibliotecas`
11039        // `("lib/x.txt" "lib/x.txt")` ordering.
11040        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/x.yaml", "servicos/x.yaml"]);
11041        let err = c.validate_code_paths().unwrap_err();
11042        let ManifestError::CodePathNonComputeUnitYamlExtension { slot, path } = err else {
11043            panic!("expected CodePathNonComputeUnitYamlExtension, got {err:?}");
11044        };
11045        assert_eq!(slot, ":servicos");
11046        assert_eq!(path, PathBuf::from("servicos/x.yaml"));
11047    }
11048
11049    #[test]
11050    fn validate_code_paths_non_computeunit_yaml_extension_diagnostic_carries_offending_slot_and_path()
11051     {
11052        // Diagnostic-shape pin (peer with
11053        // `validate_code_paths_non_lisp_extension_diagnostic_carries_offending_slot_and_path`
11054        // on the sibling tatara-lisp-source axis): the file-type-arm
11055        // Display surfaces both the offending `:slot` tag, the
11056        // offending path verbatim, and the expected
11057        // `.computeunit.yaml` compound suffix named in the remediation
11058        // text, so a `feira lint` run can render the diagnostic without
11059        // re-parsing.
11060        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.yaml"]);
11061        let rendered = c.validate_code_paths().unwrap_err().to_string();
11062        assert!(
11063            rendered.contains(":servicos"),
11064            "diagnostic must name the offending slot: {rendered}",
11065        );
11066        assert!(
11067            rendered.contains("servicos/demo.yaml"),
11068            "diagnostic must quote the offending path: {rendered}",
11069        );
11070        assert!(
11071            rendered.contains(".computeunit.yaml"),
11072            "diagnostic must name the expected compound suffix: {rendered}",
11073        );
11074    }
11075
11076    // ── validate_etiquetas — universal-axis registry-search-tag shape ──
11077
11078    fn caixa_with_etiquetas(etiquetas: Vec<&str>) -> Caixa {
11079        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11080        c.etiquetas = etiquetas.into_iter().map(String::from).collect();
11081        c
11082    }
11083
11084    #[test]
11085    fn validate_etiquetas_accepts_empty_list() {
11086        // The empty-list identity: every caixa with no declared tags
11087        // trivially passes — `Caixa::template` emits `:etiquetas ()`,
11088        // so the gate is non-disruptive against every existing manifest.
11089        let c = caixa_with_etiquetas(vec![]);
11090        c.validate_etiquetas().unwrap();
11091    }
11092
11093    #[test]
11094    fn validate_etiquetas_accepts_canonical_forms() {
11095        // Positive control sweep: a canonical-shaped non-empty distinct
11096        // tag list passes, mirroring the example checkout-aplicacao
11097        // (`:etiquetas ("example" "aplicacao" "mesh" "ecommerce" "demo")`)
11098        // and the hello-rio fixture (`("hello-world" "wasm" "rust")`).
11099        let c = caixa_with_etiquetas(vec!["example", "aplicacao", "mesh", "ecommerce", "demo"]);
11100        c.validate_etiquetas().unwrap();
11101    }
11102
11103    #[test]
11104    fn validate_etiquetas_rejects_empty_entry() {
11105        // Canonical paste-from-blank-doc footgun. Without the gate the
11106        // empty entry rendered as `keywords: [""]` in `Chart.yaml`, a
11107        // no-op tag indexing nothing in the future caixa-registry.
11108        let c = caixa_with_etiquetas(vec![""]);
11109        let err = c.validate_etiquetas().unwrap_err();
11110        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11111    }
11112
11113    #[test]
11114    fn validate_etiquetas_rejects_duplicate_entry() {
11115        // Canonical copy-paste-the-wrong-tag footgun. Without the gate
11116        // the duplicate was silently dedup'd by caixa-helm's BTreeSet
11117        // collect at chart render — a "second wins / one silently
11118        // disappears" shape divergent from every peer typed-graph set
11119        // gate. The duplicate-arm names the offending tag verbatim.
11120        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
11121        let err = c.validate_etiquetas().unwrap_err();
11122        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
11123            panic!("expected EtiquetaDuplicate, got {err:?}");
11124        };
11125        assert_eq!(etiqueta, "demo");
11126    }
11127
11128    #[test]
11129    fn validate_etiquetas_empty_takes_precedence_over_duplicate() {
11130        // Empty-first cascade pin: `("" "demo" "demo")` surfaces
11131        // `EtiquetaEmpty` not `EtiquetaDuplicate` — the narrower
11132        // structural "this entry has no value" defect dominates the
11133        // cross-entry uniqueness diagnostic. Mirrors the peer
11134        // empty-before-duplicate cascades on `:caracteristicas`
11135        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11136        // fc3b4d5) and `:membros :caixa` (`MembroCaixaEmpty` before
11137        // `MembroDuplicate`).
11138        let c = caixa_with_etiquetas(vec!["", "demo", "demo"]);
11139        let err = c.validate_etiquetas().unwrap_err();
11140        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11141    }
11142
11143    #[test]
11144    fn validate_etiquetas_duplicate_reports_first_collision() {
11145        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11146        // duplicate (the lexicographically-earliest offending position
11147        // — the second `"a"` at index 2 collides with the first `"a"`
11148        // at index 0), not the later `"b"` collision at index 3,
11149        // peer with every other first-collision diagnostic posture on
11150        // this surface (`validate_load_singularity_reports_first_collision`,
11151        // `validate_cleanup_singularity_reports_first_collision`).
11152        let c = caixa_with_etiquetas(vec!["a", "b", "a", "b"]);
11153        let err = c.validate_etiquetas().unwrap_err();
11154        let ManifestError::EtiquetaDuplicate { etiqueta } = err else {
11155            panic!("expected EtiquetaDuplicate, got {err:?}");
11156        };
11157        assert_eq!(etiqueta, "a");
11158    }
11159
11160    #[test]
11161    fn validate_etiquetas_case_sensitive() {
11162        // Case-sensitivity pin: `("Foo" "foo")` is two distinct entries,
11163        // mirroring the peer `:membros :caixa` / `:children :caixa`
11164        // exact-string-match discipline. The shape gate this routine
11165        // landed (`is_chart_keyword_shape`, Cargo crates.io keyword
11166        // grammar) accepts mixed case — crates.io's keyword rule is
11167        // "case-insensitive" at the index layer but admits mixed case
11168        // at the entry layer (the canonical Helm chart `keywords:`
11169        // shape is lowercase by convention, but the grammar admits
11170        // uppercase). Case-sensitivity at the duplicate-set layer
11171        // remains structural — two distinct strings are two distinct
11172        // entries.
11173        let c = caixa_with_etiquetas(vec!["Foo", "foo"]);
11174        c.validate_etiquetas().unwrap();
11175    }
11176
11177    #[test]
11178    fn validate_etiquetas_diagnostic_carries_offending_tag() {
11179        // Diagnostic-shape pin (peer with
11180        // `validate_code_paths_diagnostic_carries_offending_slot_and_path`):
11181        // the error's Display surfaces the offending tag verbatim, so a
11182        // `feira lint` run can render the diagnostic without re-parsing
11183        // and the author can grep their caixa.lisp for the offending
11184        // value.
11185        let c = caixa_with_etiquetas(vec!["demo", "demo"]);
11186        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11187        assert!(
11188            rendered.contains(":etiquetas"),
11189            "diagnostic must name the offending slot: {rendered}",
11190        );
11191        assert!(
11192            rendered.contains("demo"),
11193            "diagnostic must quote the offending tag: {rendered}",
11194        );
11195    }
11196
11197    #[test]
11198    fn validate_etiquetas_rejects_leading_whitespace_entry() {
11199        // Canonical paste-from-aligned-doc footgun. Without the shape
11200        // gate `" mesh"` silently passed validate and landed as a
11201        // YAML plain-style scalar with leading whitespace in the
11202        // rendered Chart.yaml `keywords:` array — every YAML 1.2
11203        // dumper trims leading whitespace from plain-style scalars,
11204        // so the authored space round-tripped inconsistently back
11205        // through `caixa.lisp`. Mirrors the peer
11206        // `validate_autores_rejects_leading_whitespace_entry`.
11207        let c = caixa_with_etiquetas(vec![" mesh"]);
11208        let err = c.validate_etiquetas().unwrap_err();
11209        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11210            panic!("expected EtiquetaInvalid, got {err:?}");
11211        };
11212        assert_eq!(etiqueta, " mesh");
11213        assert!(reason.contains("whitespace"), "got: {reason}");
11214    }
11215
11216    #[test]
11217    fn validate_etiquetas_rejects_embedded_newline_entry() {
11218        // Canonical paste-from-multiline-doc footgun — the author
11219        // pasted a multi-tag block into one `:etiquetas` entry
11220        // instead of splitting into one entry per tag. Without the
11221        // shape gate `"mesh\nhttp"` silently passed validate and
11222        // landed as a YAML-illegal multi-line scalar in the rendered
11223        // Chart.yaml `keywords:` array.
11224        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11225        let err = c.validate_etiquetas().unwrap_err();
11226        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11227            panic!("expected EtiquetaInvalid, got {err:?}");
11228        };
11229        assert_eq!(etiqueta, "mesh\nhttp");
11230        assert!(reason.contains("newline"), "got: {reason}");
11231    }
11232
11233    #[test]
11234    fn validate_etiquetas_rejects_embedded_comma_entry() {
11235        // Canonical CSV-list-separator-confusion footgun: the author
11236        // confused the CSV-style separator convention with the
11237        // `:etiquetas` list grammar. Without the shape gate
11238        // `"mesh,http,grpc"` silently passed validate and landed as a
11239        // single malformed search tag in the rendered Chart.yaml
11240        // `keywords:` array — Artifact Hub's keyword index would
11241        // either silently drop the tag or index it as
11242        // `mesh,http,grpc` instead of three separate tags.
11243        let c = caixa_with_etiquetas(vec!["mesh,http,grpc"]);
11244        let err = c.validate_etiquetas().unwrap_err();
11245        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11246            panic!("expected EtiquetaInvalid, got {err:?}");
11247        };
11248        assert_eq!(etiqueta, "mesh,http,grpc");
11249        assert!(reason.contains('`'), "got: {reason}");
11250        assert!(reason.contains(','), "got: {reason}");
11251    }
11252
11253    #[test]
11254    fn validate_etiquetas_rejects_embedded_slash_entry() {
11255        // Canonical path-separator-confusion footgun: the author
11256        // confused namespace-path notation with the keyword grammar.
11257        let c = caixa_with_etiquetas(vec!["caixa/servico"]);
11258        let err = c.validate_etiquetas().unwrap_err();
11259        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11260            panic!("expected EtiquetaInvalid, got {err:?}");
11261        };
11262        assert_eq!(etiqueta, "caixa/servico");
11263        assert!(reason.contains('/'), "got: {reason}");
11264    }
11265
11266    #[test]
11267    fn validate_etiquetas_rejects_leading_digit_entry() {
11268        // Canonical paste-from-numbered-list footgun: the author
11269        // copied `1. mesh` from a numbered doc and the `1` leaked
11270        // into the tag.
11271        let c = caixa_with_etiquetas(vec!["1mesh"]);
11272        let err = c.validate_etiquetas().unwrap_err();
11273        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11274            panic!("expected EtiquetaInvalid, got {err:?}");
11275        };
11276        assert_eq!(etiqueta, "1mesh");
11277        assert!(reason.contains("digit"), "got: {reason}");
11278    }
11279
11280    #[test]
11281    fn validate_etiquetas_rejects_leading_hyphen_entry() {
11282        // Canonical kebab-leak footgun.
11283        let c = caixa_with_etiquetas(vec!["-foo"]);
11284        let err = c.validate_etiquetas().unwrap_err();
11285        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11286            panic!("expected EtiquetaInvalid, got {err:?}");
11287        };
11288        assert_eq!(etiqueta, "-foo");
11289        assert!(reason.contains('-'), "got: {reason}");
11290    }
11291
11292    #[test]
11293    fn validate_etiquetas_rejects_non_ascii_entry() {
11294        // Canonical paste-from-Unicode-doc footgun. Every legitimate
11295        // search tag is strict ASCII; raw non-ASCII silently
11296        // round-trips inconsistently across NFC/NFD normalization on
11297        // APFS / case-folding filesystems and breaks the Artifact Hub
11298        // keyword search index lookup.
11299        let c = caixa_with_etiquetas(vec!["café"]);
11300        let err = c.validate_etiquetas().unwrap_err();
11301        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11302            panic!("expected EtiquetaInvalid, got {err:?}");
11303        };
11304        assert_eq!(etiqueta, "café");
11305        assert!(reason.contains("non-ASCII"), "got: {reason}");
11306    }
11307
11308    #[test]
11309    fn validate_etiquetas_rejects_period_entry() {
11310        // Canonical namespace-confusion / version-suffix footgun
11311        // (`"http.1"` / `"v1.0"`): Cargo's crates.io keyword grammar
11312        // excludes `.` from the continuation set even though the
11313        // sibling `:caracteristicas` axis (Cargo's feature-name
11314        // grammar) admits it. Tighter than the sibling axis, peer
11315        // with Cargo's own crates.io keyword shape.
11316        let c = caixa_with_etiquetas(vec!["http.1"]);
11317        let err = c.validate_etiquetas().unwrap_err();
11318        let ManifestError::EtiquetaInvalid { etiqueta, reason } = err else {
11319            panic!("expected EtiquetaInvalid, got {err:?}");
11320        };
11321        assert_eq!(etiqueta, "http.1");
11322        assert!(reason.contains('.'), "got: {reason}");
11323    }
11324
11325    #[test]
11326    fn validate_etiquetas_empty_takes_precedence_over_shape() {
11327        // Per-entry empty-first cascade pin: an entry that is both
11328        // empty *and* shape-invalid surfaces `EtiquetaEmpty` (the
11329        // narrower "this entry has no value" structural defect
11330        // dominates the broader shape-predicate diagnostic). The
11331        // empty arm fires before the shape predicate is consulted,
11332        // mirroring the peer `validate_autores_empty_takes_precedence_over_shape`
11333        // cascade established on the sibling universal-axis Vec<String>
11334        // surface.
11335        let c = caixa_with_etiquetas(vec![""]);
11336        let err = c.validate_etiquetas().unwrap_err();
11337        assert!(matches!(err, ManifestError::EtiquetaEmpty), "got {err:?}",);
11338    }
11339
11340    #[test]
11341    fn validate_etiquetas_shape_takes_precedence_over_duplicate() {
11342        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11343        // entry that is malformed surfaces `EtiquetaInvalid` even when
11344        // a later entry would have collided on duplicate. The
11345        // per-entry shape arm fires inside the same loop iteration as
11346        // the empty arm, before the seen-set insert at end-of-iteration
11347        // — structural per-entry defects dominate the cross-entry
11348        // uniqueness diagnostic. Mirrors the peer
11349        // `validate_autores_shape_takes_precedence_over_duplicate`.
11350        let c = caixa_with_etiquetas(vec!["mesh\nhttp", "mesh\nhttp"]);
11351        let err = c.validate_etiquetas().unwrap_err();
11352        assert!(
11353            matches!(err, ManifestError::EtiquetaInvalid { .. }),
11354            "got {err:?}",
11355        );
11356    }
11357
11358    #[test]
11359    fn validate_etiquetas_invalid_diagnostic_names_offending_slot_and_value() {
11360        // Diagnostic-shape pin on the new shape arm (peer with
11361        // `validate_autores_invalid_diagnostic_names_offending_slot_and_value`):
11362        // the rendered Display surfaces both the offending slot name
11363        // and the offending value verbatim, so a `feira lint` run
11364        // points the author at the exact `:etiquetas` entry to fix.
11365        let c = caixa_with_etiquetas(vec!["mesh\nhttp"]);
11366        let rendered = c.validate_etiquetas().unwrap_err().to_string();
11367        assert!(
11368            rendered.contains(":etiquetas"),
11369            "diagnostic must name the offending slot: {rendered}",
11370        );
11371        assert!(
11372            rendered.contains("mesh\\nhttp"),
11373            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11374        );
11375    }
11376
11377    #[test]
11378    fn validate_etiquetas_rejects_at_21_byte_boundary() {
11379        // The 20-byte cap pin — boundary-exceeding case rejected,
11380        // boundary-accepting case passes. Mirrors the peer
11381        // `chart_keyword_shape_rejects_at_21_byte_boundary` substrate-
11382        // side pin, surfaced at the per-axis caller so the cap
11383        // propagates through validate end-to-end. Constructed as a
11384        // single all-`a` token so only the cap arm fires.
11385        let max_ok = "a".repeat(20);
11386        let c = caixa_with_etiquetas(vec![max_ok.as_str()]);
11387        c.validate_etiquetas().unwrap();
11388        let too_long = "a".repeat(21);
11389        let c = caixa_with_etiquetas(vec![too_long.as_str()]);
11390        let err = c.validate_etiquetas().unwrap_err();
11391        let ManifestError::EtiquetaInvalid { reason, .. } = err else {
11392            panic!("expected EtiquetaInvalid, got {err:?}");
11393        };
11394        assert!(reason.contains("20"), "got: {reason}");
11395        assert!(reason.contains("21"), "got: {reason}");
11396    }
11397
11398    #[test]
11399    fn validate_etiquetas_accepts_canonical_shaped_forms() {
11400        // Positive control sweep: every canonical-shaped tag from the
11401        // hello-rio / checkout-aplicacao / pangea-tatara-akeyless
11402        // example fixtures plus the substrate-fixed tags caixa-helm
11403        // unions in at chart render. Drift between this list and the
11404        // substrate-side `chart_keyword_shape_accepts_canonical_forms`
11405        // sweep surfaces here — one source of truth for the rule.
11406        let c = caixa_with_etiquetas(vec![
11407            "example",
11408            "aplicacao",
11409            "mesh",
11410            "ecommerce",
11411            "demo",
11412            "infrastructure",
11413            "aws",
11414            "akeyless",
11415            "pangea-native",
11416            "hello-world",
11417            "wasm",
11418            "rust",
11419            "tatara-lisp",
11420            "caixa-servico",
11421            "lareira",
11422        ]);
11423        c.validate_etiquetas().unwrap();
11424    }
11425
11426    // ── validate_autores — universal-axis maintainer shape ────────────
11427
11428    fn caixa_with_autores(autores: Vec<&str>) -> Caixa {
11429        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11430        c.autores = autores.into_iter().map(String::from).collect();
11431        c
11432    }
11433
11434    #[test]
11435    fn validate_autores_accepts_empty_list() {
11436        // The empty-list identity: `Caixa::template` emits `:autores ()`,
11437        // so the gate is non-disruptive against every existing manifest.
11438        let c = caixa_with_autores(vec![]);
11439        c.validate_autores().unwrap();
11440    }
11441
11442    #[test]
11443    fn validate_autores_accepts_canonical_forms() {
11444        // Positive control sweep: every canonical-shaped non-empty
11445        // distinct maintainer list passes — the hello-rio / checkout-
11446        // aplicacao fixtures' `:autores ("pleme-io")` shape, plus the
11447        // multi-author shape downstream packaging surfaces emit.
11448        let c = caixa_with_autores(vec!["pleme-io"]);
11449        c.validate_autores().unwrap();
11450        let c = caixa_with_autores(vec!["alice <alice@example.com>", "bob <bob@example.com>"]);
11451        c.validate_autores().unwrap();
11452    }
11453
11454    #[test]
11455    fn validate_autores_rejects_empty_entry() {
11456        // Canonical paste-from-blank-doc footgun. Without the gate the
11457        // empty entry rendered as `maintainers: [{name: "", email: null}]`
11458        // in `Chart.yaml`, a no-op maintainer the substrate cannot route
11459        // to.
11460        let c = caixa_with_autores(vec![""]);
11461        let err = c.validate_autores().unwrap_err();
11462        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11463    }
11464
11465    #[test]
11466    fn validate_autores_rejects_duplicate_entry() {
11467        // Canonical copy-paste-the-wrong-author footgun. Unlike the
11468        // `:etiquetas` peer (caixa-helm's `BTreeSet` collect silently
11469        // dedups the rendered `keywords:` array), the `maintainers:`
11470        // rendering has *no* dedup — duplicates stack verbatim. The
11471        // duplicate-arm names the offending author verbatim.
11472        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11473        let err = c.validate_autores().unwrap_err();
11474        let ManifestError::AutorDuplicate { autor } = err else {
11475            panic!("expected AutorDuplicate, got {err:?}");
11476        };
11477        assert_eq!(autor, "pleme-io");
11478    }
11479
11480    #[test]
11481    fn validate_autores_empty_takes_precedence_over_duplicate() {
11482        // Empty-first cascade pin: `("" "pleme-io" "pleme-io")` surfaces
11483        // `AutorEmpty` not `AutorDuplicate` — the narrower structural
11484        // "this entry has no value" defect dominates the cross-entry
11485        // uniqueness diagnostic. Mirrors the peer empty-before-duplicate
11486        // cascades on `:etiquetas` (`EtiquetaEmpty` before
11487        // `EtiquetaDuplicate`, 360a499), `:caracteristicas`
11488        // (`CaracteristicaEmpty` before `CaracteristicaDuplicate`,
11489        // fc3b4d5), and `:membros :caixa` (`MembroCaixaEmpty` before
11490        // `MembroDuplicate`).
11491        let c = caixa_with_autores(vec!["", "pleme-io", "pleme-io"]);
11492        let err = c.validate_autores().unwrap_err();
11493        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11494    }
11495
11496    #[test]
11497    fn validate_autores_duplicate_reports_first_collision() {
11498        // First-collision pin: `("a" "b" "a" "b")` surfaces the `"a"`
11499        // duplicate (the lexicographically-earliest offending position
11500        // — the second `"a"` at index 2 collides with the first `"a"`
11501        // at index 0), not the later `"b"` collision at index 3,
11502        // peer with every other first-collision diagnostic posture on
11503        // this surface.
11504        let c = caixa_with_autores(vec!["a", "b", "a", "b"]);
11505        let err = c.validate_autores().unwrap_err();
11506        let ManifestError::AutorDuplicate { autor } = err else {
11507            panic!("expected AutorDuplicate, got {err:?}");
11508        };
11509        assert_eq!(autor, "a");
11510    }
11511
11512    #[test]
11513    fn validate_autores_case_sensitive() {
11514        // Case-sensitivity pin: `("Pleme-io" "pleme-io")` is two distinct
11515        // entries, mirroring the peer `:etiquetas` / `:membros :caixa`
11516        // / `:children :caixa` exact-string-match discipline.
11517        let c = caixa_with_autores(vec!["Pleme-io", "pleme-io"]);
11518        c.validate_autores().unwrap();
11519    }
11520
11521    #[test]
11522    fn validate_autores_diagnostic_carries_offending_author() {
11523        // Diagnostic-shape pin (peer with
11524        // `validate_etiquetas_diagnostic_carries_offending_tag`): the
11525        // error's Display surfaces the offending author verbatim, so a
11526        // `feira lint` run can render the diagnostic without re-parsing
11527        // and the author can grep their caixa.lisp for the offending
11528        // value.
11529        let c = caixa_with_autores(vec!["pleme-io", "pleme-io"]);
11530        let rendered = c.validate_autores().unwrap_err().to_string();
11531        assert!(
11532            rendered.contains(":autores"),
11533            "diagnostic must name the offending slot: {rendered}",
11534        );
11535        assert!(
11536            rendered.contains("pleme-io"),
11537            "diagnostic must quote the offending author: {rendered}",
11538        );
11539    }
11540
11541    #[test]
11542    fn validate_autores_rejects_leading_whitespace_entry() {
11543        // Canonical paste-from-aligned-doc footgun. Without the shape
11544        // gate `" pleme-io"` silently passed validate and landed as a
11545        // YAML plain-style scalar with leading whitespace in the
11546        // rendered Chart.yaml `maintainers:` array — every YAML 1.2
11547        // dumper trims leading whitespace from plain-style scalars, so
11548        // the authored space round-tripped inconsistently back through
11549        // `caixa.lisp`. Mirrors the peer
11550        // `validate_descricao_rejects_leading_whitespace`.
11551        let c = caixa_with_autores(vec![" pleme-io"]);
11552        let err = c.validate_autores().unwrap_err();
11553        let ManifestError::AutorInvalid { autor, reason } = err else {
11554            panic!("expected AutorInvalid, got {err:?}");
11555        };
11556        assert_eq!(autor, " pleme-io");
11557        assert!(reason.contains("whitespace"), "got: {reason}");
11558    }
11559
11560    #[test]
11561    fn validate_autores_rejects_trailing_whitespace_entry() {
11562        // Canonical paste-from-doc footgun.
11563        let c = caixa_with_autores(vec!["pleme-io "]);
11564        let err = c.validate_autores().unwrap_err();
11565        let ManifestError::AutorInvalid { autor, reason } = err else {
11566            panic!("expected AutorInvalid, got {err:?}");
11567        };
11568        assert_eq!(autor, "pleme-io ");
11569        assert!(reason.contains("whitespace"), "got: {reason}");
11570    }
11571
11572    #[test]
11573    fn validate_autores_rejects_embedded_newline_entry() {
11574        // Canonical paste-from-multiline-doc footgun — the author
11575        // pasted a multi-line block of author records into one
11576        // `:autores` entry instead of splitting into one entry per
11577        // author. Without the shape gate `"alice\nbob"` silently
11578        // passed validate and landed as a YAML-illegal multi-line
11579        // scalar in the rendered Chart.yaml `maintainers:` array.
11580        let c = caixa_with_autores(vec!["alice\nbob"]);
11581        let err = c.validate_autores().unwrap_err();
11582        let ManifestError::AutorInvalid { autor, reason } = err else {
11583            panic!("expected AutorInvalid, got {err:?}");
11584        };
11585        assert_eq!(autor, "alice\nbob");
11586        assert!(reason.contains("newline"), "got: {reason}");
11587    }
11588
11589    #[test]
11590    fn validate_autores_rejects_embedded_carriage_return_entry() {
11591        // Canonical paste-from-Windows-CRLF-doc footgun.
11592        let c = caixa_with_autores(vec!["alice\rbob"]);
11593        let err = c.validate_autores().unwrap_err();
11594        let ManifestError::AutorInvalid { autor, reason } = err else {
11595            panic!("expected AutorInvalid, got {err:?}");
11596        };
11597        assert_eq!(autor, "alice\rbob");
11598        assert!(reason.contains("carriage return"), "got: {reason}");
11599    }
11600
11601    #[test]
11602    fn validate_autores_rejects_embedded_tab_entry() {
11603        // Canonical tab-from-aligned-doc footgun.
11604        let c = caixa_with_autores(vec!["Pleme\tContributors"]);
11605        let err = c.validate_autores().unwrap_err();
11606        let ManifestError::AutorInvalid { autor, reason } = err else {
11607            panic!("expected AutorInvalid, got {err:?}");
11608        };
11609        assert_eq!(autor, "Pleme\tContributors");
11610        assert!(reason.contains("tab"), "got: {reason}");
11611    }
11612
11613    #[test]
11614    fn validate_autores_rejects_embedded_control_bytes_entry() {
11615        // Paste-from-binary-blob footguns: NUL, BEL, ESC, DEL all
11616        // surface the same control-byte arm.
11617        for entry in [
11618            "alice\x00bob",
11619            "alice\x07bob",
11620            "alice\x1bbob",
11621            "alice\x7fbob",
11622        ] {
11623            let c = caixa_with_autores(vec![entry]);
11624            let err = c.validate_autores().unwrap_err();
11625            let ManifestError::AutorInvalid { autor, reason } = err else {
11626                panic!("expected AutorInvalid for {entry:?}, got {err:?}");
11627            };
11628            assert_eq!(autor, entry);
11629            assert!(
11630                reason.contains("control character"),
11631                "{entry:?} reason: {reason}",
11632            );
11633        }
11634    }
11635
11636    #[test]
11637    fn validate_autores_accepts_unicode_entry() {
11638        // Unicode positive control: realistic maintainer names carry
11639        // Unicode (`François`, `日本語`, `naïve`). The predicate must
11640        // round-trip Unicode losslessly, peer with the
11641        // `chart_maintainer_name_shape_accepts_unicode` substrate-side
11642        // sweep.
11643        let c = caixa_with_autores(vec![
11644            "François Dupont",
11645            "日本語の名前",
11646            "naïve <naive@example.com>",
11647        ]);
11648        c.validate_autores().unwrap();
11649    }
11650
11651    #[test]
11652    fn validate_autores_empty_takes_precedence_over_shape() {
11653        // Per-entry empty-first cascade pin: an entry that is both
11654        // empty *and* shape-invalid surfaces `AutorEmpty` (the narrower
11655        // "this entry has no value" structural defect dominates the
11656        // broader shape-predicate diagnostic). The empty arm fires
11657        // before the shape predicate is consulted, mirroring the peer
11658        // `validate_repositorio_empty_takes_precedence_over_shape`
11659        // cascade on the universal `Option<String>` siblings — and now
11660        // established on the Vec<String> per-entry surface.
11661        let c = caixa_with_autores(vec![""]);
11662        let err = c.validate_autores().unwrap_err();
11663        assert!(matches!(err, ManifestError::AutorEmpty), "got {err:?}",);
11664    }
11665
11666    #[test]
11667    fn validate_autores_shape_takes_precedence_over_duplicate() {
11668        // Per-entry shape-before-cross-entry-duplicate cascade pin: an
11669        // entry that is malformed surfaces `AutorInvalid` even when a
11670        // later entry would have collided on duplicate. The per-entry
11671        // shape arm fires inside the same loop iteration as the empty
11672        // arm, before the seen-set insert at end-of-iteration —
11673        // structural per-entry defects dominate the cross-entry
11674        // uniqueness diagnostic.
11675        let c = caixa_with_autores(vec!["alice\nbob", "alice\nbob"]);
11676        let err = c.validate_autores().unwrap_err();
11677        assert!(
11678            matches!(err, ManifestError::AutorInvalid { .. }),
11679            "got {err:?}",
11680        );
11681    }
11682
11683    #[test]
11684    fn validate_autores_invalid_diagnostic_names_offending_slot_and_value() {
11685        // Diagnostic-shape pin on the new shape arm (peer with
11686        // `validate_descricao_invalid_diagnostic_carries_offending_value`):
11687        // the rendered Display surfaces both the offending slot name
11688        // and the offending value verbatim, so a `feira lint` run
11689        // points the author at the exact `:autores` entry to fix.
11690        let c = caixa_with_autores(vec!["alice\nbob"]);
11691        let rendered = c.validate_autores().unwrap_err().to_string();
11692        assert!(
11693            rendered.contains(":autores"),
11694            "diagnostic must name the offending slot: {rendered}",
11695        );
11696        assert!(
11697            rendered.contains("alice\\nbob"),
11698            "diagnostic must quote the offending value (debug-escaped): {rendered}",
11699        );
11700    }
11701
11702    #[test]
11703    fn validate_autores_rejects_at_129_byte_boundary() {
11704        // The 128-byte cap pin — boundary-exceeding case rejected,
11705        // boundary-accepting case passes. Mirrors the peer
11706        // `chart_maintainer_name_shape_rejects_at_129_byte_boundary`
11707        // substrate-side pin, surfaced at the per-axis caller so the
11708        // cap propagates through validate end-to-end. Constructed as
11709        // a single all-`a` token so only the cap arm fires.
11710        let max_ok = "a".repeat(128);
11711        let c = caixa_with_autores(vec![max_ok.as_str()]);
11712        c.validate_autores().unwrap();
11713        let too_long = "a".repeat(129);
11714        let c = caixa_with_autores(vec![too_long.as_str()]);
11715        let err = c.validate_autores().unwrap_err();
11716        let ManifestError::AutorInvalid { reason, .. } = err else {
11717            panic!("expected AutorInvalid, got {err:?}");
11718        };
11719        assert!(reason.contains("128"), "got: {reason}");
11720        assert!(reason.contains("129"), "got: {reason}");
11721    }
11722
11723    // ── validate_repositorio — universal-axis git-repo-URL shape ──────
11724
11725    fn caixa_with_repositorio(repositorio: Option<&str>) -> Caixa {
11726        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
11727        c.repositorio = repositorio.map(String::from);
11728        c
11729    }
11730
11731    #[test]
11732    fn validate_repositorio_accepts_none() {
11733        // The omit-the-slot identity: `:repositorio` is optional. The
11734        // gate is a no-op when the author didn't declare a value —
11735        // every caixa without a `:repositorio` line trivially passes,
11736        // and the substrate-side renderers fall back to their
11737        // documented placeholder (`caixa-helm`'s `home: None`,
11738        // `caixa-flux`'s `https://github.com/pleme-io/<nome>` derived
11739        // URL). Mirrors the peer `validate_restart_window_accepts_none`
11740        // posture on the other `Option<String>` Caixa slot.
11741        let c = caixa_with_repositorio(None);
11742        c.validate_repositorio().unwrap();
11743    }
11744
11745    #[test]
11746    fn validate_repositorio_accepts_canonical_forms() {
11747        // Positive control sweep across every documented `:repositorio`
11748        // authoring shape — the same union the shared
11749        // `crate::render::is_git_repo_url` predicate accepts and the
11750        // peer `:deps :fonte :repo` axis already routes through.
11751        // Covers the `github:` shorthand (the canonical pleme-io
11752        // convention used in the `:repositorio` field of every
11753        // manifest fixture across `caixa-helm` / `caixa-mesh` and the
11754        // `examples/`), the `https://…` URL the README quickstart uses,
11755        // the `ssh://`, `git://`, `git@host:path` scp-style SSH, and
11756        // `file://` URL schemes the shared predicate documents.
11757        for repo in [
11758            "github:pleme-io/hello-rio",
11759            "github:pleme-io/checkout",
11760            "https://github.com/pleme-io/hello-rio",
11761            "ssh://git@github.com/pleme-io/hello-rio.git",
11762            "git://github.com/pleme-io/hello-rio.git",
11763            "git@github.com:pleme-io/hello-rio.git",
11764            "file:///srv/pleme/hello-rio",
11765        ] {
11766            let c = caixa_with_repositorio(Some(repo));
11767            c.validate_repositorio()
11768                .unwrap_or_else(|err| panic!("canonical {repo:?} must pass: {err:?}"));
11769        }
11770    }
11771
11772    #[test]
11773    fn validate_repositorio_rejects_empty_some() {
11774        // Canonical paste-from-blank-doc footgun. The narrower
11775        // [`ManifestError::RepositorioEmpty`] arm fires before the
11776        // shape predicate is consulted, mirroring the empty-first
11777        // cascade every peer per-axis identity gate uses
11778        // (`NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
11779        // `FonteRepoEmpty` → `FonteRepoInvalid`). Without this gate
11780        // the empty `Some("")` silently passed the renderer's
11781        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
11782        // on `None`) and landed as `home: ""` in `Chart.yaml` /
11783        // `url: ""` in the FluxCD `GitRepository`.
11784        let c = caixa_with_repositorio(Some(""));
11785        let err = c.validate_repositorio().unwrap_err();
11786        assert!(
11787            matches!(err, ManifestError::RepositorioEmpty),
11788            "got {err:?}",
11789        );
11790    }
11791
11792    #[test]
11793    fn validate_repositorio_rejects_whitespace() {
11794        // Paste-from-doc whitespace footgun. The shared
11795        // `is_git_repo_url` predicate refuses any whitespace byte; a
11796        // trailing space in a `:repositorio` value silently broke
11797        // `git clone '<value> '` at clone time. The diagnostic names
11798        // the offending value verbatim.
11799        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio "));
11800        let err = c.validate_repositorio().unwrap_err();
11801        let ManifestError::RepositorioInvalid { repositorio, .. } = err else {
11802            panic!("expected RepositorioInvalid, got {err:?}");
11803        };
11804        assert_eq!(repositorio, "github:pleme-io/hello-rio ");
11805    }
11806
11807    #[test]
11808    fn validate_repositorio_rejects_control_char() {
11809        // Paste-from-multiline-doc CRLF footgun — control characters
11810        // at the URL boundary are a class of subprocess-arg injection
11811        // and break git's URL parser at every porcelain entry point.
11812        let c = caixa_with_repositorio(Some("https://example.com/repo\n"));
11813        let err = c.validate_repositorio().unwrap_err();
11814        assert!(
11815            matches!(err, ManifestError::RepositorioInvalid { .. }),
11816            "got {err:?}",
11817        );
11818    }
11819
11820    #[test]
11821    fn validate_repositorio_rejects_leading_dash() {
11822        // Canonical CLI-argument-injection footgun: `git clone <repo>`
11823        // interprets a leading `-` as a CLI flag, so a
11824        // `-upload-pack=…` value escapes the subprocess argument
11825        // boundary. The shared predicate refuses every leading-`-`
11826        // shape at validate time.
11827        let c = caixa_with_repositorio(Some("-upload-pack=evil"));
11828        let err = c.validate_repositorio().unwrap_err();
11829        assert!(
11830            matches!(err, ManifestError::RepositorioInvalid { .. }),
11831            "got {err:?}",
11832        );
11833    }
11834
11835    #[test]
11836    fn validate_repositorio_rejects_missing_colon_separator() {
11837        // The bare `org/repo` ambiguity footgun — `git clone` reads
11838        // a no-`:` form as a relative filesystem path rather than the
11839        // GitHub-shorthand expansion the author probably intended.
11840        // The shared predicate refuses every shape without a `:`
11841        // separator.
11842        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
11843        let err = c.validate_repositorio().unwrap_err();
11844        assert!(
11845            matches!(err, ManifestError::RepositorioInvalid { .. }),
11846            "got {err:?}",
11847        );
11848    }
11849
11850    #[test]
11851    fn validate_repositorio_rejects_fragment_anchor() {
11852        // Paste-from-browser-address-bar footgun on the
11853        // `:repositorio` axis — an author copies a GitHub permalink
11854        // to a README section / line-permalink and forgets to trim
11855        // the `#fragment` tail. The shared `is_git_repo_url`
11856        // predicate refuses the byte at the URL-grammar layer
11857        // (libcurl strips the fragment before opening the
11858        // transport, so the byte rides verbatim into the rendered
11859        // `Chart.yaml` `home:` and FluxCD `GitRepository` `url:`
11860        // fields but is silently dropped on the wire — two
11861        // manifest variants whose values differ only in their
11862        // fragment anchor lock to two distinct rendered artifacts
11863        // for the byte-identical clone, defeating the THEORY.md
11864        // §V.2 render-determinism contract on the `:repositorio`
11865        // axis the peer `:fonte :repo` axis already closes).
11866        let c = caixa_with_repositorio(Some("https://github.com/pleme-io/hello-rio#readme"));
11867        let err = c.validate_repositorio().unwrap_err();
11868        let ManifestError::RepositorioInvalid {
11869            repositorio,
11870            reason,
11871        } = err
11872        else {
11873            panic!("expected RepositorioInvalid, got {err:?}");
11874        };
11875        assert_eq!(repositorio, "https://github.com/pleme-io/hello-rio#readme");
11876        assert!(
11877            reason.contains("must not contain `#`"),
11878            "reason must surface the fragment-`#` arm, got {reason:?}"
11879        );
11880    }
11881
11882    #[test]
11883    fn validate_repositorio_rejects_query_string() {
11884        // Paste-from-browser-address-bar footgun on the
11885        // `:repositorio` axis (peer with the a68f818 fragment-`#`
11886        // arm on the same axis). An author copies a GitHub tab
11887        // deep-link out of the address bar and forgets to trim
11888        // the `?tab=…` query tail. The shared `is_git_repo_url`
11889        // predicate refuses the byte at the URL-grammar layer
11890        // (GitHub / GitLab / Bitbucket silently ignore the
11891        // `?query` tail and serve the same repo regardless, so
11892        // the byte rides verbatim into the rendered `Chart.yaml`
11893        // `home:` and FluxCD `GitRepository` `url:` fields but
11894        // is silently masked at the wire — two manifest variants
11895        // whose values differ only in their query tail lock to
11896        // two distinct rendered artifacts for the byte-identical
11897        // clone, defeating the THEORY.md §V.2 render-determinism
11898        // contract on the `:repositorio` axis the peer `:fonte
11899        // :repo` axis already closes).
11900        let c = caixa_with_repositorio(Some(
11901            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file",
11902        ));
11903        let err = c.validate_repositorio().unwrap_err();
11904        let ManifestError::RepositorioInvalid {
11905            repositorio,
11906            reason,
11907        } = err
11908        else {
11909            panic!("expected RepositorioInvalid, got {err:?}");
11910        };
11911        assert_eq!(
11912            repositorio,
11913            "https://github.com/pleme-io/hello-rio?tab=readme-ov-file"
11914        );
11915        assert!(
11916            reason.contains("must not contain `?`"),
11917            "reason must surface the query-`?` arm, got {reason:?}"
11918        );
11919    }
11920
11921    #[test]
11922    fn validate_repositorio_rejects_embedded_backslash() {
11923        // Windows-file-path-confusion footgun on the `:repositorio`
11924        // axis (peer with the prior fragment-`#` / query-`?` arms on
11925        // the same axis, and peer with the new dep-level `:fonte :repo`
11926        // backslash arm on the URL-grammar trajectory). An author
11927        // pastes a Windows Explorer address-bar `file:///C:\Users\me\
11928        // hello-rio` into the `:repositorio` slot, expecting the
11929        // `lareira-<nome>` chart's `home:` field and the FluxCD
11930        // `GitRepository` `url:` field to render the canonical local
11931        // file-URI. The shared `is_git_repo_url` predicate refuses
11932        // the byte at the URL-grammar layer (libcurl silently
11933        // translates `\` → `/` on some platforms and refuses it on
11934        // others, so the byte rides verbatim into the rendered
11935        // artifacts but is silently rewritten or rejected at the wire
11936        // — two manifest variants whose values differ only in
11937        // backslash-vs-forward-slash lock to two distinct rendered
11938        // artifacts for the byte-identical clone, defeating the
11939        // THEORY.md §V.2 render-determinism contract on the
11940        // `:repositorio` axis the peer `:fonte :repo` axis already
11941        // closes).
11942        let c = caixa_with_repositorio(Some("file:///C:\\Users\\me\\hello-rio"));
11943        let err = c.validate_repositorio().unwrap_err();
11944        let ManifestError::RepositorioInvalid {
11945            repositorio,
11946            reason,
11947        } = err
11948        else {
11949            panic!("expected RepositorioInvalid, got {err:?}");
11950        };
11951        assert_eq!(repositorio, "file:///C:\\Users\\me\\hello-rio");
11952        assert!(
11953            reason.contains("must not contain `\\`"),
11954            "reason must surface the backslash-`\\` arm, got {reason:?}"
11955        );
11956    }
11957
11958    #[test]
11959    fn validate_repositorio_rejects_uri_template_placeholder() {
11960        // URI Template (RFC 6570) placeholder footgun on the
11961        // `:repositorio` axis (peer with the prior fragment-`#` /
11962        // query-`?` / backslash-`\` arms on the same axis, and peer
11963        // with the new dep-level `:fonte :repo` `{` / `}` arm on the
11964        // URL-grammar trajectory). An author pastes a quick-start
11965        // README snippet / OpenAPI `servers:` URL / Helm chart
11966        // `home:` template carrying unresolved `{org}` / `{repo}`
11967        // placeholders into the `:repositorio` slot, expecting the
11968        // substrate to resolve the placeholder downstream. The
11969        // shared `is_git_repo_url` predicate refuses the byte at the
11970        // URL-grammar layer (libcurl percent-encodes `{` / `}` to
11971        // `%7B` / `%7D` on the wire, so the byte round-trips
11972        // inconsistently between the rendered `Chart.yaml home:` /
11973        // FluxCD `GitRepository url:` and the resolver's `git clone`
11974        // invocation, defeating the THEORY.md §V.2 render-
11975        // determinism contract on the `:repositorio` axis the peer
11976        // `:fonte :repo` axis already closes; every git porcelain
11977        // entry-point additionally fetches a nonexistent literal-
11978        // `{placeholder}`-named path far from the source caixa.lisp).
11979        let c = caixa_with_repositorio(Some("https://github.com/{org}/hello-rio"));
11980        let err = c.validate_repositorio().unwrap_err();
11981        let ManifestError::RepositorioInvalid {
11982            repositorio,
11983            reason,
11984        } = err
11985        else {
11986            panic!("expected RepositorioInvalid, got {err:?}");
11987        };
11988        assert_eq!(repositorio, "https://github.com/{org}/hello-rio");
11989        assert!(
11990            reason.contains("must not contain `{`"),
11991            "reason must surface the open-brace `{{` arm, got {reason:?}"
11992        );
11993        assert!(
11994            reason.contains("URI Template") || reason.contains("RFC 6570"),
11995            "reason must name the RFC 6570 URI Template grammar, got {reason:?}"
11996        );
11997    }
11998
11999    #[test]
12000    fn validate_repositorio_empty_takes_precedence_over_shape() {
12001        // Empty-first cascade pin: the empty `Some("")` surfaces the
12002        // narrower `RepositorioEmpty` not the shape-predicate-wrapped
12003        // `RepositorioInvalid`, mirroring the peer
12004        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid`,
12005        // `FonteRepoEmpty` → `FonteRepoInvalid` cascades. The shared
12006        // `is_git_repo_url` predicate also rejects the empty input
12007        // (defensively, with its own `"must not be empty"` reason),
12008        // but the manifest-layer empty arm runs first to surface the
12009        // narrower diagnostic verbatim.
12010        let c = caixa_with_repositorio(Some(""));
12011        let err = c.validate_repositorio().unwrap_err();
12012        assert!(
12013            matches!(err, ManifestError::RepositorioEmpty),
12014            "got {err:?}",
12015        );
12016    }
12017
12018    #[test]
12019    fn validate_repositorio_diagnostic_carries_offending_value() {
12020        // Diagnostic-shape pin (peer with
12021        // `validate_autores_diagnostic_carries_offending_author`): the
12022        // error's Display surfaces the offending value + slot name
12023        // verbatim, so a `feira lint` run can render the diagnostic
12024        // without re-parsing and the author can grep their caixa.lisp
12025        // for the offending `:repositorio` value.
12026        let c = caixa_with_repositorio(Some("pleme-io/hello-rio"));
12027        let rendered = c.validate_repositorio().unwrap_err().to_string();
12028        assert!(
12029            rendered.contains(":repositorio"),
12030            "diagnostic must name the offending slot: {rendered}",
12031        );
12032        assert!(
12033            rendered.contains("pleme-io/hello-rio"),
12034            "diagnostic must quote the offending value: {rendered}",
12035        );
12036    }
12037
12038    // ── validate_descricao — universal-axis Chart.yaml description shape ──
12039
12040    fn caixa_with_descricao(descricao: Option<&str>) -> Caixa {
12041        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12042        c.descricao = descricao.map(String::from);
12043        c
12044    }
12045
12046    #[test]
12047    fn validate_descricao_accepts_none() {
12048        // The omit-the-slot identity: `:descricao` is optional. The
12049        // gate is a no-op when the author didn't declare a value —
12050        // every caixa without a `:descricao` line trivially passes,
12051        // and the substrate-side renderers fall back to their
12052        // documented `caixa.nome`-derived placeholder. Mirrors the
12053        // peer `validate_repositorio_accepts_none` posture on the
12054        // sibling `Option<String>` Caixa slot.
12055        let c = caixa_with_descricao(None);
12056        c.validate_descricao().unwrap();
12057    }
12058
12059    #[test]
12060    fn validate_descricao_accepts_canonical_summary() {
12061        // Positive control: the canonical pleme-io descricao shape —
12062        // a short free-form prose summary — passes the gate. Covers
12063        // the fixture shapes the `caixa-helm` / `caixa-flux` /
12064        // `caixa-mesh` test fixtures use (`"Canonical Rust→wasm32-
12065        // wasip2 caixa Servico."`, `"Checkout flow."`).
12066        for desc in [
12067            "Canonical Rust→wasm32-wasip2 caixa Servico.",
12068            "Checkout flow.",
12069            "AWS provider caixa for tatara-lisp",
12070            "FIXME — describe this caixa",
12071            "x",
12072        ] {
12073            let c = caixa_with_descricao(Some(desc));
12074            c.validate_descricao()
12075                .unwrap_or_else(|err| panic!("canonical {desc:?} must pass: {err:?}"));
12076        }
12077    }
12078
12079    #[test]
12080    fn validate_descricao_rejects_empty_some() {
12081        // Canonical paste-from-blank-doc footgun. Without this gate
12082        // the empty `Some("")` silently passed the renderer's
12083        // `Option::unwrap_or_else(|| <fallback>)` (which only fires
12084        // on `None`) and landed as `description: ""` in `Chart.yaml`
12085        // and a blank `README.md` header. Mirrors the peer
12086        // [`ManifestError::RepositorioEmpty`] empty-arm on the
12087        // sibling `Option<String>` Caixa slot.
12088        let c = caixa_with_descricao(Some(""));
12089        let err = c.validate_descricao().unwrap_err();
12090        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
12091    }
12092
12093    #[test]
12094    fn validate_descricao_rejects_leading_whitespace() {
12095        // Paste-from-aligned-doc footgun: a leading ASCII space the
12096        // bare empty-arm gate accepted, the shape predicate now
12097        // refuses. The diagnostic carries the offending value
12098        // verbatim (with the leading space preserved) so the author
12099        // can grep their caixa.lisp for the exact `:descricao` line
12100        // and fix the round-trip-inconsistent leading whitespace.
12101        // Mirrors the peer
12102        // `validate_licenca_rejects_leading_whitespace` arm on the
12103        // sibling `:licenca` axis.
12104        let c = caixa_with_descricao(Some(" Checkout flow."));
12105        let err = c.validate_descricao().unwrap_err();
12106        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
12107            panic!("expected DescricaoInvalid, got {err:?}");
12108        };
12109        assert_eq!(descricao, " Checkout flow.");
12110        assert!(reason.contains("whitespace"), "got: {reason:?}");
12111    }
12112
12113    #[test]
12114    fn validate_descricao_rejects_trailing_whitespace() {
12115        // Paste-from-doc footgun: a trailing ASCII space the bare
12116        // empty-arm gate accepted, the shape predicate now refuses.
12117        let c = caixa_with_descricao(Some("Checkout flow. "));
12118        let err = c.validate_descricao().unwrap_err();
12119        let ManifestError::DescricaoInvalid { descricao, reason } = err else {
12120            panic!("expected DescricaoInvalid, got {err:?}");
12121        };
12122        assert_eq!(descricao, "Checkout flow. ");
12123        assert!(reason.contains("whitespace"), "got: {reason:?}");
12124    }
12125
12126    #[test]
12127    fn validate_descricao_rejects_embedded_newline() {
12128        // Paste-from-multiline-doc footgun: an embedded LF the bare
12129        // empty-arm gate accepted, the shape predicate now refuses.
12130        // Without this gate the embedded newline silently landed in
12131        // the rendered Chart.yaml as a multi-line YAML block scalar,
12132        // and every chart-aware UI (`helm list`, `helm search`,
12133        // Artifact Hub) renders the description in a single-line
12134        // column so the embedded newline is silently dropped at
12135        // every downstream consumer.
12136        let c = caixa_with_descricao(Some("Checkout\nflow."));
12137        let err = c.validate_descricao().unwrap_err();
12138        assert!(
12139            matches!(err, ManifestError::DescricaoInvalid { .. }),
12140            "got {err:?}",
12141        );
12142        assert!(err.to_string().contains("newline"), "got {err}");
12143    }
12144
12145    #[test]
12146    fn validate_descricao_rejects_embedded_carriage_return() {
12147        // Paste-from-Windows-CRLF-doc footgun.
12148        let c = caixa_with_descricao(Some("Checkout\rflow."));
12149        let err = c.validate_descricao().unwrap_err();
12150        assert!(
12151            matches!(err, ManifestError::DescricaoInvalid { .. }),
12152            "got {err:?}",
12153        );
12154        assert!(err.to_string().contains("carriage return"), "got {err}");
12155    }
12156
12157    #[test]
12158    fn validate_descricao_rejects_embedded_tab() {
12159        // Tab-from-aligned-doc footgun.
12160        let c = caixa_with_descricao(Some("Checkout\tflow."));
12161        let err = c.validate_descricao().unwrap_err();
12162        assert!(
12163            matches!(err, ManifestError::DescricaoInvalid { .. }),
12164            "got {err:?}",
12165        );
12166        assert!(err.to_string().contains("tab"), "got {err}");
12167    }
12168
12169    #[test]
12170    fn validate_descricao_rejects_embedded_control_bytes() {
12171        // Paste-from-binary-blob footgun: every other control byte
12172        // (NUL, BEL, ESC, DEL) is refused at validate time. Mirrors
12173        // the peer SPDX-expression control-byte arm.
12174        for s in [
12175            "Checkout\x00flow.",
12176            "Checkout\x07flow.",
12177            "Checkout\x1bflow.",
12178            "Checkout\x7fflow.",
12179        ] {
12180            let c = caixa_with_descricao(Some(s));
12181            let err = c.validate_descricao().unwrap_err();
12182            assert!(
12183                matches!(err, ManifestError::DescricaoInvalid { .. }),
12184                "{s:?} got {err:?}",
12185            );
12186            assert!(
12187                err.to_string().contains("control character"),
12188                "{s:?} got {err}",
12189            );
12190        }
12191    }
12192
12193    #[test]
12194    fn validate_descricao_accepts_unicode_prose() {
12195        // Positive control: Unicode prose is accepted — the
12196        // canonical fixtures carry `→` (U+2192) and `—` (U+2014),
12197        // and `Caixa::template`'s `"FIXME — describe this caixa"`
12198        // scaffold every `feira init` emits must continue to pass.
12199        for s in [
12200            "Canonical Rust→wasm32-wasip2 caixa Servico.",
12201            "FIXME — describe this caixa",
12202            "Caixa pour le projet tâche",
12203            "日本語の説明",
12204        ] {
12205            let c = caixa_with_descricao(Some(s));
12206            c.validate_descricao()
12207                .unwrap_or_else(|err| panic!("Unicode {s:?} must pass: {err:?}"));
12208        }
12209    }
12210
12211    #[test]
12212    fn validate_descricao_empty_takes_precedence_over_shape() {
12213        // Cascade pin: a `Some("")` surfaces the narrower
12214        // `DescricaoEmpty` arm, not the broader `DescricaoInvalid`
12215        // shape-predicate arm. Mirrors the peer
12216        // `validate_licenca_empty_takes_precedence_over_shape` pin
12217        // on the sibling `:licenca` axis.
12218        let c = caixa_with_descricao(Some(""));
12219        let err = c.validate_descricao().unwrap_err();
12220        assert!(matches!(err, ManifestError::DescricaoEmpty), "got {err:?}",);
12221    }
12222
12223    #[test]
12224    fn validate_descricao_invalid_diagnostic_carries_offending_value_and_slot() {
12225        // Diagnostic-shape pin: the error's Display surfaces both
12226        // the `:descricao` slot name and the offending value
12227        // verbatim, so a `feira lint` run can render the diagnostic
12228        // without re-parsing and the author can grep their caixa.lisp
12229        // for the offending `:descricao` line. Mirrors the peer
12230        // `validate_licenca_invalid_diagnostic_carries_offending_value_and_slot`
12231        // pin (ee2e888) on the sibling `:licenca` axis.
12232        // The `{descricao:?}` Debug format escapes embedded control
12233        // bytes; the quoted offending value surfaces as
12234        // `"Checkout\nflow."` (literal backslash-n) in the rendered
12235        // diagnostic. The author can grep their caixa.lisp for the
12236        // literal `Checkout` summary prefix.
12237        let c = caixa_with_descricao(Some("Checkout\nflow."));
12238        let rendered = c.validate_descricao().unwrap_err().to_string();
12239        assert!(
12240            rendered.contains(":descricao"),
12241            "diagnostic must name the offending slot: {rendered}",
12242        );
12243        assert!(
12244            rendered.contains("Checkout\\nflow."),
12245            "diagnostic must quote the offending value (debug-escaped): {rendered}",
12246        );
12247    }
12248
12249    #[test]
12250    fn validate_descricao_template_passes() {
12251        // Round-trip pin: the bare `Caixa::template` shape carries
12252        // `:descricao "FIXME — describe this caixa"` (a non-empty
12253        // sentinel), so the template-derived Caixa passes the gate by
12254        // construction. A future template-shape change that omits or
12255        // empties `:descricao` would surface here as a regression.
12256        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12257        c.validate_descricao().unwrap();
12258    }
12259
12260    #[test]
12261    fn validate_descricao_diagnostic_names_offending_slot() {
12262        // Diagnostic-shape pin (peer with
12263        // `validate_repositorio_diagnostic_carries_offending_value`):
12264        // the error's Display surfaces the `:descricao` slot name
12265        // verbatim, so a `feira lint` run can render the diagnostic
12266        // without re-parsing and the author can grep their caixa.lisp
12267        // for the offending `:descricao` line.
12268        let c = caixa_with_descricao(Some(""));
12269        let rendered = c.validate_descricao().unwrap_err().to_string();
12270        assert!(
12271            rendered.contains(":descricao"),
12272            "diagnostic must name the offending slot: {rendered}",
12273        );
12274    }
12275
12276    // ── validate_licenca — universal-axis chart README license shape ──
12277
12278    fn caixa_with_licenca(licenca: Option<&str>) -> Caixa {
12279        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12280        c.licenca = licenca.map(String::from);
12281        c
12282    }
12283
12284    #[test]
12285    fn validate_licenca_accepts_none() {
12286        // The omit-the-slot identity: `:licenca` is optional. The
12287        // gate is a no-op when the author didn't declare a value —
12288        // every caixa without a `:licenca` line trivially passes,
12289        // and the substrate-side `caixa-helm` renderer falls back to
12290        // the documented `"MIT"` placeholder. Mirrors the peer
12291        // `validate_descricao_accepts_none` posture on the sibling
12292        // `Option<String>` Caixa slot.
12293        let c = caixa_with_licenca(None);
12294        c.validate_licenca().unwrap();
12295    }
12296
12297    #[test]
12298    fn validate_licenca_accepts_canonical_expressions() {
12299        // Positive control: every canonical SPDX expression shape
12300        // pleme-io carries in its existing fixtures + the canonical
12301        // SPDX dual-license / with-exception / `+`-suffix / grouped /
12302        // user-defined-reference shapes all pass the gate. Covers
12303        // the single-license, `OR`-compound, `AND`-compound,
12304        // `WITH`-exception, parenthesis-grouped, `+`-suffix, and
12305        // `LicenseRef-` / `DocumentRef-:LicenseRef-` shapes — every
12306        // production the SPDX 2.1 expression grammar admits that
12307        // sits within the alphabet floor the
12308        // `is_spdx_expression_shape` predicate enforces.
12309        for lic in [
12310            "MIT",
12311            "Apache-2.0",
12312            "Apache-2.0 OR MIT",
12313            "Apache-2.0 AND MIT",
12314            "BSD-3-Clause",
12315            "MPL-2.0",
12316            "GPL-3.0-or-later",
12317            "GPL-2.0+",
12318            "Apache-2.0 WITH LLVM-exception",
12319            "(MIT OR Apache-2.0) AND BSD-3-Clause",
12320            "(MIT OR Apache-2.0) AND BSD-3-Clause AND ISC",
12321            "LicenseRef-MyLicense",
12322            "DocumentRef-spdx-tool:LicenseRef-MIT-Style",
12323            "x",
12324        ] {
12325            let c = caixa_with_licenca(Some(lic));
12326            c.validate_licenca()
12327                .unwrap_or_else(|err| panic!("canonical {lic:?} must pass: {err:?}"));
12328        }
12329    }
12330
12331    #[test]
12332    fn validate_licenca_rejects_trailing_whitespace() {
12333        // Paste-from-doc whitespace footgun. A trailing space in the
12334        // `:licenca` value would silently break a downstream SPDX
12335        // parser that splits on exact `AND` / `OR` / `WITH` keyword
12336        // boundaries. The shape predicate refuses every trailing
12337        // whitespace byte by construction. Peer with
12338        // `validate_repositorio_rejects_whitespace` and
12339        // `validate_edicao_rejects_trailing_whitespace`.
12340        let c = caixa_with_licenca(Some("MIT "));
12341        let err = c.validate_licenca().unwrap_err();
12342        let ManifestError::LicencaInvalid { licenca, .. } = err else {
12343            panic!("expected LicencaInvalid, got {err:?}");
12344        };
12345        assert_eq!(licenca, "MIT ");
12346    }
12347
12348    #[test]
12349    fn validate_licenca_rejects_leading_whitespace() {
12350        // Symmetric paste-from-doc whitespace footgun on the leading
12351        // boundary — the gate refuses every shape that starts with a
12352        // space byte by construction. Peer with
12353        // `validate_edicao_rejects_leading_whitespace`.
12354        let c = caixa_with_licenca(Some(" MIT"));
12355        let err = c.validate_licenca().unwrap_err();
12356        assert!(
12357            matches!(err, ManifestError::LicencaInvalid { .. }),
12358            "got {err:?}",
12359        );
12360    }
12361
12362    #[test]
12363    fn validate_licenca_rejects_control_char() {
12364        // Paste-from-multiline-doc CRLF footgun — control characters
12365        // at the value boundary land as a malformed line in the
12366        // rendered chart `README.md` `## License` section. Peer with
12367        // `validate_repositorio_rejects_control_char` and
12368        // `validate_edicao_rejects_control_char`.
12369        for lic in ["MIT\n", "MIT\r\n", "MIT\rApache-2.0"] {
12370            let c = caixa_with_licenca(Some(lic));
12371            let err = c.validate_licenca().unwrap_err();
12372            assert!(
12373                matches!(err, ManifestError::LicencaInvalid { .. }),
12374                "expected LicencaInvalid on {lic:?}, got {err:?}",
12375            );
12376        }
12377    }
12378
12379    #[test]
12380    fn validate_licenca_rejects_tab() {
12381        // Tab-from-aligned-doc footgun — SPDX expressions use a
12382        // single ASCII space between tokens; a tab breaks every
12383        // downstream SPDX parser that splits on exact `" "`
12384        // boundaries.
12385        let c = caixa_with_licenca(Some("MIT\tOR Apache-2.0"));
12386        let err = c.validate_licenca().unwrap_err();
12387        assert!(
12388            matches!(err, ManifestError::LicencaInvalid { .. }),
12389            "got {err:?}",
12390        );
12391    }
12392
12393    #[test]
12394    fn validate_licenca_rejects_non_ascii() {
12395        // Smart-quote / non-ASCII paste footgun — SPDX identifiers
12396        // are ASCII per the `idstring = 1*(ALPHA / DIGIT / "-" /
12397        // ".")` production. The shape predicate refuses every
12398        // non-ASCII byte by construction; peer with
12399        // `validate_edicao_rejects_non_ascii_lookalike`.
12400        for lic in ["MIT\u{a0}OR Apache-2.0", "MIT\u{2013}1.0", "Café-1.0"] {
12401            let c = caixa_with_licenca(Some(lic));
12402            let err = c.validate_licenca().unwrap_err();
12403            assert!(
12404                matches!(err, ManifestError::LicencaInvalid { .. }),
12405                "expected LicencaInvalid on {lic:?}, got {err:?}",
12406            );
12407        }
12408    }
12409
12410    #[test]
12411    fn validate_licenca_rejects_underscore() {
12412        // Underscore-instead-of-hyphen typo footgun — `Apache_2.0` /
12413        // `MIT_Style` / `BSD_3_Clause` are familiar shapes from
12414        // snake-case identifier conventions that don't apply to the
12415        // SPDX `idstring` grammar (which admits only `ALPHA / DIGIT /
12416        // "-" / "."`). The shape predicate refuses every underscore
12417        // byte by construction.
12418        for lic in ["Apache_2.0", "MIT_Style", "BSD_3_Clause"] {
12419            let c = caixa_with_licenca(Some(lic));
12420            let err = c.validate_licenca().unwrap_err();
12421            assert!(
12422                matches!(err, ManifestError::LicencaInvalid { .. }),
12423                "expected LicencaInvalid on {lic:?}, got {err:?}",
12424            );
12425        }
12426    }
12427
12428    #[test]
12429    fn validate_licenca_rejects_comma_separator() {
12430        // Comma-instead-of-`OR`-keyword colloquial idiom footgun —
12431        // SPDX expressions compose multiple licenses via `AND` / `OR`
12432        // keywords, not the comma separator. The shape predicate
12433        // refuses every comma byte by construction.
12434        for lic in ["MIT, Apache-2.0", "MIT,Apache-2.0"] {
12435            let c = caixa_with_licenca(Some(lic));
12436            let err = c.validate_licenca().unwrap_err();
12437            assert!(
12438                matches!(err, ManifestError::LicencaInvalid { .. }),
12439                "expected LicencaInvalid on {lic:?}, got {err:?}",
12440            );
12441        }
12442    }
12443
12444    #[test]
12445    fn validate_licenca_rejects_slash_dual_license() {
12446        // Slash-dual-license colloquial idiom footgun — the
12447        // `MIT/Apache-2.0` shape is common in Cargo's pre-SPDX
12448        // `package.license` field but non-SPDX; the SPDX equivalent
12449        // is `MIT OR Apache-2.0`. The shape predicate refuses every
12450        // forward-slash byte by construction.
12451        for lic in ["MIT/Apache-2.0", "MIT/BSD-3-Clause"] {
12452            let c = caixa_with_licenca(Some(lic));
12453            let err = c.validate_licenca().unwrap_err();
12454            assert!(
12455                matches!(err, ManifestError::LicencaInvalid { .. }),
12456                "expected LicencaInvalid on {lic:?}, got {err:?}",
12457            );
12458        }
12459    }
12460
12461    #[test]
12462    fn validate_licenca_rejects_semicolon_separator() {
12463        // Semicolon-list-separator confusion footgun — adjacent to
12464        // the comma-separator idiom, every list-separator-belongs-
12465        // to-list-grammar confusion lands here.
12466        let c = caixa_with_licenca(Some("MIT; Apache-2.0"));
12467        let err = c.validate_licenca().unwrap_err();
12468        assert!(
12469            matches!(err, ManifestError::LicencaInvalid { .. }),
12470            "got {err:?}",
12471        );
12472    }
12473
12474    #[test]
12475    fn validate_licenca_empty_takes_precedence_over_shape() {
12476        // Empty-first cascade pin: the empty `Some("")` surfaces the
12477        // narrower `LicencaEmpty` not the shape-predicate-wrapped
12478        // `LicencaInvalid`, mirroring the peer
12479        // `validate_edicao_empty_takes_precedence_over_shape` and
12480        // `validate_repositorio_empty_takes_precedence_over_shape`
12481        // (`RepositorioEmpty` → `RepositorioInvalid`), `NomeEmpty` →
12482        // `NomeInvalid`, `VersaoEmpty` → `VersaoInvalid` cascades.
12483        // The shape predicate also refuses the empty input
12484        // (defensively — `"must not be empty"`), but the manifest-
12485        // layer empty arm runs first to surface the narrower
12486        // diagnostic verbatim.
12487        let c = caixa_with_licenca(Some(""));
12488        let err = c.validate_licenca().unwrap_err();
12489        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12490    }
12491
12492    #[test]
12493    fn validate_licenca_invalid_diagnostic_carries_offending_value() {
12494        // Diagnostic-shape pin on the shape-predicate arm (peer with
12495        // `validate_edicao_invalid_diagnostic_carries_offending_value`
12496        // and `validate_repositorio_diagnostic_carries_offending_value`):
12497        // the error's Display surfaces the offending value + slot
12498        // name verbatim, so a `feira lint` run can render the
12499        // diagnostic without re-parsing and the author can grep
12500        // their caixa.lisp for the offending `:licenca` value.
12501        let c = caixa_with_licenca(Some("Apache_2.0"));
12502        let rendered = c.validate_licenca().unwrap_err().to_string();
12503        assert!(
12504            rendered.contains(":licenca"),
12505            "diagnostic must name the offending slot: {rendered}",
12506        );
12507        assert!(
12508            rendered.contains("Apache_2.0"),
12509            "diagnostic must quote the offending value: {rendered}",
12510        );
12511    }
12512
12513    #[test]
12514    fn validate_licenca_rejects_empty_some() {
12515        // Canonical paste-from-blank-doc footgun. Without this gate
12516        // the empty `Some("")` silently passed the renderer's
12517        // `Option::unwrap_or_else(|| "MIT".into())` (which only
12518        // fires on `None`) and landed as a bare trailing period in
12519        // the rendered chart `README.md` `## License` section.
12520        // Mirrors the peer [`ManifestError::DescricaoEmpty`] empty-
12521        // arm on the sibling `Option<String>` Caixa slot.
12522        let c = caixa_with_licenca(Some(""));
12523        let err = c.validate_licenca().unwrap_err();
12524        assert!(matches!(err, ManifestError::LicencaEmpty), "got {err:?}",);
12525    }
12526
12527    #[test]
12528    fn validate_licenca_template_passes() {
12529        // Round-trip pin: the bare `Caixa::template` shape (whether
12530        // it carries `:licenca` or omits it) passes the gate by
12531        // construction. A future template-shape change that
12532        // introduced `(:licenca "")` would surface here as a
12533        // regression. Mirrors the peer
12534        // `validate_descricao_template_passes` pin.
12535        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
12536        c.validate_licenca().unwrap();
12537    }
12538
12539    #[test]
12540    fn validate_licenca_diagnostic_names_offending_slot() {
12541        // Diagnostic-shape pin (peer with
12542        // `validate_descricao_diagnostic_names_offending_slot`):
12543        // the error's Display surfaces the `:licenca` slot name
12544        // verbatim, so a `feira lint` run can render the diagnostic
12545        // without re-parsing and the author can grep their caixa.lisp
12546        // for the offending `:licenca` line.
12547        let c = caixa_with_licenca(Some(""));
12548        let rendered = c.validate_licenca().unwrap_err().to_string();
12549        assert!(
12550            rendered.contains(":licenca"),
12551            "diagnostic must name the offending slot: {rendered}",
12552        );
12553    }
12554
12555    // ── Caixa::licenca — outer top-level Option<&str> scalar accessor ──
12556
12557    #[test]
12558    fn licenca_returns_licenca_byte_string_verbatim_across_permutations() {
12559        // The canonical per-`Caixa` `:licenca` SPDX-expression scalar
12560        // pin: [`Caixa::licenca`] must return the `:licenca` typed
12561        // byte-string verbatim as an `Option<&str>`, byte-equal to the
12562        // raw `self.licenca.as_deref()` access across every
12563        // representative value in the accept-set — `None` (the "omit
12564        // the slot to defer to the caixa-helm renderer's `MIT`
12565        // fallback" arm every existing fixture without a `:licenca`
12566        // line carries), `Some("")` (a past-the-guard sentinel that
12567        // pins the accessor doesn't perform a silent
12568        // `Some("") → None` collapse on the empty arm — validate
12569        // rejects `Some("")` through `LicencaEmpty` but the accessor
12570        // must ship the raw slot verbatim so a validate-time gate
12571        // regression surfaces at the caixa-helm emit boundary rather
12572        // than being silently absorbed into the fallback), `Some("MIT")`
12573        // (the canonical single-license shape every `feira init`
12574        // template scaffolds), `Some("Apache-2.0 OR MIT")` (the
12575        // canonical `OR`-compound shape the peer
12576        // `validate_licenca_accepts_canonical_expressions` positive
12577        // sweep exercises), `Some("(MIT OR Apache-2.0) AND
12578        // BSD-3-Clause")` (the canonical parenthesis-grouped shape),
12579        // `Some("MIT ")` / `Some(" MIT")` / `Some("MIT\n")` /
12580        // `Some("Apache_2.0")` / `Some("MIT,Apache-2.0")` (past-the-
12581        // guard sentinels — validate rejects each through
12582        // `LicencaInvalid` but the accessor must ship the raw slot
12583        // verbatim).
12584        //
12585        // First outer top-level [`Caixa`] `Option<&str>`-return scalar
12586        // accessor pin on the substrate primitive — opens the "outer
12587        // [`Caixa`] `Option<&str>` scalar" projection pattern the
12588        // sibling per-`Caixa` `:descricao` / `:repositorio` / `:edicao`
12589        // future lifts fold on. Sibling in shape to the peer per-`:placement`
12590        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12591        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12592        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12593        // axes, extended onto the outer top-level [`Caixa`] universal-
12594        // axis surface. Pins against a future silent detour that
12595        // returned an owned `Option<String>` (which would type-check
12596        // but silently allocate on every accessor call, breaking the
12597        // zero-cost projection every peer sibling accessor carries), a
12598        // `Some("") → None` collapse (which would silently absorb the
12599        // `LicencaEmpty` refusal case at the accessor boundary and the
12600        // caixa-helm emit path would silently fall back to `"MIT"` on
12601        // a struct-literal `Caixa { licenca: Some(""), .. }`), or a
12602        // `None → Some("MIT")` collapse (which would silently reify
12603        // the caixa-helm renderer's `"MIT"` fallback at the accessor
12604        // boundary and every downstream consumer keying off the
12605        // `Option::is_none()` discriminator would lose the "author
12606        // omitted the slot" signal).
12607        for licenca in [
12608            None,
12609            Some(""),
12610            Some("MIT"),
12611            Some("Apache-2.0 OR MIT"),
12612            Some("(MIT OR Apache-2.0) AND BSD-3-Clause"),
12613            Some("MIT "),
12614            Some(" MIT"),
12615            Some("MIT\n"),
12616            Some("Apache_2.0"),
12617            Some("MIT,Apache-2.0"),
12618        ] {
12619            let c = caixa_with_licenca(licenca);
12620            assert_eq!(
12621                c.licenca(),
12622                licenca,
12623                "Caixa::licenca must return :licenca verbatim (got {:?}, \
12624                 expected {licenca:?})",
12625                c.licenca(),
12626            );
12627            assert_eq!(
12628                c.licenca(),
12629                c.licenca.as_deref(),
12630                "Caixa::licenca must byte-equal the raw \
12631                 `self.licenca.as_deref()` field access across every \
12632                 value in the Option<&str> accept-set",
12633            );
12634        }
12635    }
12636
12637    #[test]
12638    fn validate_licenca_empty_arm_routes_through_accessor() {
12639        // Composition pin: [`Caixa::validate_licenca`]'s empty-arm gate
12640        // must key off [`Caixa::licenca`], not the raw
12641        // `self.licenca.as_deref()` field access. Structurally: a
12642        // `Caixa { licenca: Some(""), .. }` must surface the
12643        // `LicencaEmpty` refusal exactly, and a
12644        // `Caixa { licenca: Some("MIT"), .. }` (the canonical
12645        // single-license form) must pass validate. The pair jointly
12646        // pins the accessor + validate-gate composition: any future
12647        // silent detour that had the accessor return `None` on the
12648        // empty arm (a `.filter(|s| !s.is_empty())` collapse) would
12649        // silently absorb the `LicencaEmpty` refusal at the accessor
12650        // boundary and the validate gate would accept a struct-literal
12651        // `Caixa { licenca: Some(""), .. }` — the composition pin
12652        // catches that at caixa-core build time.
12653        //
12654        // Peer of the per-`:politicas :circuit-breaker`
12655        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
12656        // accessor-composition pin
12657        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
12658        // on the sibling per-M3-mesh-slot required-`u32` axis — same
12659        // "the validate / shape-gate predicate must route through the
12660        // substrate-primitive typed dispatch" discipline extended onto
12661        // the outer top-level [`Caixa`] universal-axis
12662        // `Option<&str>`-composition surface.
12663        let c = caixa_with_licenca(Some(""));
12664        assert!(
12665            matches!(c.validate_licenca(), Err(ManifestError::LicencaEmpty)),
12666            "validate_licenca must reject licenca == Some(\"\") with \
12667             LicencaEmpty — the accessor and the validate gate must \
12668             route through the same substrate-primitive typed dispatch \
12669             on the :licenca empty arm",
12670        );
12671        let c = caixa_with_licenca(Some("MIT"));
12672        assert!(
12673            c.validate_licenca().is_ok(),
12674            "validate_licenca must accept licenca == Some(\"MIT\") \
12675             (the canonical single-license SPDX shape)",
12676        );
12677    }
12678
12679    #[test]
12680    fn licenca_projects_option_str_by_borrow() {
12681        // The by-borrow pin: [`Caixa::licenca`] returns
12682        // `Option<&str>` by borrow — the `&str` borrows the underlying
12683        // `String` storage of the `Option<String>` slot and the
12684        // accessor must not allocate a fresh `String` on every call.
12685        // Peer of the per-`:placement`
12686        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
12687        // borrow pin on the peer per-M3-mesh-slot
12688        // `Option<&str>`-return axis, extended onto the outer top-
12689        // level [`Caixa`] universal-axis `Option<&str>` shape — the
12690        // accessor's returned `&str` must borrow from `&self` (the
12691        // returned reference's lifetime is tied to `&self`), and
12692        // calling the accessor twice on the same [`Caixa`] must yield
12693        // the same `Option<&str>` verbatim (idempotent, no side
12694        // effects on `&self`).
12695        //
12696        // Pins against a future silent detour that returned an owned
12697        // `Option<String>` (which would type-check but silently
12698        // allocate on every call, breaking the zero-cost projection
12699        // every peer sibling accessor carries), or a one-arm-only
12700        // accessor that returned a saturating value on some sentinel
12701        // input (breaking the pass-through invariant the sibling
12702        // required-scalar accessors carry).
12703        for licenca in [None, Some(""), Some("MIT"), Some("Apache-2.0 OR MIT")] {
12704            let c = caixa_with_licenca(licenca);
12705            let first = c.licenca();
12706            let second = c.licenca();
12707            assert_eq!(
12708                first, second,
12709                "Caixa::licenca must be idempotent — two successive \
12710                 calls on the same &self must return the same \
12711                 Option<&str>",
12712            );
12713            assert_eq!(
12714                first, licenca,
12715                "Caixa::licenca must return :licenca verbatim by \
12716                 borrow — got {first:?}, expected {licenca:?}",
12717            );
12718        }
12719    }
12720
12721    // ── Caixa::repositorio — outer top-level Option<&str> scalar accessor ──
12722
12723    #[test]
12724    fn repositorio_returns_repositorio_byte_string_verbatim_across_permutations() {
12725        // The canonical per-`Caixa` `:repositorio` git-repo-URL scalar
12726        // pin: [`Caixa::repositorio`] must return the `:repositorio`
12727        // typed byte-string verbatim as an `Option<&str>`, byte-equal
12728        // to the raw `self.repositorio.as_deref()` access across every
12729        // representative value in the accept-set — `None` (the "omit
12730        // the slot to defer to the per-renderer placeholder" arm every
12731        // existing fixture without a `:repositorio` line carries),
12732        // `Some("")` (a past-the-guard sentinel that pins the accessor
12733        // doesn't perform a silent `Some("") → None` collapse on the
12734        // empty arm — validate rejects `Some("")` through
12735        // `RepositorioEmpty` but the accessor must ship the raw slot
12736        // verbatim so a validate-time gate regression surfaces at the
12737        // caixa-helm / caixa-flux emit boundary rather than being
12738        // silently absorbed into the per-renderer fallback),
12739        // `Some("github:pleme-io/hello-rio")` (the canonical `github:`
12740        // shorthand every existing manifest fixture across
12741        // `caixa-helm` / `caixa-mesh` and the `examples/` uses),
12742        // `Some("https://github.com/pleme-io/checkout")` (the canonical
12743        // `https://` URL the README quickstart uses),
12744        // `Some("ssh://git@github.com/pleme-io/checkout.git")` /
12745        // `Some("git://github.com/pleme-io/checkout.git")` /
12746        // `Some("git@github.com:pleme-io/checkout.git")` /
12747        // `Some("file:///opt/mirrors/pleme-io/checkout")` (every non-
12748        // github scheme the shared `is_git_repo_url` predicate
12749        // documents), and five past-the-guard sentinels for the
12750        // `RepositorioInvalid` refusal cases (`Some("pleme-io/checkout")`
12751        // missing-colon, `Some("-upload-pack=evil")` leading-dash, /
12752        // `Some("github:pleme-io/checkout?ref=main")` query-string, /
12753        // `Some("github:pleme-io/checkout#main")` fragment-anchor, /
12754        // `Some("github:pleme-io/{tpl}")` URI-template-placeholder — the
12755        // sentinels pin the accessor doesn't silently absorb the
12756        // refusal cases into a fallback).
12757        //
12758        // Second outer top-level [`Caixa`] `Option<&str>`-return scalar
12759        // accessor pin on the substrate primitive — sibling of the peer
12760        // [`Caixa::licenca`] (6d5bc28) pin
12761        // (`licenca_returns_licenca_byte_string_verbatim_across_permutations`)
12762        // that opened the "outer [`Caixa`] `Option<&str>` scalar"
12763        // projection pin pattern this pin folds on. Sibling in shape to
12764        // the peer per-`:placement`
12765        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
12766        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
12767        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
12768        // axes, extended onto the outer top-level [`Caixa`] universal-
12769        // axis surface. Pins against a future silent detour that
12770        // returned an owned `Option<String>` (which would type-check
12771        // but silently allocate on every accessor call, breaking the
12772        // zero-cost projection every peer sibling accessor carries), a
12773        // `Some("") → None` collapse (which would silently absorb the
12774        // `RepositorioEmpty` refusal case at the accessor boundary and
12775        // the caixa-helm `Chart.yaml` `home:` fold would silently
12776        // render a `home: null` / omitted field on a struct-literal
12777        // `Caixa { repositorio: Some(""), .. }`), or a
12778        // `None → Some(<default>)` collapse (which would silently reify
12779        // the per-renderer fallback at the accessor boundary and every
12780        // downstream consumer keying off the `Option::is_none()`
12781        // discriminator would lose the "author omitted the slot"
12782        // signal).
12783        for repositorio in [
12784            None,
12785            Some(""),
12786            Some("github:pleme-io/hello-rio"),
12787            Some("https://github.com/pleme-io/checkout"),
12788            Some("ssh://git@github.com/pleme-io/checkout.git"),
12789            Some("git://github.com/pleme-io/checkout.git"),
12790            Some("git@github.com:pleme-io/checkout.git"),
12791            Some("file:///opt/mirrors/pleme-io/checkout"),
12792            Some("pleme-io/checkout"),
12793            Some("-upload-pack=evil"),
12794            Some("github:pleme-io/checkout?ref=main"),
12795            Some("github:pleme-io/checkout#main"),
12796            Some("github:pleme-io/{tpl}"),
12797        ] {
12798            let c = caixa_with_repositorio(repositorio);
12799            assert_eq!(
12800                c.repositorio(),
12801                repositorio,
12802                "Caixa::repositorio must return :repositorio verbatim \
12803                 (got {:?}, expected {repositorio:?})",
12804                c.repositorio(),
12805            );
12806            assert_eq!(
12807                c.repositorio(),
12808                c.repositorio.as_deref(),
12809                "Caixa::repositorio must byte-equal the raw \
12810                 `self.repositorio.as_deref()` field access across every \
12811                 value in the Option<&str> accept-set",
12812            );
12813        }
12814    }
12815
12816    #[test]
12817    fn validate_repositorio_empty_arm_routes_through_accessor() {
12818        // Composition pin: [`Caixa::validate_repositorio`]'s empty-arm
12819        // gate must key off [`Caixa::repositorio`], not the raw
12820        // `self.repositorio.as_deref()` field access. Structurally: a
12821        // `Caixa { repositorio: Some(""), .. }` must surface the
12822        // `RepositorioEmpty` refusal exactly, and a
12823        // `Caixa { repositorio: Some("github:pleme-io/hello-rio"), .. }`
12824        // (the canonical `github:` shorthand form) must pass validate.
12825        // The pair jointly pins the accessor + validate-gate
12826        // composition: any future silent detour that had the accessor
12827        // return `None` on the empty arm (a `.filter(|s| !s.is_empty())`
12828        // collapse) would silently absorb the `RepositorioEmpty` refusal
12829        // at the accessor boundary and the validate gate would accept a
12830        // struct-literal `Caixa { repositorio: Some(""), .. }` — the
12831        // composition pin catches that at caixa-core build time.
12832        //
12833        // Peer of the [`Caixa::licenca`] (6d5bc28)
12834        // `validate_licenca_empty_arm_routes_through_accessor`
12835        // composition pin on the sibling outer top-level [`Caixa`]
12836        // `Option<&str>` universal-axis surface — same "the validate /
12837        // shape-gate predicate must route through the substrate-
12838        // primitive typed dispatch" discipline extended onto the second
12839        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
12840        // composition surface.
12841        let c = caixa_with_repositorio(Some(""));
12842        assert!(
12843            matches!(
12844                c.validate_repositorio(),
12845                Err(ManifestError::RepositorioEmpty),
12846            ),
12847            "validate_repositorio must reject repositorio == Some(\"\") \
12848             with RepositorioEmpty — the accessor and the validate gate \
12849             must route through the same substrate-primitive typed \
12850             dispatch on the :repositorio empty arm",
12851        );
12852        let c = caixa_with_repositorio(Some("github:pleme-io/hello-rio"));
12853        assert!(
12854            c.validate_repositorio().is_ok(),
12855            "validate_repositorio must accept repositorio == \
12856             Some(\"github:pleme-io/hello-rio\") (the canonical \
12857             `github:` shorthand git-repo-URL shape)",
12858        );
12859    }
12860
12861    #[test]
12862    fn repositorio_projects_option_str_by_borrow() {
12863        // The by-borrow pin: [`Caixa::repositorio`] returns
12864        // `Option<&str>` by borrow — the `&str` borrows the underlying
12865        // `String` storage of the `Option<String>` slot and the
12866        // accessor must not allocate a fresh `String` on every call.
12867        // Peer of the per-`:placement`
12868        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) and the
12869        // [`Caixa::licenca`] (6d5bc28) by-borrow pins on the peer
12870        // `Option<&str>`-return axes, extended onto the second outer
12871        // top-level [`Caixa`] universal-axis `Option<&str>` shape —
12872        // the accessor's returned `&str` must borrow from `&self` (the
12873        // returned reference's lifetime is tied to `&self`), and
12874        // calling the accessor twice on the same [`Caixa`] must yield
12875        // the same `Option<&str>` verbatim (idempotent, no side effects
12876        // on `&self`).
12877        //
12878        // Pins against a future silent detour that returned an owned
12879        // `Option<String>` (which would type-check but silently
12880        // allocate on every call, breaking the zero-cost projection
12881        // every peer sibling accessor carries), or a one-arm-only
12882        // accessor that returned a saturating value on some sentinel
12883        // input (breaking the pass-through invariant the sibling
12884        // required-scalar accessors carry).
12885        for repositorio in [
12886            None,
12887            Some(""),
12888            Some("github:pleme-io/hello-rio"),
12889            Some("https://github.com/pleme-io/checkout"),
12890        ] {
12891            let c = caixa_with_repositorio(repositorio);
12892            let first = c.repositorio();
12893            let second = c.repositorio();
12894            assert_eq!(
12895                first, second,
12896                "Caixa::repositorio must be idempotent — two successive \
12897                 calls on the same &self must return the same \
12898                 Option<&str>",
12899            );
12900            assert_eq!(
12901                first, repositorio,
12902                "Caixa::repositorio must return :repositorio verbatim by \
12903                 borrow — got {first:?}, expected {repositorio:?}",
12904            );
12905        }
12906    }
12907
12908    // ── Caixa::canonical_git_url — resolved-git-URL composer ──────────
12909
12910    #[test]
12911    fn canonical_git_url_returns_repositorio_verbatim_on_some_arm() {
12912        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] must
12913        // return the author-declared `:repositorio` byte-string verbatim
12914        // on the `Some` arm — no scheme rewrite, no trailing-slash
12915        // canonicalization, no `github:` → `https://github.com/`
12916        // desugaring. The resolved-URL composer is the projection of
12917        // the raw [`Caixa::repositorio`] `Option<&str>` accessor onto
12918        // the `String`-return arity every substrate-side field-fill
12919        // consumer keys off; on the `Some` arm the projection is
12920        // `str::to_owned` verbatim, so every accept-set value the
12921        // sibling `repositorio_returns_repositorio_byte_string_verbatim_
12922        // across_permutations` pin covers (`https://…`, `github:…`,
12923        // `ssh://…`, `git://…`, `git@…`, `file://…`, and the past-the-
12924        // guard sentinel `pleme-io/…`) must survive the accessor
12925        // byte-equal. Pins against a future silent detour that rewrote
12926        // the `github:` shorthand to the `https://github.com/` full URL
12927        // at the accessor boundary (which would silently split the
12928        // resolved-URL surface from the raw [`Caixa::repositorio`]
12929        // accessor's documented pass-through invariant), or a trailing-
12930        // slash normalization (which would silently break the
12931        // FluxCD `GitRepository` `spec.url` byte-exact match every
12932        // downstream consumer keys the source-controller reconcile off).
12933        for repositorio in [
12934            "github:pleme-io/hello-rio",
12935            "https://github.com/pleme-io/checkout",
12936            "ssh://git@github.com/pleme-io/checkout.git",
12937            "git://github.com/pleme-io/checkout.git",
12938            "git@github.com:pleme-io/checkout.git",
12939            "file:///opt/mirrors/pleme-io/checkout",
12940        ] {
12941            let c = caixa_with_repositorio(Some(repositorio));
12942            assert_eq!(
12943                c.canonical_git_url(),
12944                repositorio,
12945                "Caixa::canonical_git_url on the Some arm must return \
12946                 :repositorio verbatim (got {:?}, expected {repositorio:?})",
12947                c.canonical_git_url(),
12948            );
12949        }
12950    }
12951
12952    #[test]
12953    fn canonical_git_url_falls_back_to_pleme_org_url_on_none_arm() {
12954        // Fail-before-pass-after pin: [`Caixa::canonical_git_url`] on the
12955        // `None` arm must emit the substrate's canonical pleme-org github
12956        // URL derived from `caixa.nome()` — `https://github.com/<org>/
12957        // <nome>` with `<org>` bound to [`crate::DEFAULT_PLEME_GIT_ORG`]
12958        // and `<nome>` bound to the typed [`Caixa::nome`] accessor. This
12959        // is the exact byte-image of the prior inline
12960        // [`caixa-flux::ClusterBundleOpts::for_caixa`] `git_url`
12961        // composer at caixa-flux/src/lib.rs:2080 that every prior caller
12962        // re-derived open-coded. Pins against a future silent detour
12963        // that migrated the `<org>` segment to a different constant (a
12964        // fork rebranding that split off a new
12965        // `DEFAULT_PLEME_GIT_ORG_MIRROR` const the accessor would need
12966        // to migrate onto), a scheme change (`https://` → `git://` or
12967        // `ssh://`), or a per-`Caixa` `.canonical_git_url_prefix`
12968        // override (which would break the substrate-wide single-source-
12969        // of-truth guarantee this method encodes).
12970        let c = caixa_with_repositorio(None);
12971        let expected = format!(
12972            "https://github.com/{org}/{nome}",
12973            org = crate::DEFAULT_PLEME_GIT_ORG,
12974            nome = c.nome(),
12975        );
12976        assert_eq!(
12977            c.canonical_git_url(),
12978            expected,
12979            "Caixa::canonical_git_url on the None arm must fold through \
12980             the substrate's canonical pleme-org github URL fallback \
12981             `https://github.com/<DEFAULT_PLEME_GIT_ORG>/<nome>` — got \
12982             {:?}, expected {expected:?}",
12983            c.canonical_git_url(),
12984        );
12985    }
12986
12987    #[test]
12988    fn canonical_git_url_byte_matches_manual_composition() {
12989        // Byte-parity pin: [`Caixa::canonical_git_url`] must render
12990        // byte-identically to the manual open-coded
12991        // `caixa.repositorio().map(str::to_owned).unwrap_or_else(||
12992        //  format!("https://github.com/{org}/{nome}", ...))` composition
12993        // every prior substrate-side caller re-derived. Guards the
12994        // paired-site convergence just applied at caixa-flux's
12995        // [`ClusterBundleOpts::for_caixa`] `git_url` composer (which
12996        // now routes through this accessor): a future implementation of
12997        // this method that reordered the format arguments, swapped the
12998        // `<org>` constant for a different one, or interposed a
12999        // canonicalization pass on the `Some` arm surfaces here as a
13000        // caixa-core build-time test failure rather than as a downstream
13001        // FluxCD `GitRepository` reconcile mismatch far from this
13002        // method's source.
13003        for repositorio in [
13004            None,
13005            Some("github:pleme-io/hello-rio"),
13006            Some("https://github.com/pleme-io/checkout"),
13007            Some("ssh://git@github.com/pleme-io/checkout.git"),
13008        ] {
13009            let c = caixa_with_repositorio(repositorio);
13010            let manual = c.repositorio().map_or_else(
13011                || {
13012                    format!(
13013                        "https://github.com/{org}/{nome}",
13014                        org = crate::DEFAULT_PLEME_GIT_ORG,
13015                        nome = c.nome(),
13016                    )
13017                },
13018                str::to_owned,
13019            );
13020            assert_eq!(
13021                c.canonical_git_url(),
13022                manual,
13023                "Caixa::canonical_git_url must byte-equal the manual \
13024                 open-coded `repositorio().map(str::to_owned)\
13025                 .unwrap_or_else(|| format!(...))` composition across \
13026                 every representative :repositorio input — got {:?}, \
13027                 expected {manual:?}",
13028                c.canonical_git_url(),
13029            );
13030        }
13031    }
13032
13033    // ── Caixa::publish_tag — resolved-publish-tag composer ───────────
13034
13035    #[test]
13036    fn publish_tag_composes_prefix_and_versao_on_all_shapes() {
13037        // Fail-before-pass-after pin: [`Caixa::publish_tag`] must compose
13038        // [`crate::DEFAULT_PUBLISH_TAG_PREFIX`] against the caixa's typed
13039        // [`Caixa::versao`] byte-string across every SemVer-2 shape the
13040        // sibling [`validate_versao_accepts_canonical_forms`] positive-set
13041        // sweep documents — bare MAJOR.MINOR.PATCH, pre-release tags
13042        // (`-rc.1`), build metadata (`+build.42`), the combined form, and
13043        // the `0.0.0` boundary case. Every accept-set value the peer
13044        // validate gate lets through must survive the resolved-tag
13045        // projection byte-equal.
13046        for versao in [
13047            "0.1.0",
13048            "0.0.0",
13049            "1.0.0",
13050            "1.2.3-rc.1",
13051            "1.2.3+build.42",
13052            "1.2.3-rc.1+build.42",
13053        ] {
13054            let c = caixa_with_versao(versao);
13055            let expected = format!(
13056                "{prefix}{versao}",
13057                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13058            );
13059            assert_eq!(
13060                c.publish_tag(),
13061                expected,
13062                "Caixa::publish_tag must compose \
13063                 DEFAULT_PUBLISH_TAG_PREFIX ({prefix:?}) against \
13064                 :versao ({versao:?}) verbatim — got {got:?}, \
13065                 expected {expected:?}",
13066                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13067                got = c.publish_tag(),
13068            );
13069        }
13070    }
13071
13072    #[test]
13073    fn publish_tag_starts_with_default_publish_tag_prefix() {
13074        // Prefix-shape pin: every [`Caixa::publish_tag`] emission must
13075        // begin with the canonical [`crate::DEFAULT_PUBLISH_TAG_PREFIX`]
13076        // byte-string on every input, guarding a hypothetical future
13077        // implementation that migrated the prefix segment to an inline
13078        // literal (`"v"`) that would silently drift from any rebrand of
13079        // the lifted constant. Peer to the sibling caixa-flux
13080        // `cluster_bundle_default_git_tag_uses_lifted_caixa_core_prefix`
13081        // test which pins the same prefix invariant at the reader-side
13082        // `GitRefSpec::Tag` emit site.
13083        for versao in ["0.0.0", "0.1.0", "1.2.3-rc.1", "9.9.9+build.1"] {
13084            let c = caixa_with_versao(versao);
13085            let tag = c.publish_tag();
13086            assert!(
13087                tag.starts_with(crate::DEFAULT_PUBLISH_TAG_PREFIX),
13088                "Caixa::publish_tag emission {tag:?} must start with \
13089                 the lifted crate::DEFAULT_PUBLISH_TAG_PREFIX \
13090                 ({prefix:?})",
13091                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13092            );
13093        }
13094    }
13095
13096    #[test]
13097    fn publish_tag_byte_matches_manual_composition() {
13098        // Byte-parity pin: [`Caixa::publish_tag`] must render byte-
13099        // identically to the manual open-coded
13100        // `format!("{prefix}{versao}", prefix =
13101        //  caixa_core::DEFAULT_PUBLISH_TAG_PREFIX, versao =
13102        //  caixa.versao())` composition every prior substrate-side
13103        // caller re-derived. Guards the paired-site convergence just
13104        // applied at caixa-flux's [`ClusterBundleOpts::for_caixa`]
13105        // `git_ref` composer (which now routes through this accessor):
13106        // a future implementation of this method that reordered the
13107        // format arguments, swapped the `<prefix>` constant for a
13108        // different one, or interposed a canonicalization pass on the
13109        // `:versao` axis surfaces here as a caixa-core build-time test
13110        // failure rather than as a downstream FluxCD `GitRepository`
13111        // reconcile mismatch far from this method's source.
13112        for versao in [
13113            "0.1.0",
13114            "0.0.0",
13115            "1.2.3-rc.1",
13116            "1.2.3+build.42",
13117            "1.2.3-rc.1+build.42",
13118        ] {
13119            let c = caixa_with_versao(versao);
13120            let manual = format!(
13121                "{prefix}{versao}",
13122                prefix = crate::DEFAULT_PUBLISH_TAG_PREFIX,
13123                versao = c.versao(),
13124            );
13125            assert_eq!(
13126                c.publish_tag(),
13127                manual,
13128                "Caixa::publish_tag must byte-equal the manual \
13129                 open-coded `format!(\"{{prefix}}{{versao}}\", ...)` \
13130                 composition across every representative :versao input \
13131                 — got {got:?}, expected {manual:?}",
13132                got = c.publish_tag(),
13133            );
13134        }
13135    }
13136
13137    // ── Caixa::lareira_chart_name — resolved-chart-name composer ─────
13138
13139    #[test]
13140    fn lareira_chart_name_composes_prefix_and_nome_on_all_shapes() {
13141        // Fail-before-pass-after pin: [`Caixa::lareira_chart_name`] must
13142        // compose [`crate::LAREIRA_CHART_NAME_PREFIX`] against the caixa's
13143        // typed [`Caixa::nome`] byte-string across every DNS-1123 shape
13144        // the sibling [`validate_nome_accepts_canonical_forms`] positive-
13145        // set sweep documents — single-word, hyphen-joined, version-
13146        // suffixed, single-char, two-char, digit-start, retry-suffixed.
13147        // Every accept-set value the peer validate gate lets through must
13148        // survive the resolved-chart-name projection byte-equal.
13149        for nome in [
13150            "checkout",
13151            "cart-v2",
13152            "a",
13153            "db",
13154            "3rd-party-shim",
13155            "payment-retry",
13156            "0",
13157        ] {
13158            let c = caixa_with_nome(nome);
13159            let expected = format!("{prefix}{nome}", prefix = crate::LAREIRA_CHART_NAME_PREFIX);
13160            assert_eq!(
13161                c.lareira_chart_name(),
13162                expected,
13163                "Caixa::lareira_chart_name must compose \
13164                 LAREIRA_CHART_NAME_PREFIX ({prefix:?}) against \
13165                 :nome ({nome:?}) verbatim — got {got:?}, \
13166                 expected {expected:?}",
13167                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
13168                got = c.lareira_chart_name(),
13169            );
13170        }
13171    }
13172
13173    #[test]
13174    fn lareira_chart_name_starts_with_lifted_prefix() {
13175        // Prefix-shape pin: every [`Caixa::lareira_chart_name`] emission
13176        // must begin with the canonical
13177        // [`crate::LAREIRA_CHART_NAME_PREFIX`] byte-string on every
13178        // input, guarding a hypothetical future implementation that
13179        // migrated the prefix segment to an inline literal (`"lareira-"`)
13180        // that would silently drift from any rebrand of the lifted
13181        // constant. Peer to the sibling
13182        // [`publish_tag_starts_with_default_publish_tag_prefix`] pin on
13183        // the co-resident resolved-publish-tag composer's prefix axis.
13184        for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13185            let c = caixa_with_nome(nome);
13186            let chart = c.lareira_chart_name();
13187            assert!(
13188                chart.starts_with(crate::LAREIRA_CHART_NAME_PREFIX),
13189                "Caixa::lareira_chart_name emission {chart:?} must start \
13190                 with the lifted crate::LAREIRA_CHART_NAME_PREFIX \
13191                 ({prefix:?})",
13192                prefix = crate::LAREIRA_CHART_NAME_PREFIX,
13193            );
13194        }
13195    }
13196
13197    #[test]
13198    fn lareira_chart_name_byte_matches_canonical_helper_composition() {
13199        // Byte-parity pin: [`Caixa::lareira_chart_name`] must render
13200        // byte-identically to the manual open-coded
13201        // `caixa_core::lareira_chart_name(caixa.nome())` two-step
13202        // composition every prior substrate-side caller re-derived.
13203        // Guards the paired-site convergence just applied at caixa-helm's
13204        // [`render_chart_for_servico_with`] `ChartDir.name` composer,
13205        // caixa-flux's [`cluster_bundle`] per-CR `chart_name` binding,
13206        // and caixa-tatara's [`process_for_aplicacao`] `release_name`
13207        // composer (all of which now route through this accessor): a
13208        // future implementation of this method that reordered the
13209        // composition arguments, swapped the `<prefix>` constant for a
13210        // different one, or interposed a canonicalization pass on the
13211        // `:nome` axis surfaces here as a caixa-core build-time test
13212        // failure rather than as a downstream Helm chart-render / FluxCD
13213        // reconcile / tatara Process-CR mismatch far from this method's
13214        // source.
13215        for nome in [
13216            "checkout",
13217            "cart-v2",
13218            "a",
13219            "db",
13220            "3rd-party-shim",
13221            "payment-retry",
13222        ] {
13223            let c = caixa_with_nome(nome);
13224            let manual = crate::lareira_chart_name(c.nome());
13225            assert_eq!(
13226                c.lareira_chart_name(),
13227                manual,
13228                "Caixa::lareira_chart_name must byte-equal the manual \
13229                 open-coded `caixa_core::lareira_chart_name(caixa.nome())` \
13230                 composition across every representative :nome input — \
13231                 got {got:?}, expected {manual:?}",
13232                got = c.lareira_chart_name(),
13233            );
13234        }
13235    }
13236
13237    // ── Caixa::oci_chart_ref — resolved-OCI-chart-ref composer ────────
13238
13239    #[test]
13240    fn oci_chart_ref_composes_scheme_and_lareira_chart_name_on_all_shapes() {
13241        // Fail-before-pass-after pin: [`Caixa::oci_chart_ref`] must
13242        // compose [`crate::OCI_SCHEME_PREFIX`] + the caller-supplied
13243        // `registry` + [`crate::lareira_chart_name`]-of-[`Caixa::nome`]
13244        // across the full paired `(registry, :nome)` accept-set — every
13245        // representative registry the substrate-side emitters carry
13246        // (`ghcr.io/pleme-io/charts`, the canonical CAIXA-SDLC §II
13247        // ArtifactHub-tier registry; `ghcr.io/pleme-io`, the bare-org
13248        // arm the sibling `oci_chart_ref_pins_byte_shape_against_prior_
13249        // inline_format` render-side pin exercises; `registry.example.
13250        // com`, an off-org shape; `localhost:5000`, the local-dev shape
13251        // every `feira chart` iteration path lands under) × every DNS-
13252        // 1123 `:nome` shape the peer `validate_nome_accepts_canonical_
13253        // forms` positive-set sweep documents (single-word, hyphen-
13254        // joined, single-char, two-char, digit-start, retry-suffixed).
13255        // Every accept-set pair the peer validate gates let through must
13256        // survive the resolved-OCI-ref projection byte-equal.
13257        for registry in [
13258            "ghcr.io/pleme-io/charts",
13259            "ghcr.io/pleme-io",
13260            "registry.example.com",
13261            "localhost:5000",
13262        ] {
13263            for nome in [
13264                "checkout",
13265                "cart-v2",
13266                "a",
13267                "db",
13268                "3rd-party-shim",
13269                "payment-retry",
13270                "0",
13271            ] {
13272                let c = caixa_with_nome(nome);
13273                let expected = format!(
13274                    "{scheme}{registry}/{chart}",
13275                    scheme = crate::OCI_SCHEME_PREFIX,
13276                    chart = crate::lareira_chart_name(nome),
13277                );
13278                assert_eq!(
13279                    c.oci_chart_ref(registry),
13280                    expected,
13281                    "Caixa::oci_chart_ref must compose \
13282                     OCI_SCHEME_PREFIX ({scheme:?}) + registry ({registry:?}) + \
13283                     lareira_chart_name(:nome ({nome:?})) verbatim — got {got:?}, \
13284                     expected {expected:?}",
13285                    scheme = crate::OCI_SCHEME_PREFIX,
13286                    got = c.oci_chart_ref(registry),
13287                );
13288            }
13289        }
13290    }
13291
13292    #[test]
13293    fn oci_chart_ref_starts_with_lifted_scheme_prefix() {
13294        // Scheme-prefix-shape pin: every [`Caixa::oci_chart_ref`]
13295        // emission must begin with the canonical
13296        // [`crate::OCI_SCHEME_PREFIX`] byte-string on every input, guarding
13297        // a hypothetical future implementation that migrated the scheme
13298        // segment to an inline literal (`"oci://"`) that would silently
13299        // drift from any rebrand of the lifted constant. Peer to the
13300        // sibling [`publish_tag_starts_with_default_publish_tag_prefix`]
13301        // + [`lareira_chart_name_starts_with_lifted_prefix`] pins on the
13302        // co-resident resolved-publish-tag / resolved-chart-name
13303        // composers' prefix axes.
13304        for registry in [
13305            "ghcr.io/pleme-io/charts",
13306            "ghcr.io/pleme-io",
13307            "localhost:5000",
13308        ] {
13309            for nome in ["checkout", "cart", "a", "payment-retry", "0"] {
13310                let c = caixa_with_nome(nome);
13311                let ref_ = c.oci_chart_ref(registry);
13312                assert!(
13313                    ref_.starts_with(crate::OCI_SCHEME_PREFIX),
13314                    "Caixa::oci_chart_ref emission {ref_:?} must start \
13315                     with the lifted crate::OCI_SCHEME_PREFIX ({scheme:?}) \
13316                     — registry ({registry:?}), :nome ({nome:?})",
13317                    scheme = crate::OCI_SCHEME_PREFIX,
13318                );
13319            }
13320        }
13321    }
13322
13323    #[test]
13324    fn oci_chart_ref_byte_matches_canonical_helper_composition() {
13325        // Byte-parity pin: [`Caixa::oci_chart_ref`] must render byte-
13326        // identically to the manual open-coded
13327        // `caixa_core::oci_chart_ref(registry, caixa.nome())` two-step
13328        // composition every prior substrate-side caller re-derived.
13329        // Guards the paired-site convergence just applied at caixa-
13330        // tatara's [`derive_chart_ref`] helper (which now routes through
13331        // this accessor): a future implementation of this method that
13332        // reordered the composition arguments, swapped the `<scheme>`
13333        // constant for a different one, migrated the `<chart>` segment
13334        // off the paired [`crate::lareira_chart_name`] composer, or
13335        // interposed a canonicalization pass on either input axis
13336        // surfaces here as a caixa-core build-time test failure rather
13337        // than as a downstream `helm install` / FluxCD OCI-source
13338        // reconcile / tatara `Process`-CR mismatch far from this
13339        // method's source. Sibling to the peer
13340        // [`lareira_chart_name_byte_matches_canonical_helper_composition`]
13341        // / [`publish_tag_byte_matches_manual_composition`] /
13342        // [`canonical_git_url_byte_matches_manual_composition`] byte-
13343        // parity pins that carry the same discipline on the co-resident
13344        // resolved-chart-name / resolved-publish-tag / resolved-git-URL
13345        // composers.
13346        for registry in [
13347            "ghcr.io/pleme-io/charts",
13348            "ghcr.io/pleme-io",
13349            "registry.example.com",
13350            "localhost:5000",
13351        ] {
13352            for nome in [
13353                "checkout",
13354                "cart-v2",
13355                "a",
13356                "db",
13357                "3rd-party-shim",
13358                "payment-retry",
13359            ] {
13360                let c = caixa_with_nome(nome);
13361                let manual = crate::oci_chart_ref(registry, c.nome());
13362                assert_eq!(
13363                    c.oci_chart_ref(registry),
13364                    manual,
13365                    "Caixa::oci_chart_ref must byte-equal the manual \
13366                     open-coded `caixa_core::oci_chart_ref(registry, \
13367                     caixa.nome())` composition across every representative \
13368                     (registry, :nome) pair — registry ({registry:?}), \
13369                     :nome ({nome:?}), got {got:?}, expected {manual:?}",
13370                    got = c.oci_chart_ref(registry),
13371                );
13372            }
13373        }
13374    }
13375
13376    // ── Caixa::descricao — outer top-level Option<&str> scalar accessor ──
13377
13378    #[test]
13379    fn descricao_returns_descricao_byte_string_verbatim_across_permutations() {
13380        // The canonical per-`Caixa` `:descricao` free-form-prose scalar
13381        // pin: [`Caixa::descricao`] must return the `:descricao` typed
13382        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13383        // raw `self.descricao.as_deref()` access across every
13384        // representative value in the accept-set — `None` (the "omit
13385        // the slot to defer to the per-renderer `caixa.nome`-derived
13386        // fallback" arm every existing fixture without a `:descricao`
13387        // line carries), `Some("")` (a past-the-guard sentinel that
13388        // pins the accessor doesn't perform a silent `Some("") → None`
13389        // collapse on the empty arm — validate rejects `Some("")`
13390        // through `DescricaoEmpty` but the accessor must ship the raw
13391        // slot verbatim so a validate-time gate regression surfaces at
13392        // the caixa-helm / caixa-feira emit boundary rather than being
13393        // silently absorbed into the per-renderer `caixa.nome`-derived
13394        // fallback), `Some("Checkout flow.")` (the canonical one-line
13395        // prose descriptor the peer
13396        // `validate_descricao_accepts_canonical_value` positive sweep
13397        // exercises), `Some("Canonical Rust→wasm32-wasip2 caixa
13398        // Servico.")` (the multi-byte Unicode continuation-byte shape
13399        // the `hello-rio` fixture carries), `Some("→ — · ✓")` (a
13400        // multi-glyph Unicode shape the peer
13401        // `is_chart_description_shape` predicate accepts), and five
13402        // past-the-guard sentinels for the `DescricaoInvalid` refusal
13403        // cases (`Some(" Checkout flow.")` leading-whitespace,
13404        // `Some("Checkout flow. ")` trailing-whitespace,
13405        // `Some("Checkout\nflow.")` embedded-LF,
13406        // `Some("Checkout\tflow.")` embedded-TAB, and
13407        // `Some("Checkout\x00flow.")` embedded-NUL — the sentinels pin
13408        // the accessor doesn't silently absorb the refusal cases into
13409        // a fallback).
13410        //
13411        // Third outer top-level [`Caixa`] `Option<&str>`-return scalar
13412        // accessor pin on the substrate primitive — sibling of the peer
13413        // [`Caixa::licenca`] (6d5bc28) and [`Caixa::repositorio`]
13414        // (cc7332d) pins that opened the "outer [`Caixa`]
13415        // `Option<&str>` scalar" projection pin pattern this pin folds
13416        // on. Sibling in shape to the peer per-`:placement`
13417        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13418        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13419        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13420        // axes, extended onto the outer top-level [`Caixa`] universal-
13421        // axis surface. Pins against a future silent detour that
13422        // returned an owned `Option<String>` (which would type-check
13423        // but silently allocate on every accessor call, breaking the
13424        // zero-cost projection every peer sibling accessor carries), a
13425        // `Some("") → None` collapse (which would silently absorb the
13426        // `DescricaoEmpty` refusal case at the accessor boundary and
13427        // the caixa-helm `Chart.yaml` `description:` fold would
13428        // silently render a `caixa.nome`-derived fallback on a
13429        // struct-literal `Caixa { descricao: Some(""), .. }`), or a
13430        // `None → Some(<default>)` collapse (which would silently
13431        // reify the per-renderer `caixa.nome`-derived fallback at the
13432        // accessor boundary and every downstream consumer keying off
13433        // the `Option::is_none()` discriminator would lose the "author
13434        // omitted the slot" signal).
13435        for descricao in [
13436            None,
13437            Some(""),
13438            Some("Checkout flow."),
13439            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13440            Some("→ — · ✓"),
13441            Some(" Checkout flow."),
13442            Some("Checkout flow. "),
13443            Some("Checkout\nflow."),
13444            Some("Checkout\tflow."),
13445            Some("Checkout\x00flow."),
13446        ] {
13447            let c = caixa_with_descricao(descricao);
13448            assert_eq!(
13449                c.descricao(),
13450                descricao,
13451                "Caixa::descricao must return :descricao verbatim (got \
13452                 {:?}, expected {descricao:?})",
13453                c.descricao(),
13454            );
13455            assert_eq!(
13456                c.descricao(),
13457                c.descricao.as_deref(),
13458                "Caixa::descricao must byte-equal the raw \
13459                 `self.descricao.as_deref()` field access across every \
13460                 value in the Option<&str> accept-set",
13461            );
13462        }
13463    }
13464
13465    #[test]
13466    fn validate_descricao_empty_arm_routes_through_accessor() {
13467        // Composition pin: [`Caixa::validate_descricao`]'s empty-arm
13468        // gate must key off [`Caixa::descricao`], not the raw
13469        // `self.descricao.as_deref()` field access. Structurally: a
13470        // `Caixa { descricao: Some(""), .. }` must surface the
13471        // `DescricaoEmpty` refusal exactly, and a
13472        // `Caixa { descricao: Some("Checkout flow."), .. }` (the
13473        // canonical one-line-prose form) must pass validate. The pair
13474        // jointly pins the accessor + validate-gate composition: any
13475        // future silent detour that had the accessor return `None` on
13476        // the empty arm (a `.filter(|s| !s.is_empty())` collapse) would
13477        // silently absorb the `DescricaoEmpty` refusal at the accessor
13478        // boundary and the validate gate would accept a struct-literal
13479        // `Caixa { descricao: Some(""), .. }` — the composition pin
13480        // catches that at caixa-core build time.
13481        //
13482        // Peer of the [`Caixa::licenca`] (6d5bc28)
13483        // `validate_licenca_empty_arm_routes_through_accessor` and
13484        // [`Caixa::repositorio`] (cc7332d)
13485        // `validate_repositorio_empty_arm_routes_through_accessor`
13486        // composition pins on the sibling outer top-level [`Caixa`]
13487        // `Option<&str>` universal-axis surface — same "the validate /
13488        // shape-gate predicate must route through the substrate-
13489        // primitive typed dispatch" discipline extended onto the third
13490        // outer top-level [`Caixa`] universal-axis `Option<&str>`-
13491        // composition surface.
13492        let c = caixa_with_descricao(Some(""));
13493        assert!(
13494            matches!(c.validate_descricao(), Err(ManifestError::DescricaoEmpty),),
13495            "validate_descricao must reject descricao == Some(\"\") \
13496             with DescricaoEmpty — the accessor and the validate gate \
13497             must route through the same substrate-primitive typed \
13498             dispatch on the :descricao empty arm",
13499        );
13500        let c = caixa_with_descricao(Some("Checkout flow."));
13501        assert!(
13502            c.validate_descricao().is_ok(),
13503            "validate_descricao must accept descricao == \
13504             Some(\"Checkout flow.\") (the canonical one-line-prose \
13505             chart-description shape)",
13506        );
13507    }
13508
13509    #[test]
13510    fn descricao_projects_option_str_by_borrow() {
13511        // The by-borrow pin: [`Caixa::descricao`] returns
13512        // `Option<&str>` by borrow — the `&str` borrows the underlying
13513        // `String` storage of the `Option<String>` slot and the
13514        // accessor must not allocate a fresh `String` on every call.
13515        // Peer of the [`Caixa::licenca`] (6d5bc28) and
13516        // [`Caixa::repositorio`] (cc7332d) by-borrow pins on the peer
13517        // outer top-level [`Caixa`] `Option<&str>`-return axes, and of
13518        // the per-`:placement`
13519        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13520        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13521        // return axis, extended onto the third outer top-level
13522        // [`Caixa`] universal-axis `Option<&str>` shape — the
13523        // accessor's returned `&str` must borrow from `&self` (the
13524        // returned reference's lifetime is tied to `&self`), and
13525        // calling the accessor twice on the same [`Caixa`] must yield
13526        // the same `Option<&str>` verbatim (idempotent, no side
13527        // effects on `&self`).
13528        //
13529        // Pins against a future silent detour that returned an owned
13530        // `Option<String>` (which would type-check but silently
13531        // allocate on every call, breaking the zero-cost projection
13532        // every peer sibling accessor carries), or a one-arm-only
13533        // accessor that returned a saturating value on some sentinel
13534        // input (breaking the pass-through invariant the sibling
13535        // required-scalar accessors carry).
13536        for descricao in [
13537            None,
13538            Some(""),
13539            Some("Checkout flow."),
13540            Some("Canonical Rust→wasm32-wasip2 caixa Servico."),
13541        ] {
13542            let c = caixa_with_descricao(descricao);
13543            let first = c.descricao();
13544            let second = c.descricao();
13545            assert_eq!(
13546                first, second,
13547                "Caixa::descricao must be idempotent — two successive \
13548                 calls on the same &self must return the same \
13549                 Option<&str>",
13550            );
13551            assert_eq!(
13552                first, descricao,
13553                "Caixa::descricao must return :descricao verbatim by \
13554                 borrow — got {first:?}, expected {descricao:?}",
13555            );
13556        }
13557    }
13558
13559    // ── validate_edicao — universal-axis language-edition shape ──
13560
13561    fn caixa_with_edicao(edicao: Option<&str>) -> Caixa {
13562        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13563        c.edicao = edicao.map(String::from);
13564        c
13565    }
13566
13567    #[test]
13568    fn validate_edicao_accepts_none() {
13569        // The omit-the-slot identity: `:edicao` is optional. The
13570        // gate is a no-op when the author didn't declare a value —
13571        // every caixa without an `:edicao` line trivially passes,
13572        // and the substrate-side build pipeline falls back to the
13573        // documented default edition. Mirrors the peer
13574        // `validate_licenca_accepts_none` posture on the sibling
13575        // `Option<String>` Caixa slot.
13576        let c = caixa_with_edicao(None);
13577        c.validate_edicao().unwrap();
13578    }
13579
13580    #[test]
13581    fn validate_edicao_accepts_canonical_value() {
13582        // Positive control: the canonical `"2026"` edition every
13583        // existing renderer-side fixture (`caixa-helm`, `caixa-flux`,
13584        // `caixa-mesh`) carries by construction passes the gate.
13585        // Future-introduced sibling editions (`"2027"`, `"2030"`,
13586        // `"2049"`) that match the same 4-digit ASCII decimal year
13587        // shape must also trivially pass — the structural shape
13588        // predicate accepts every well-formed year regardless of
13589        // whether the substrate yet understands the specific value
13590        // (a future known-edition allowlist tightens that).
13591        for ed in ["2026", "2027", "2030", "2049"] {
13592            let c = caixa_with_edicao(Some(ed));
13593            c.validate_edicao()
13594                .unwrap_or_else(|err| panic!("canonical {ed:?} must pass: {err:?}"));
13595        }
13596    }
13597
13598    #[test]
13599    fn validate_edicao_rejects_empty_some() {
13600        // Canonical paste-from-blank-doc footgun. Without this gate
13601        // the empty `Some("")` silently lands as `(:edicao "")` in
13602        // the rendered caixa.lisp and a future renderer-side
13603        // consumer's `Option::unwrap_or_else` (which only fires on
13604        // `None`) skips its fallback. Mirrors the peer
13605        // [`ManifestError::LicencaEmpty`] empty-arm on the sibling
13606        // `Option<String>` Caixa slot.
13607        let c = caixa_with_edicao(Some(""));
13608        let err = c.validate_edicao().unwrap_err();
13609        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13610    }
13611
13612    #[test]
13613    fn validate_edicao_rejects_free_form_non_year() {
13614        // Free-form non-year footgun: the bare `"x"` / `"latest"` /
13615        // `"nightly"` shapes carry no operational meaning on the
13616        // substrate's build-time edition selector. Until this gate
13617        // landed the bare empty-arm check let every such value
13618        // through and broke far from the source caixa.lisp. Peer
13619        // with the shape-predicate cascade
13620        // `validate_repositorio_rejects_missing_colon_separator`
13621        // establishes past its own empty arm.
13622        for ed in ["x", "latest", "nightly", "stable"] {
13623            let c = caixa_with_edicao(Some(ed));
13624            let err = c.validate_edicao().unwrap_err();
13625            assert!(
13626                matches!(err, ManifestError::EdicaoInvalid { .. }),
13627                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13628            );
13629        }
13630    }
13631
13632    #[test]
13633    fn validate_edicao_rejects_trailing_whitespace() {
13634        // Paste-from-doc whitespace footgun. A trailing space in
13635        // the `:edicao` value would silently break the substrate's
13636        // build-time edition match-table lookup at the rendered
13637        // artifact's edition-selector consumer. The shape predicate
13638        // refuses every whitespace byte by construction (any byte
13639        // outside `0-9` fails `is_ascii_digit`). Peer with
13640        // `validate_repositorio_rejects_whitespace`.
13641        let c = caixa_with_edicao(Some("2026 "));
13642        let err = c.validate_edicao().unwrap_err();
13643        let ManifestError::EdicaoInvalid { edicao, .. } = err else {
13644            panic!("expected EdicaoInvalid, got {err:?}");
13645        };
13646        assert_eq!(edicao, "2026 ");
13647    }
13648
13649    #[test]
13650    fn validate_edicao_rejects_leading_whitespace() {
13651        // Symmetric paste-from-doc whitespace footgun on the leading
13652        // boundary — the gate refuses every shape with a non-digit
13653        // byte by construction.
13654        let c = caixa_with_edicao(Some(" 2026"));
13655        let err = c.validate_edicao().unwrap_err();
13656        assert!(
13657            matches!(err, ManifestError::EdicaoInvalid { .. }),
13658            "got {err:?}",
13659        );
13660    }
13661
13662    #[test]
13663    fn validate_edicao_rejects_control_char() {
13664        // Paste-from-multiline-doc CRLF footgun — control characters
13665        // at the value boundary break the substrate's build-time
13666        // edition-selector parser. Peer with
13667        // `validate_repositorio_rejects_control_char`.
13668        let c = caixa_with_edicao(Some("2026\n"));
13669        let err = c.validate_edicao().unwrap_err();
13670        assert!(
13671            matches!(err, ManifestError::EdicaoInvalid { .. }),
13672            "got {err:?}",
13673        );
13674    }
13675
13676    #[test]
13677    fn validate_edicao_rejects_non_ascii_lookalike() {
13678        // Fullwidth-keyboard look-alike footgun — `"2026"` is
13679        // the U+FF12 U+FF10 U+FF12 U+FF16 sequence (CJK fullwidth
13680        // digits), 4 codepoints but 12 UTF-8 bytes; the substrate's
13681        // edition selector wants an ASCII year, and the gate
13682        // refuses every non-ASCII shape by construction (length in
13683        // bytes is 12 ≠ 4, *and* every byte falls outside
13684        // `is_ascii_digit`'s `0-9` range).
13685        let c = caixa_with_edicao(Some("2026"));
13686        let err = c.validate_edicao().unwrap_err();
13687        assert!(
13688            matches!(err, ManifestError::EdicaoInvalid { .. }),
13689            "got {err:?}",
13690        );
13691    }
13692
13693    #[test]
13694    fn validate_edicao_rejects_version_tag_prefix() {
13695        // Common version-tag idiom footgun — `"v2026"` / `"e2026"`
13696        // / `"r2026"` are familiar shapes from git-tag / Rust
13697        // edition / release-tag conventions that don't apply to
13698        // the year-shaped edition axis. The shape predicate refuses
13699        // every leading non-digit prefix.
13700        for ed in ["v2026", "e2026", "r2026"] {
13701            let c = caixa_with_edicao(Some(ed));
13702            let err = c.validate_edicao().unwrap_err();
13703            assert!(
13704                matches!(err, ManifestError::EdicaoInvalid { .. }),
13705                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13706            );
13707        }
13708    }
13709
13710    #[test]
13711    fn validate_edicao_rejects_decimal_shape() {
13712        // Decimal-shaped pseudo-version footgun — `"2026.1"` /
13713        // `"2026.0"` are familiar shapes from semver / float
13714        // conventions that don't apply to the year-shaped edition
13715        // axis. The shape predicate refuses every non-digit byte
13716        // (`.` falls outside `is_ascii_digit`).
13717        for ed in ["2026.1", "2026.0", "2026.0.1"] {
13718            let c = caixa_with_edicao(Some(ed));
13719            let err = c.validate_edicao().unwrap_err();
13720            assert!(
13721                matches!(err, ManifestError::EdicaoInvalid { .. }),
13722                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13723            );
13724        }
13725    }
13726
13727    #[test]
13728    fn validate_edicao_rejects_wrong_length_numeric() {
13729        // Wrong-length numeric footgun — `"26"` (truncated) /
13730        // `"202"` (truncated) / `"20260"` (extra digit) / `"00026"`
13731        // (zero-padded too wide) all parse as integers but don't
13732        // name a 4-digit year. The shape predicate refuses every
13733        // value whose length isn't exactly 4 bytes.
13734        for ed in ["26", "202", "20260", "00026", "9"] {
13735            let c = caixa_with_edicao(Some(ed));
13736            let err = c.validate_edicao().unwrap_err();
13737            assert!(
13738                matches!(err, ManifestError::EdicaoInvalid { .. }),
13739                "expected EdicaoInvalid on {ed:?}, got {err:?}",
13740            );
13741        }
13742    }
13743
13744    #[test]
13745    fn validate_edicao_empty_takes_precedence_over_shape() {
13746        // Empty-first cascade pin: the empty `Some("")` surfaces
13747        // the narrower `EdicaoEmpty` not the shape-predicate-
13748        // wrapped `EdicaoInvalid`, mirroring the peer
13749        // `validate_repositorio_empty_takes_precedence_over_shape`
13750        // (`RepositorioEmpty` → `RepositorioInvalid`),
13751        // `NomeEmpty` → `NomeInvalid`, `VersaoEmpty` →
13752        // `VersaoInvalid`, `FonteRepoEmpty` → `FonteRepoInvalid`
13753        // cascades. The shape predicate also refuses the empty
13754        // input (defensively — `s.len() != 4`), but the
13755        // manifest-layer empty arm runs first to surface the
13756        // narrower diagnostic verbatim.
13757        let c = caixa_with_edicao(Some(""));
13758        let err = c.validate_edicao().unwrap_err();
13759        assert!(matches!(err, ManifestError::EdicaoEmpty), "got {err:?}",);
13760    }
13761
13762    #[test]
13763    fn validate_edicao_template_passes() {
13764        // Round-trip pin: the bare `Caixa::template` shape (which
13765        // carries `:edicao "2026"` verbatim) passes the gate by
13766        // construction. A future template-shape change that
13767        // introduced `(:edicao "")` or a non-year value would
13768        // surface here as a regression. Mirrors the peer
13769        // `validate_licenca_template_passes` pin.
13770        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
13771        c.validate_edicao().unwrap();
13772    }
13773
13774    #[test]
13775    fn validate_edicao_diagnostic_names_offending_slot() {
13776        // Diagnostic-shape pin (peer with
13777        // `validate_licenca_diagnostic_names_offending_slot`): the
13778        // error's Display surfaces the `:edicao` slot name verbatim,
13779        // so a `feira lint` run can render the diagnostic without
13780        // re-parsing and the author can grep their caixa.lisp for
13781        // the offending `:edicao` line.
13782        let c = caixa_with_edicao(Some(""));
13783        let rendered = c.validate_edicao().unwrap_err().to_string();
13784        assert!(
13785            rendered.contains(":edicao"),
13786            "diagnostic must name the offending slot: {rendered}",
13787        );
13788    }
13789
13790    #[test]
13791    fn validate_edicao_invalid_diagnostic_carries_offending_value() {
13792        // Diagnostic-shape pin on the shape-predicate arm (peer
13793        // with `validate_repositorio_diagnostic_carries_offending_value`):
13794        // the error's Display surfaces the offending value + slot
13795        // name verbatim, so a `feira lint` run can render the
13796        // diagnostic without re-parsing and the author can grep
13797        // their caixa.lisp for the offending `:edicao` value.
13798        let c = caixa_with_edicao(Some("v2026"));
13799        let rendered = c.validate_edicao().unwrap_err().to_string();
13800        assert!(
13801            rendered.contains(":edicao"),
13802            "diagnostic must name the offending slot: {rendered}",
13803        );
13804        assert!(
13805            rendered.contains("v2026"),
13806            "diagnostic must quote the offending value: {rendered}",
13807        );
13808    }
13809
13810    // ── Caixa::edicao — outer top-level Option<&str> scalar accessor ──
13811
13812    #[test]
13813    fn edicao_returns_edicao_byte_string_verbatim_across_permutations() {
13814        // The canonical per-`Caixa` `:edicao` language-edition scalar
13815        // pin: [`Caixa::edicao`] must return the `:edicao` typed
13816        // byte-string verbatim as an `Option<&str>`, byte-equal to the
13817        // raw `self.edicao.as_deref()` access across every representative
13818        // value in the accept-set — `None` (the "omit the slot to defer
13819        // to the substrate's default edition" arm every existing
13820        // [`caixa-resolver`] fixture without an `:edicao` line carries),
13821        // `Some("")` (a past-the-guard sentinel that pins the accessor
13822        // doesn't perform a silent `Some("") → None` collapse on the
13823        // empty arm — validate rejects `Some("")` through `EdicaoEmpty`
13824        // but the accessor must ship the raw slot verbatim so a
13825        // validate-time gate regression surfaces at any future edition-
13826        // aware consumer's boundary rather than being silently absorbed
13827        // into the substrate's default edition), `Some("2026")` (the
13828        // canonical 4-digit-ASCII-decimal-year shape every `feira init`
13829        // template scaffolds via [`Caixa::template`] and every
13830        // renderer-side fixture at `caixa-helm/src/lib.rs:978` /
13831        // `caixa-flux/src/lib.rs:2319` / `caixa-mesh/src/lib.rs:3208`
13832        // carries by construction), `Some("2018")` / `Some("2021")` /
13833        // `Some("2024")` (canonical 4-digit-ASCII-decimal-year shapes
13834        // peer with Cargo's `[package] edition` grammar every future-
13835        // introduced sibling to `"2026"` will follow), and eight
13836        // past-the-guard sentinels for the `EdicaoInvalid` refusal cases
13837        // (`Some("2026 ")` trailing-whitespace, `Some(" 2026")` leading-
13838        // whitespace, `Some("2026\n")` embedded-LF, `Some("2026")`
13839        // fullwidth-non-ASCII-lookalike, `Some("v2026")` version-tag-
13840        // prefix, `Some("2026.1")` decimal-shape, `Some("26")` wrong-
13841        // length-numeric, `Some("latest")` free-form-non-year — the
13842        // sentinels pin the accessor doesn't silently absorb the
13843        // refusal cases into a substrate-default-edition fallback).
13844        //
13845        // Fourth and final outer top-level [`Caixa`] `Option<&str>`-
13846        // return scalar accessor pin on the substrate primitive —
13847        // sibling of the peer [`Caixa::licenca`] (6d5bc28),
13848        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13849        // (3f16e2f) pins that opened the "outer [`Caixa`]
13850        // `Option<&str>` scalar" projection pin pattern this pin folds
13851        // on. Sibling in shape to the peer per-`:placement`
13852        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) /
13853        // [`crate::aplicacao::Placement::affinity`] (74ec2d3) accessor
13854        // pins on the sibling per-M3-mesh-slot `Option<&str>`-return
13855        // axes, extended onto the outer top-level [`Caixa`] universal-
13856        // axis surface's last unlifted `Option<String>` slot. Pins
13857        // against a future silent detour that returned an owned
13858        // `Option<String>` (which would type-check but silently
13859        // allocate on every accessor call, breaking the zero-cost
13860        // projection every peer sibling accessor carries), a
13861        // `Some("") → None` collapse (which would silently absorb the
13862        // `EdicaoEmpty` refusal case at the accessor boundary and any
13863        // future edition-aware consumer would silently fall back to
13864        // the substrate's default edition on a struct-literal
13865        // `Caixa { edicao: Some(""), .. }`), or a
13866        // `None → Some("2026")` collapse (which would silently reify
13867        // the substrate's default edition at the accessor boundary
13868        // and every downstream consumer keying off the
13869        // `Option::is_none()` discriminator would lose the "author
13870        // omitted the slot" signal).
13871        for edicao in [
13872            None,
13873            Some(""),
13874            Some("2026"),
13875            Some("2018"),
13876            Some("2021"),
13877            Some("2024"),
13878            Some("2026 "),
13879            Some(" 2026"),
13880            Some("2026\n"),
13881            Some("2026"),
13882            Some("v2026"),
13883            Some("2026.1"),
13884            Some("26"),
13885            Some("latest"),
13886        ] {
13887            let c = caixa_with_edicao(edicao);
13888            assert_eq!(
13889                c.edicao(),
13890                edicao,
13891                "Caixa::edicao must return :edicao verbatim (got {:?}, \
13892                 expected {edicao:?})",
13893                c.edicao(),
13894            );
13895            assert_eq!(
13896                c.edicao(),
13897                c.edicao.as_deref(),
13898                "Caixa::edicao must byte-equal the raw \
13899                 `self.edicao.as_deref()` field access across every \
13900                 value in the Option<&str> accept-set",
13901            );
13902        }
13903    }
13904
13905    #[test]
13906    fn validate_edicao_empty_arm_routes_through_accessor() {
13907        // Composition pin: [`Caixa::validate_edicao`]'s empty-arm gate
13908        // must key off [`Caixa::edicao`], not the raw
13909        // `self.edicao.as_deref()` field access. Structurally: a
13910        // `Caixa { edicao: Some(""), .. }` must surface the
13911        // `EdicaoEmpty` refusal exactly, and a
13912        // `Caixa { edicao: Some("2026"), .. }` (the canonical
13913        // 4-digit-ASCII-decimal-year form) must pass validate. The
13914        // pair jointly pins the accessor + validate-gate composition:
13915        // any future silent detour that had the accessor return `None`
13916        // on the empty arm (a `.filter(|s| !s.is_empty())` collapse)
13917        // would silently absorb the `EdicaoEmpty` refusal at the
13918        // accessor boundary and the validate gate would accept a
13919        // struct-literal `Caixa { edicao: Some(""), .. }` — the
13920        // composition pin catches that at caixa-core build time.
13921        //
13922        // Peer of the [`Caixa::licenca`] (6d5bc28)
13923        // `validate_licenca_empty_arm_routes_through_accessor`,
13924        // [`Caixa::repositorio`] (cc7332d)
13925        // `validate_repositorio_empty_arm_routes_through_accessor`,
13926        // and [`Caixa::descricao`] (3f16e2f)
13927        // `validate_descricao_empty_arm_routes_through_accessor`
13928        // composition pins on the sibling outer top-level [`Caixa`]
13929        // `Option<&str>` universal-axis surface — same "the validate /
13930        // shape-gate predicate must route through the substrate-
13931        // primitive typed dispatch" discipline extended onto the
13932        // fourth and final outer top-level [`Caixa`] universal-axis
13933        // `Option<&str>`-composition surface, closing the accessor-
13934        // composition family.
13935        let c = caixa_with_edicao(Some(""));
13936        assert!(
13937            matches!(c.validate_edicao(), Err(ManifestError::EdicaoEmpty)),
13938            "validate_edicao must reject edicao == Some(\"\") with \
13939             EdicaoEmpty — the accessor and the validate gate must \
13940             route through the same substrate-primitive typed dispatch \
13941             on the :edicao empty arm",
13942        );
13943        let c = caixa_with_edicao(Some("2026"));
13944        assert!(
13945            c.validate_edicao().is_ok(),
13946            "validate_edicao must accept edicao == Some(\"2026\") \
13947             (the canonical 4-digit-ASCII-decimal-year shape)",
13948        );
13949    }
13950
13951    #[test]
13952    fn edicao_projects_option_str_by_borrow() {
13953        // The by-borrow pin: [`Caixa::edicao`] returns
13954        // `Option<&str>` by borrow — the `&str` borrows the underlying
13955        // `String` storage of the `Option<String>` slot and the
13956        // accessor must not allocate a fresh `String` on every call.
13957        // Peer of the [`Caixa::licenca`] (6d5bc28),
13958        // [`Caixa::repositorio`] (cc7332d), and [`Caixa::descricao`]
13959        // (3f16e2f) by-borrow pins on the peer outer top-level
13960        // [`Caixa`] `Option<&str>`-return axes, and of the
13961        // per-`:placement`
13962        // [`crate::aplicacao::Placement::shard_key`] (7cd2a28) by-
13963        // borrow pin on the peer per-M3-mesh-slot `Option<&str>`-
13964        // return axis, extended onto the fourth and final outer top-
13965        // level [`Caixa`] universal-axis `Option<&str>` shape — the
13966        // accessor's returned `&str` must borrow from `&self` (the
13967        // returned reference's lifetime is tied to `&self`), and
13968        // calling the accessor twice on the same [`Caixa`] must yield
13969        // the same `Option<&str>` verbatim (idempotent, no side
13970        // effects on `&self`).
13971        //
13972        // Pins against a future silent detour that returned an owned
13973        // `Option<String>` (which would type-check but silently
13974        // allocate on every call, breaking the zero-cost projection
13975        // every peer sibling accessor carries), or a one-arm-only
13976        // accessor that returned a saturating value on some sentinel
13977        // input (breaking the pass-through invariant the sibling
13978        // required-scalar accessors carry).
13979        for edicao in [None, Some(""), Some("2026"), Some("2018")] {
13980            let c = caixa_with_edicao(edicao);
13981            let first = c.edicao();
13982            let second = c.edicao();
13983            assert_eq!(
13984                first, second,
13985                "Caixa::edicao must be idempotent — two successive \
13986                 calls on the same &self must return the same \
13987                 Option<&str>",
13988            );
13989            assert_eq!(
13990                first, edicao,
13991                "Caixa::edicao must return :edicao verbatim by \
13992                 borrow — got {first:?}, expected {edicao:?}",
13993            );
13994        }
13995    }
13996
13997    #[test]
13998    fn nome_returns_nome_byte_string_verbatim_across_permutations() {
13999        // The canonical per-`Caixa` `:nome` universal-axis DNS-1123-
14000        // label caixa-identity scalar pin: [`Caixa::nome`] must return
14001        // the `:nome` typed `String` verbatim as `&str`, byte-equal to
14002        // the raw field access across every representative value in
14003        // the accept-set — the canonical `"demo"` template baseline
14004        // (the same `feira init`-scaffolded default the sibling
14005        // `validate_nome_accepts_canonical_template` positive-control
14006        // gate pins), plus every sibling per-typed-slot atom accessor's
14007        // canonical positive-arm byte-string (`"catalog"` per
14008        // [`crate::aplicacao::Membro::nome`], `"cart"` per the peer
14009        // per-`:contratos` `:de`, `"hello-rio"` per the canonical
14010        // `caixa-helm`/`caixa-flux` cross-crate integration-test
14011        // fixture, `"checkout"` per the M3 mesh-slot Aplicacao
14012        // canonical example), plus every past-the-guard sentinel for
14013        // the `NomeEmpty` / `NomeInvalid` / `NomeChartNameBudgetExceeded`
14014        // refusal cases (`""`, `"Bad_Name"`, `"a"` × 56 — 56 bytes fits
14015        // the bare DNS-1123 63-byte cap but overflows the joint
14016        // `lareira-<nome>` chart-name budget the sibling
14017        // [`Caixa::validate_nome_chart_name_budget`] gate closes on).
14018        //
14019        // The past-the-guard sentinels pin the accessor doesn't
14020        // silently absorb the refusal cases into a template-derived
14021        // fallback (a future `.nome().is_empty().then(|| "demo")`
14022        // collapse would silently absorb the `NomeEmpty` refusal at
14023        // the accessor boundary and the validate gate would accept a
14024        // struct-literal `Caixa { nome: "".into(), .. }` — the pin
14025        // catches that at caixa-core build time).
14026        //
14027        // First outer top-level [`Caixa`] `&str`-return required-
14028        // scalar accessor pin — opens the "outer [`Caixa`] `&str`
14029        // required-scalar" projection pattern the sibling per-`Caixa`
14030        // `:versao` future lift folds on. Sibling in shape to the peer
14031        // per-`:membros` [`crate::aplicacao::Membro::nome`] (4a32abf)
14032        // required-`String`-carry accessor pin on the sibling per-
14033        // sub-struct required-axis, extended onto the outer top-level
14034        // [`Caixa`] universal-axis required-`String`-carry axis.
14035        for nome in [
14036            "demo",
14037            "catalog",
14038            "cart",
14039            "hello-rio",
14040            "checkout",
14041            "",
14042            "Bad_Name",
14043            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
14044        ] {
14045            let c = caixa_with_nome(nome);
14046            assert_eq!(
14047                c.nome(),
14048                nome,
14049                "Caixa::nome must return :nome verbatim (got {}, \
14050                 expected {nome})",
14051                c.nome(),
14052            );
14053            assert_eq!(
14054                c.nome(),
14055                c.nome.as_str(),
14056                "Caixa::nome must byte-equal the raw .nome field \
14057                 access across every value in the String accept-set",
14058            );
14059        }
14060    }
14061
14062    #[test]
14063    fn validate_nome_empty_arm_routes_through_accessor() {
14064        // Composition pin: [`Caixa::validate_nome`]'s empty-arm must
14065        // key off [`Caixa::nome`], not the raw `.nome` field access.
14066        // Structurally: a `Caixa { nome: "".into(), .. }` must surface
14067        // the `NomeEmpty` refusal exactly, and the canonical `"demo"`
14068        // template baseline (the peer positive-arm the sibling
14069        // `validate_nome_accepts_canonical_template` gate carves out)
14070        // must pass validate. The pair jointly pins the accessor +
14071        // validate-gate composition: any future silent detour that
14072        // had the accessor return a fresh `"demo"` on the empty arm
14073        // (a `.nome().is_empty().then(|| "demo")` fallback collapse)
14074        // would silently absorb the `NomeEmpty` refusal at the
14075        // accessor boundary and the validate gate would accept a
14076        // struct-literal `Caixa { nome: "".into(), .. }` — the
14077        // composition pin catches that at caixa-core build time.
14078        //
14079        // Peer of the sibling per-`Caixa`
14080        // `validate_licenca_empty_arm_routes_through_accessor` (6d5bc28)
14081        // / `validate_repositorio_empty_arm_routes_through_accessor`
14082        // (cc7332d) / `validate_descricao_empty_arm_routes_through_accessor`
14083        // (3f16e2f) / `validate_edicao_empty_arm_routes_through_accessor`
14084        // (2641cbd) composition pins on the sibling outer top-level
14085        // [`Caixa`] `Option<&str>` axes — same "the validate /
14086        // shape-gate predicate must route through the substrate-
14087        // primitive typed dispatch" discipline extended onto the peer
14088        // outer top-level [`Caixa`] required-`&str` composition axis.
14089        let c = caixa_with_nome("");
14090        assert!(
14091            matches!(c.validate_nome(), Err(ManifestError::NomeEmpty)),
14092            "validate_nome must reject nome == \"\" with NomeEmpty — \
14093             the accessor and the validate gate must route through the \
14094             same substrate-primitive typed dispatch on the :nome \
14095             empty-arm",
14096        );
14097        let c = caixa_with_nome("demo");
14098        assert!(
14099            c.validate_nome().is_ok(),
14100            "validate_nome must accept nome == \"demo\" (the canonical \
14101             DNS-1123-label template baseline)",
14102        );
14103    }
14104
14105    #[test]
14106    fn nome_projects_str_by_borrow() {
14107        // The by-borrow pin: [`Caixa::nome`] returns `&str` by borrow
14108        // — the `&str` borrows the underlying `String` storage of the
14109        // required `nome` slot and the accessor must not allocate a
14110        // fresh `String` on every call. Peer of the [`Caixa::licenca`]
14111        // (6d5bc28) / [`Caixa::repositorio`] (cc7332d) /
14112        // [`Caixa::descricao`] (3f16e2f) / [`Caixa::edicao`] (2641cbd)
14113        // by-borrow pins on the peer outer top-level [`Caixa`]
14114        // `Option<&str>`-return axes, extended onto the first outer
14115        // top-level [`Caixa`] required-`&str`-return axis — the
14116        // accessor's returned `&str` must borrow from `&self` (the
14117        // returned reference's lifetime is tied to `&self`), and
14118        // calling the accessor twice on the same [`Caixa`] must yield
14119        // the same `&str` verbatim (idempotent, no side effects on
14120        // `&self`).
14121        //
14122        // Pins against a future silent detour that returned an owned
14123        // `String` (which would type-check but silently allocate on
14124        // every call, breaking the zero-cost projection every peer
14125        // sibling accessor carries), an accidental
14126        // `.nome.to_lowercase()` detour that returned a fresh
14127        // allocation through an already-DNS-1123-lowercase-only
14128        // string (breaking a future `const fn` regression), or a
14129        // one-arm-only accessor that returned a canonicalized value
14130        // on some sentinel input (breaking the pass-through invariant
14131        // the sibling required-scalar accessors carry).
14132        for nome in ["demo", "catalog", "hello-rio", "checkout"] {
14133            let c = caixa_with_nome(nome);
14134            let first = c.nome();
14135            let second = c.nome();
14136            assert_eq!(
14137                first, second,
14138                "Caixa::nome must be idempotent — two successive calls \
14139                 on the same &self must return the same &str",
14140            );
14141            assert_eq!(
14142                first, nome,
14143                "Caixa::nome must return :nome verbatim by borrow — \
14144                 got {first}, expected {nome}",
14145            );
14146        }
14147    }
14148
14149    #[test]
14150    fn versao_returns_versao_byte_string_verbatim_across_permutations() {
14151        // The canonical per-`Caixa` `:versao` universal-axis SemVer-2
14152        // pinned-version scalar pin: [`Caixa::versao`] must return the
14153        // `:versao` typed `String` verbatim as `&str`, byte-equal to the
14154        // raw `.versao` field access across every representative value
14155        // in the accept-set — the canonical `"0.1.0"` template baseline
14156        // (the same `feira init`-scaffolded default the sibling
14157        // `validate_versao_accepts_canonical_template` positive-control
14158        // gate pins), plus every canonical SemVer-2 shape the sibling
14159        // `validate_versao_accepts_canonical_forms` positive-arm sweep
14160        // covers (`"0.0.0"`, `"1.0.0"`, `"0.2.0-rc.1"`,
14161        // `"1.0.0-alpha.0"`, `"1.0.0+build.42"`, `"1.0.0-rc.1+build.42"`,
14162        // `"10.20.30"`), plus every past-the-guard sentinel for the
14163        // `VersaoEmpty` / `VersaoInvalid` refusal cases (`""` the empty
14164        // arm, `"v0.1.0"` the git-tag-shape-leak footgun, `"0.1"` the
14165        // missing-patch footgun, `"^0.1"` the requirement-shape-leak
14166        // footgun, `"0.1.0.0"` the four-part-Java-convention footgun,
14167        // `"latest"` the docker-tag-shape footgun — the sentinels pin
14168        // the accessor doesn't silently absorb the refusal cases into a
14169        // template-derived fallback like `"0.1.0"`).
14170        //
14171        // The past-the-guard sentinels pin the accessor doesn't silently
14172        // absorb the refusal cases into a template-derived fallback (a
14173        // future `.versao().is_empty().then(|| "0.1.0")` collapse would
14174        // silently absorb the `VersaoEmpty` refusal at the accessor
14175        // boundary and the validate gate would accept a struct-literal
14176        // `Caixa { versao: "".into(), .. }` — the pin catches that at
14177        // caixa-core build time).
14178        //
14179        // Second outer top-level [`Caixa`] `&str`-return required-scalar
14180        // accessor pin — folds on the "outer [`Caixa`] `&str` required-
14181        // scalar" projection pattern the sibling per-`Caixa`
14182        // [`Caixa::nome`] (e6b7d97) opened. Sibling in shape to the peer
14183        // per-`:membros` [`crate::aplicacao::Membro::versao_requirement`]
14184        // (4127bb6) / per-`:children`
14185        // [`crate::supervisor::ChildSpec::versao_requirement`] (2c053c8)
14186        // / per-`:upgrade-from`
14187        // [`crate::UpgradeFromEntry::prior_versao`] (75d27a8) per-sub-
14188        // struct `:versao`-shaped `&str`-return accessor pins on the
14189        // sibling per-typed-slot version-carrier axes, extended onto the
14190        // second outer top-level [`Caixa`] universal-axis required-
14191        // `String`-carry axis so the two universal-axis identity-
14192        // carrying scalars every `defcaixa` form supplies (`:nome` +
14193        // `:versao`) share the same "one typed dispatch per axis" pin
14194        // discipline.
14195        for versao in [
14196            "0.1.0",
14197            "0.0.0",
14198            "1.0.0",
14199            "0.2.0-rc.1",
14200            "1.0.0-alpha.0",
14201            "1.0.0+build.42",
14202            "1.0.0-rc.1+build.42",
14203            "10.20.30",
14204            "",
14205            "v0.1.0",
14206            "0.1",
14207            "^0.1",
14208            "0.1.0.0",
14209            "latest",
14210        ] {
14211            let c = caixa_with_versao(versao);
14212            assert_eq!(
14213                c.versao(),
14214                versao,
14215                "Caixa::versao must return :versao verbatim (got {}, \
14216                 expected {versao})",
14217                c.versao(),
14218            );
14219            assert_eq!(
14220                c.versao(),
14221                c.versao.as_str(),
14222                "Caixa::versao must byte-equal the raw .versao field \
14223                 access across every value in the String accept-set",
14224            );
14225        }
14226    }
14227
14228    #[test]
14229    fn validate_versao_empty_arm_routes_through_accessor() {
14230        // Composition pin: [`Caixa::validate_versao`]'s empty-arm gate
14231        // must key off [`Caixa::versao`], not the raw `.versao` field
14232        // access. Structurally: a `Caixa { versao: "".into(), .. }` must
14233        // surface the `VersaoEmpty` refusal exactly, and the canonical
14234        // `"0.1.0"` template baseline (the peer positive-arm the sibling
14235        // `validate_versao_accepts_canonical_template` gate carves out)
14236        // must pass validate. The pair jointly pins the accessor +
14237        // validate-gate composition: any future silent detour that had
14238        // the accessor return a fresh `"0.1.0"` on the empty arm
14239        // (a `.versao().is_empty().then(|| "0.1.0")` fallback collapse)
14240        // would silently absorb the `VersaoEmpty` refusal at the
14241        // accessor boundary and the validate gate would accept a
14242        // struct-literal `Caixa { versao: "".into(), .. }` — the
14243        // composition pin catches that at caixa-core build time.
14244        //
14245        // Peer of the sibling per-`Caixa`
14246        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97)
14247        // composition pin on the sibling outer top-level [`Caixa`]
14248        // required-`&str` universal-axis surface — same "the validate /
14249        // shape-gate predicate must route through the substrate-
14250        // primitive typed dispatch" discipline extended onto the peer
14251        // outer top-level [`Caixa`] required-`&str` universal-axis
14252        // pinned-version composition axis, closing the second
14253        // coordinate of the "one canonical typed dispatch per per-Caixa
14254        // required-`&str` universal-axis" discipline.
14255        let c = caixa_with_versao("");
14256        assert!(
14257            matches!(c.validate_versao(), Err(ManifestError::VersaoEmpty)),
14258            "validate_versao must reject versao == \"\" with VersaoEmpty — \
14259             the accessor and the validate gate must route through the \
14260             same substrate-primitive typed dispatch on the :versao \
14261             empty-arm",
14262        );
14263        let c = caixa_with_versao("0.1.0");
14264        assert!(
14265            c.validate_versao().is_ok(),
14266            "validate_versao must accept versao == \"0.1.0\" (the \
14267             canonical SemVer-2 template baseline)",
14268        );
14269    }
14270
14271    #[test]
14272    fn versao_projects_str_by_borrow() {
14273        // The by-borrow pin: [`Caixa::versao`] returns `&str` by borrow
14274        // — the `&str` borrows the underlying `String` storage of the
14275        // required `versao` slot and the accessor must not allocate a
14276        // fresh `String` on every call. Peer of the [`Caixa::nome`]
14277        // (e6b7d97) by-borrow pin on the sibling outer top-level
14278        // [`Caixa`] required-`&str`-return axis, extended onto the
14279        // second outer top-level [`Caixa`] required-`&str`-return
14280        // universal-axis pinned-version surface — the accessor's
14281        // returned `&str` must borrow from `&self` (the returned
14282        // reference's lifetime is tied to `&self`), and calling the
14283        // accessor twice on the same [`Caixa`] must yield the same
14284        // `&str` verbatim (idempotent, no side effects on `&self`).
14285        //
14286        // Pins against a future silent detour that returned an owned
14287        // `String` (which would type-check but silently allocate on
14288        // every call, breaking the zero-cost projection every peer
14289        // sibling accessor carries), an accidental
14290        // `semver::Version::parse(&self.versao).unwrap().to_string()`
14291        // detour that returned a canonicalized fresh allocation through
14292        // an already-canonical byte-string (breaking a future `const fn`
14293        // regression and silently absorbing the `VersaoInvalid` refusal
14294        // at the accessor boundary), or a one-arm-only accessor that
14295        // returned a canonicalized value on some sentinel input
14296        // (breaking the pass-through invariant the sibling required-
14297        // scalar accessors carry).
14298        for versao in ["0.1.0", "1.0.0", "0.2.0-rc.1", "1.0.0+build.42"] {
14299            let c = caixa_with_versao(versao);
14300            let first = c.versao();
14301            let second = c.versao();
14302            assert_eq!(
14303                first, second,
14304                "Caixa::versao must be idempotent — two successive \
14305                 calls on the same &self must return the same &str",
14306            );
14307            assert_eq!(
14308                first, versao,
14309                "Caixa::versao must return :versao verbatim by borrow \
14310                 — got {first}, expected {versao}",
14311            );
14312        }
14313    }
14314
14315    fn caixa_with_kind(kind: CaixaKind) -> Caixa {
14316        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
14317        c.kind = kind;
14318        c
14319    }
14320
14321    #[test]
14322    fn kind_returns_kind_variant_verbatim_across_permutations() {
14323        // The canonical per-`Caixa` `:kind` universal-axis closed-set-
14324        // enum discriminant pin: [`Caixa::kind`] must return the `:kind`
14325        // typed [`CaixaKind`] variant verbatim by `Copy`, byte-equal to
14326        // the raw `.kind` field access across every variant in the
14327        // closed accept-set (`Biblioteca` — the library kind that
14328        // exports lisp forms; `Binario` — the nix-built executable kind
14329        // under `exe/`; `Servico` — the wasm-component daemon kind
14330        // under `servicos/`; `Supervisor` — the OTP-shaped hierarchical
14331        // reconciliation kind; `Aplicacao` — the M3 typed-mesh
14332        // composition kind).
14333        //
14334        // Pins against a future silent detour that re-derived the kind
14335        // from a peer axis (an accidental fallback to
14336        // `if !servicos.is_empty() { Servico } else if
14337        // !membros.is_empty() { Aplicacao } else { Biblioteca }`
14338        // collapse that read the code-surface / mesh-slot columns into
14339        // the kind discriminator), a variant remap the operator
14340        // authors on one consumer without the other, or a stale-derive
14341        // detour that substituted [`CaixaKind::Biblioteca`] as the
14342        // default when the field held any other variant (which would
14343        // silently collapse the distinction between "author explicitly
14344        // declared `:kind Servico`" and "author declared any other
14345        // kind" every downstream renderer-dispatch site depends on).
14346        //
14347        // First outer top-level [`Caixa`] `Copy`-return required-enum-
14348        // discriminant accessor pin — opens the "outer [`Caixa`]
14349        // `Copy`-return required-discriminant" projection pattern.
14350        // Sibling in shape to the peer per-`:supervisor`
14351        // [`crate::supervisor::SupervisorSpec::estrategia`] (eafb619),
14352        // per-`:placement` [`crate::aplicacao::Placement::estrategia`]
14353        // (921fe1b), and per-`:children`
14354        // [`crate::supervisor::ChildSpec::restart`] (dfb4a81)
14355        // `Copy`-return closed-set-enum discriminant accessor pins on
14356        // the sibling nested-spec typed-slot discriminator axes,
14357        // extended here to the outer top-level [`Caixa`] universal-
14358        // axis surface.
14359        for kind in [
14360            CaixaKind::Biblioteca,
14361            CaixaKind::Binario,
14362            CaixaKind::Servico,
14363            CaixaKind::Supervisor,
14364            CaixaKind::Aplicacao,
14365        ] {
14366            let c = caixa_with_kind(kind);
14367            assert_eq!(
14368                c.kind(),
14369                kind,
14370                "Caixa::kind must return :kind verbatim (got {:?}, \
14371                 expected {kind:?})",
14372                c.kind(),
14373            );
14374            assert_eq!(
14375                c.kind(),
14376                c.kind,
14377                "Caixa::kind accessor and .kind field access must \
14378                 byte-equal — the accessor is the substrate-primitive \
14379                 typed dispatch every downstream kind-gate consumer \
14380                 must route through",
14381            );
14382        }
14383    }
14384
14385    #[test]
14386    fn require_kind_reads_through_lifted_kind_accessor() {
14387        // Two-consumer coherence pin: the [`crate::render::require_kind`]
14388        // entry-gate predicate (the canonical two-line
14389        // `require_kind(caixa, Servico)?` prelude every per-Servico /
14390        // per-Aplicacao renderer runs at its entry-point) and the
14391        // sibling [`crate::render::KindMismatch`] error carrier's
14392        // `actual:` field (which names the offending caixa's variant
14393        // in the diagnostic) must both key off the lifted accessor, so
14394        // any future rebrand on the typed slot's reader shape lands at
14395        // exactly one place. Pins the two-site coherence by exercising
14396        // every off-diagonal `(actual, expected)` pair across the
14397        // closed accept-set — the `KindMismatch { actual, expected }`
14398        // surfaced on the mismatch arm must byte-equal the pair the
14399        // accessor returns for each side.
14400        //
14401        // Peer of the sibling per-`:placement`
14402        // `validate_placement_reads_through_lifted_estrategia_accessor`
14403        // (921fe1b) two-arm consumer-coherence pin on the M3 mesh-slot
14404        // `Copy`-return discriminant axis — same "the entry-gate
14405        // predicate and the error carrier's `actual:` field must route
14406        // through the substrate-primitive typed dispatch" discipline
14407        // extended onto the outer top-level [`Caixa`] universal-axis
14408        // discriminant surface.
14409        for expected in [
14410            CaixaKind::Biblioteca,
14411            CaixaKind::Binario,
14412            CaixaKind::Servico,
14413            CaixaKind::Supervisor,
14414            CaixaKind::Aplicacao,
14415        ] {
14416            for actual in [
14417                CaixaKind::Biblioteca,
14418                CaixaKind::Binario,
14419                CaixaKind::Servico,
14420                CaixaKind::Supervisor,
14421                CaixaKind::Aplicacao,
14422            ] {
14423                let c = caixa_with_kind(actual);
14424                let result = crate::render::require_kind(&c, expected);
14425                if expected == actual {
14426                    assert!(
14427                        result.is_ok(),
14428                        "require_kind must accept when actual == expected \
14429                         (actual={actual:?}, expected={expected:?})",
14430                    );
14431                } else {
14432                    let err = result.expect_err("require_kind must reject when actual != expected");
14433                    assert_eq!(
14434                        err.actual,
14435                        c.kind(),
14436                        "KindMismatch.actual must byte-equal Caixa::kind() \
14437                         — the error carrier's `actual:` field reads \
14438                         through the lifted accessor",
14439                    );
14440                    assert_eq!(
14441                        err.expected, expected,
14442                        "KindMismatch.expected must byte-equal the \
14443                         expected variant passed to require_kind",
14444                    );
14445                }
14446            }
14447        }
14448    }
14449
14450    #[test]
14451    fn aplicacao_view_kind_gate_routes_through_accessor() {
14452        // Composition pin: [`Caixa::aplicacao_view`]'s kind-gate arm
14453        // must key off [`Caixa::kind`], not the raw `.kind` field
14454        // access. Structurally: a `Caixa { kind: X, .. }` for any
14455        // non-`Aplicacao` variant must fold to `None` on the
14456        // `aplicacao_view` composer (the "kind mismatch → no typed
14457        // view" contract every downstream Aplicacao consumer keys off
14458        // via `?`), and a `Caixa { kind: Aplicacao, .. }` must fold to
14459        // `Some(_)`. The pair jointly pins the accessor + view-gate
14460        // composition: any future silent detour that had the accessor
14461        // return a fresh [`CaixaKind::Aplicacao`] on some sentinel
14462        // input would silently absorb the kind-mismatch case at the
14463        // accessor boundary and every per-Aplicacao renderer would
14464        // silently render a non-Aplicacao caixa's mesh slots — the
14465        // composition pin catches that at caixa-core build time.
14466        //
14467        // Peer of the sibling per-`Caixa`
14468        // `validate_nome_empty_arm_routes_through_accessor` (e6b7d97) /
14469        // `validate_versao_empty_arm_routes_through_accessor` (20c0539)
14470        // composition pins on the sibling outer top-level [`Caixa`]
14471        // required-`&str` universal-axis surfaces — same "the
14472        // composer / validate gate must route through the substrate-
14473        // primitive typed dispatch" discipline extended onto the
14474        // outer top-level [`Caixa`] `Copy`-return required-
14475        // discriminant composition axis.
14476        for kind in [
14477            CaixaKind::Biblioteca,
14478            CaixaKind::Binario,
14479            CaixaKind::Servico,
14480            CaixaKind::Supervisor,
14481        ] {
14482            let c = caixa_with_kind(kind);
14483            assert!(
14484                c.aplicacao_view().is_none(),
14485                "aplicacao_view must return None on non-Aplicacao \
14486                 kind {kind:?} — the composer's kind-gate must route \
14487                 through Caixa::kind()",
14488            );
14489        }
14490        let c = caixa_with_kind(CaixaKind::Aplicacao);
14491        assert!(
14492            c.aplicacao_view().is_some(),
14493            "aplicacao_view must return Some on kind Aplicacao — \
14494             the composer's kind-gate must accept the matching arm \
14495             through Caixa::kind()",
14496        );
14497    }
14498
14499    #[test]
14500    fn supervisor_view_kind_gate_routes_through_accessor() {
14501        // Composition pin (mirror of the sibling
14502        // `aplicacao_view_kind_gate_routes_through_accessor` on the
14503        // second `_view` composer): [`Caixa::supervisor_view`]'s kind-
14504        // gate arm must key off [`Caixa::kind`], not the raw `.kind`
14505        // field access. A `Caixa { kind: X, .. }` for any non-
14506        // `Supervisor` variant must fold to `None` on the
14507        // `supervisor_view` composer, and a `Caixa { kind:
14508        // Supervisor, .. }` must fold to `Some(_)`. Same peer
14509        // composition pin discipline on the second `_view` composer
14510        // axis.
14511        for kind in [
14512            CaixaKind::Biblioteca,
14513            CaixaKind::Binario,
14514            CaixaKind::Servico,
14515            CaixaKind::Aplicacao,
14516        ] {
14517            let c = caixa_with_kind(kind);
14518            assert!(
14519                c.supervisor_view().is_none(),
14520                "supervisor_view must return None on non-Supervisor \
14521                 kind {kind:?} — the composer's kind-gate must route \
14522                 through Caixa::kind()",
14523            );
14524        }
14525        let mut c = caixa_with_kind(CaixaKind::Supervisor);
14526        // A Supervisor caixa needs a strategy + at least one child to
14527        // fold to a Some(_) that also validates; the composer itself
14528        // requires only the kind arm, so bare kind flip is enough to
14529        // pin the `Some(_)` return, but we populate the minimum
14530        // supervisor shape so a future strengthening of the composer
14531        // to reject an empty spec doesn't false-positive this pin.
14532        c.estrategia = Some(crate::supervisor::RestartStrategy::OneForOne);
14533        c.children = vec![crate::supervisor::ChildSpec {
14534            caixa: "child".into(),
14535            versao: "^0.1".into(),
14536            restart: crate::supervisor::RestartPolicy::Permanent,
14537        }];
14538        assert!(
14539            c.supervisor_view().is_some(),
14540            "supervisor_view must return Some on kind Supervisor — \
14541             the composer's kind-gate must accept the matching arm \
14542             through Caixa::kind()",
14543        );
14544    }
14545
14546    #[test]
14547    fn kind_projects_by_copy() {
14548        // The by-`Copy` pin: [`Caixa::kind`] returns a fresh
14549        // [`CaixaKind`] by `Copy` — the accessor must not borrow from
14550        // `&self` (the returned value is owned, `Copy`-projected from
14551        // the underlying [`CaixaKind`] storage; two calls on the same
14552        // [`Caixa`] must yield byte-equal values). Peer of the peer
14553        // per-`:placement` `Placement::estrategia` / per-`:supervisor`
14554        // `SupervisorSpec::estrategia` / per-`:children`
14555        // `ChildSpec::restart` `Copy`-return discriminant accessor
14556        // pins on the sibling nested-spec typed-slot discriminator
14557        // axes, extended onto the first outer top-level [`Caixa`]
14558        // required-`Copy`-return axis — pins against a future silent
14559        // detour that returned `&CaixaKind` (which would type-check
14560        // but silently constrain every consumer's callsite to a
14561        // borrow-shaped dispatch, breaking the zero-cost `Copy`
14562        // projection every peer sibling accessor carries).
14563        for kind in [
14564            CaixaKind::Biblioteca,
14565            CaixaKind::Binario,
14566            CaixaKind::Servico,
14567            CaixaKind::Supervisor,
14568            CaixaKind::Aplicacao,
14569        ] {
14570            let c = caixa_with_kind(kind);
14571            let first: CaixaKind = c.kind();
14572            let second: CaixaKind = c.kind();
14573            assert_eq!(
14574                first, second,
14575                "Caixa::kind must be idempotent — two successive \
14576                 calls on the same &self must return the same \
14577                 CaixaKind variant",
14578            );
14579            assert_eq!(
14580                first, kind,
14581                "Caixa::kind must return :kind verbatim by Copy — \
14582                 got {first:?}, expected {kind:?}",
14583            );
14584        }
14585    }
14586
14587    // ── Caixa::autores — outer top-level &[T] slice accessor ──────────
14588
14589    #[test]
14590    fn autores_returns_autores_slice_verbatim_across_permutations() {
14591        // The canonical per-`Caixa` `:autores` universal-axis maintainer-
14592        // name-list slice pin: [`Caixa::autores`] must return the
14593        // `:autores` typed [`Vec<String>`] list verbatim as a
14594        // `&[String]`, byte-equal to the raw `self.autores.as_slice()`
14595        // access across every representative value in the accept-set —
14596        // `[]` (the "no maintainers declared" arm every existing
14597        // fixture without an `:autores` line carries), `[""]` (a past-
14598        // the-guard sentinel that pins the accessor doesn't perform a
14599        // silent `[""] → []` collapse on the empty-entry arm — validate
14600        // rejects `[""]` through `AutorEmpty` but the accessor must
14601        // ship the raw slot verbatim so a validate-time gate regression
14602        // surfaces at the caixa-helm emit boundary rather than being
14603        // silently absorbed into a maintainer-drop), `["pleme-io"]` (the
14604        // canonical single-maintainer form every `feira init` template
14605        // scaffolds), `["alice", "bob"]` (a canonical multi-maintainer
14606        // form), `["alice <alice@example.com>", "bob <bob@example.com>"]`
14607        // (the canonical RFC-5322 `<name> <email>` form the
14608        // `is_chart_maintainer_name_shape` predicate accepts), and
14609        // `["pleme-io", "pleme-io"]` (a past-the-guard duplicate
14610        // sentinel — validate rejects through `AutorDuplicate` but the
14611        // accessor must ship the raw slot verbatim).
14612        //
14613        // First outer top-level [`Caixa`] `&[T]`-return slice accessor
14614        // pin on the substrate primitive — opens the "outer [`Caixa`]
14615        // `&[T]` slice" projection pattern the sibling per-`Caixa`
14616        // `:etiquetas` / `:deps` / `:deps-dev` / `:exe` / `:bibliotecas`
14617        // / `:servicos` / `:upgrade-from` / `:children` future lifts
14618        // fold on. Sibling in shape to the peer per-`:supervisor`
14619        // [`crate::supervisor::SupervisorSpec::children`] (bc92bce),
14620        // per-`:placement` [`crate::aplicacao::Placement::clusters`]
14621        // (a6e18d7), per-`:membros`
14622        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36),
14623        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14624        // (0dcc926), and per-`:upgrade-from :instructions`
14625        // [`crate::upgrade::UpgradeFromEntry::instructions`] (0137e5a)
14626        // `&[T]`-return slice accessor pins on the sibling per-M2 /
14627        // per-M3 typed-slot list axes, extended onto the outer top-
14628        // level [`Caixa`] universal-axis surface. Pins against a future
14629        // silent detour that returned an owned `Vec<String>` (which
14630        // would type-check but silently clone on every accessor call,
14631        // breaking the zero-cost projection every peer sibling slice
14632        // accessor carries), a `[""] → []` collapse (which would
14633        // silently absorb the `AutorEmpty` refusal case at the accessor
14634        // boundary), or a `["a", "a"] → ["a"]` dedup collapse (which
14635        // would silently absorb the `AutorDuplicate` refusal case at
14636        // the accessor boundary and the caixa-helm `maintainers:` fold
14637        // would silently render a dedupped list on a struct-literal
14638        // `Caixa { autores: vec!["a".into(), "a".into()], .. }`).
14639        for autores in [
14640            vec![],
14641            vec![""],
14642            vec!["pleme-io"],
14643            vec!["alice", "bob"],
14644            vec!["alice <alice@example.com>", "bob <bob@example.com>"],
14645            vec!["pleme-io", "pleme-io"],
14646        ] {
14647            let c = caixa_with_autores(autores.clone());
14648            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14649            assert_eq!(
14650                c.autores(),
14651                expected.as_slice(),
14652                "Caixa::autores must return :autores verbatim (got {:?}, \
14653                 expected {expected:?})",
14654                c.autores(),
14655            );
14656            assert_eq!(
14657                c.autores(),
14658                c.autores.as_slice(),
14659                "Caixa::autores must byte-equal the raw \
14660                 `self.autores.as_slice()` field access across every \
14661                 value in the Vec<String> accept-set",
14662            );
14663        }
14664    }
14665
14666    #[test]
14667    fn validate_autores_empty_entry_arm_routes_through_accessor() {
14668        // Composition pin: [`Caixa::validate_autores`]'s per-entry
14669        // empty-arm gate must key off [`Caixa::autores`], not the raw
14670        // `&self.autores` field-borrow walk. Structurally: a
14671        // `Caixa { autores: vec!["".into()], .. }` must surface the
14672        // `AutorEmpty` refusal exactly, and a
14673        // `Caixa { autores: vec!["pleme-io".into()], .. }` (the
14674        // canonical single-maintainer form) must pass validate. The
14675        // pair jointly pins the accessor + validate-gate composition:
14676        // any future silent detour that had the accessor return an
14677        // empty slice on the `[""]` arm (a
14678        // `.iter().filter(|s| !s.is_empty()).collect()` collapse)
14679        // would silently absorb the `AutorEmpty` refusal at the
14680        // accessor boundary and the validate gate would accept a
14681        // struct-literal `Caixa { autores: vec!["".into()], .. }` —
14682        // the composition pin catches that at caixa-core build time.
14683        //
14684        // Peer of the per-`Caixa` [`Caixa::validate_licenca`] (6d5bc28)
14685        // accessor-composition pin
14686        // (`validate_licenca_empty_arm_routes_through_accessor`) on the
14687        // sibling `Option<&str>`-composition axis and the
14688        // per-`:politicas :circuit-breaker`
14689        // [`crate::aplicacao::CircuitBreaker::max_failures`] (3a74062)
14690        // accessor-composition pin
14691        // (`validate_politicas_max_failures_zero_floor_arm_routes_through_accessor`)
14692        // on the sibling required-`u32`-composition axis — same "the
14693        // validate / shape-gate predicate must route through the
14694        // substrate-primitive typed dispatch" discipline extended onto
14695        // the outer top-level [`Caixa`] universal-axis `&[T]`-
14696        // composition surface.
14697        let c = caixa_with_autores(vec![""]);
14698        assert!(
14699            matches!(c.validate_autores(), Err(ManifestError::AutorEmpty)),
14700            "validate_autores must reject autores == vec![\"\"] with \
14701             AutorEmpty — the accessor and the validate gate must \
14702             route through the same substrate-primitive typed dispatch \
14703             on the :autores per-entry empty arm",
14704        );
14705        let c = caixa_with_autores(vec!["pleme-io"]);
14706        assert!(
14707            c.validate_autores().is_ok(),
14708            "validate_autores must accept autores == vec![\"pleme-io\"] \
14709             (the canonical single-maintainer shape every `feira init` \
14710             template scaffolds)",
14711        );
14712    }
14713
14714    #[test]
14715    fn autores_projects_slice_by_borrow() {
14716        // The by-borrow pin: [`Caixa::autores`] returns `&[String]` by
14717        // borrow — the returned slice borrows the underlying
14718        // `Vec<String>` storage of the `:autores` slot and the
14719        // accessor must not clone the backing `Vec` on every call.
14720        // Peer of the per-`:membros`
14721        // [`crate::aplicacao::AplicacaoSpec::membros`] (6c77e36) /
14722        // per-`:contratos` [`crate::aplicacao::AplicacaoSpec::contratos`]
14723        // (0dcc926) / per-`:placement`
14724        // [`crate::aplicacao::Placement::clusters`] (a6e18d7) /
14725        // per-`:supervisor` [`crate::supervisor::SupervisorSpec::children`]
14726        // (bc92bce) by-borrow pins on the sibling per-M2 / per-M3
14727        // typed-slot `&[T]`-return axes, extended onto the outer top-
14728        // level [`Caixa`] universal-axis `&[String]` shape — the
14729        // accessor's returned slice must borrow from `&self` (the
14730        // returned reference's lifetime is tied to `&self`), and
14731        // calling the accessor twice on the same [`Caixa`] must yield
14732        // slices that are pointer-equal (the underlying byte-buffer is
14733        // the storage `Vec`'s allocation, not a fresh copy) as well as
14734        // value-equal (idempotent, no side effects on `&self`).
14735        //
14736        // Pins against a future silent detour that returned an owned
14737        // `Vec<String>` (which would type-check but silently clone on
14738        // every call, breaking the zero-cost projection every peer
14739        // sibling slice accessor carries), a `&Vec<String>` return
14740        // (which would leak the backing `Vec`'s grow/push/reserve
14741        // surface no downstream consumer reaches for), or a one-arm-
14742        // only accessor that returned a saturating value on some
14743        // sentinel input (breaking the pass-through invariant the
14744        // sibling slice accessors carry).
14745        for autores in [
14746            vec![],
14747            vec!["pleme-io"],
14748            vec!["alice", "bob"],
14749            vec!["pleme-io", "pleme-io"],
14750        ] {
14751            let c = caixa_with_autores(autores.clone());
14752            let expected: Vec<String> = autores.iter().map(|s| (*s).to_string()).collect();
14753            let first = c.autores();
14754            let second = c.autores();
14755            assert_eq!(
14756                first, second,
14757                "Caixa::autores must be idempotent — two successive \
14758                 calls on the same &self must return the same \
14759                 &[String]",
14760            );
14761            assert_eq!(
14762                first.as_ptr(),
14763                second.as_ptr(),
14764                "Caixa::autores must borrow the underlying Vec<String> \
14765                 storage — two successive calls must return slices \
14766                 with the same backing pointer (a fresh Vec<String> \
14767                 clone would change the pointer on every call)",
14768            );
14769            assert_eq!(
14770                first,
14771                expected.as_slice(),
14772                "Caixa::autores must return :autores verbatim by \
14773                 borrow — got {first:?}, expected {expected:?}",
14774            );
14775        }
14776    }
14777
14778    // ── Caixa::etiquetas — outer top-level &[T] slice accessor ────────
14779
14780    #[test]
14781    fn etiquetas_returns_etiquetas_slice_verbatim_across_permutations() {
14782        // The canonical per-`Caixa` `:etiquetas` universal-axis
14783        // registry-search-tag-list slice pin: [`Caixa::etiquetas`] must
14784        // return the `:etiquetas` typed [`Vec<String>`] list verbatim
14785        // as a `&[String]`, byte-equal to the raw
14786        // `self.etiquetas.as_slice()` access across every representative
14787        // value in the accept-set — `[]` (the "no tags declared" arm
14788        // every existing fixture without an `:etiquetas` line carries),
14789        // `[""]` (a past-the-guard sentinel that pins the accessor
14790        // doesn't perform a silent `[""] → []` collapse on the empty-
14791        // entry arm — validate rejects `[""]` through `EtiquetaEmpty`
14792        // but the accessor must ship the raw slot verbatim so a
14793        // validate-time gate regression surfaces at the caixa-helm emit
14794        // boundary rather than being silently absorbed into a keyword-
14795        // drop), `["demo"]` (the canonical single-tag form every
14796        // `feira init` template scaffolds), `["example", "aplicacao",
14797        // "mesh", "ecommerce", "demo"]` (the canonical multi-tag form
14798        // the checkout-aplicacao fixture emits), and `["demo", "demo"]`
14799        // (a past-the-guard duplicate sentinel — validate rejects
14800        // through `EtiquetaDuplicate` but the accessor must ship the
14801        // raw slot verbatim so the caixa-helm `BTreeSet::collect` dedup
14802        // at chart-render time isn't silently promoted into the
14803        // accessor boundary and struct-literal
14804        // `Caixa { etiquetas: vec!["demo".into(), "demo".into()], .. }`
14805        // fixtures continue to expose the duplicate at the accessor).
14806        //
14807        // Second outer top-level [`Caixa`] `&[T]`-return slice accessor
14808        // pin on the substrate primitive — folds on the "outer
14809        // [`Caixa`] `&[T]` slice" projection pattern
14810        // `autores_returns_autores_slice_verbatim_across_permutations`
14811        // (b5d813f) opened, sibling in shape and idiom. Pins against a
14812        // future silent detour that returned an owned `Vec<String>`
14813        // (which would type-check but silently clone on every accessor
14814        // call, breaking the zero-cost projection every peer sibling
14815        // slice accessor carries), a `[""] → []` collapse (which would
14816        // silently absorb the `EtiquetaEmpty` refusal case at the
14817        // accessor boundary), or a `["a", "a"] → ["a"]` dedup collapse
14818        // (which would silently absorb the `EtiquetaDuplicate` refusal
14819        // case at the accessor boundary — the caixa-helm chart-render
14820        // `BTreeSet::collect` dedup is downstream of the accessor and
14821        // must not be silently promoted into it).
14822        for etiquetas in [
14823            vec![],
14824            vec![""],
14825            vec!["demo"],
14826            vec!["example", "aplicacao", "mesh", "ecommerce", "demo"],
14827            vec!["demo", "demo"],
14828        ] {
14829            let c = caixa_with_etiquetas(etiquetas.clone());
14830            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14831            assert_eq!(
14832                c.etiquetas(),
14833                expected.as_slice(),
14834                "Caixa::etiquetas must return :etiquetas verbatim (got \
14835                 {:?}, expected {expected:?})",
14836                c.etiquetas(),
14837            );
14838            assert_eq!(
14839                c.etiquetas(),
14840                c.etiquetas.as_slice(),
14841                "Caixa::etiquetas must byte-equal the raw \
14842                 `self.etiquetas.as_slice()` field access across every \
14843                 value in the Vec<String> accept-set",
14844            );
14845        }
14846    }
14847
14848    #[test]
14849    fn validate_etiquetas_empty_entry_arm_routes_through_accessor() {
14850        // Composition pin: [`Caixa::validate_etiquetas`]'s per-entry
14851        // empty-arm gate must key off [`Caixa::etiquetas`], not the raw
14852        // `&self.etiquetas` field-borrow walk. Structurally: a
14853        // `Caixa { etiquetas: vec!["".into()], .. }` must surface the
14854        // `EtiquetaEmpty` refusal exactly, and a
14855        // `Caixa { etiquetas: vec!["demo".into()], .. }` (the canonical
14856        // single-tag form) must pass validate. The pair jointly pins
14857        // the accessor + validate-gate composition: any future silent
14858        // detour that had the accessor return an empty slice on the
14859        // `[""]` arm (a
14860        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
14861        // silently absorb the `EtiquetaEmpty` refusal at the accessor
14862        // boundary and the validate gate would accept a struct-literal
14863        // `Caixa { etiquetas: vec!["".into()], .. }` — the composition
14864        // pin catches that at caixa-core build time.
14865        //
14866        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
14867        // through_accessor` (b5d813f) accessor-composition pin on the
14868        // sibling `&[T]`-composition axis — same "the validate / shape-
14869        // gate predicate must route through the substrate-primitive
14870        // typed dispatch" discipline extended onto the sibling outer
14871        // top-level [`Caixa`] `&[T]`-composition surface.
14872        let c = caixa_with_etiquetas(vec![""]);
14873        assert!(
14874            matches!(c.validate_etiquetas(), Err(ManifestError::EtiquetaEmpty)),
14875            "validate_etiquetas must reject etiquetas == vec![\"\"] \
14876             with EtiquetaEmpty — the accessor and the validate gate \
14877             must route through the same substrate-primitive typed \
14878             dispatch on the :etiquetas per-entry empty arm",
14879        );
14880        let c = caixa_with_etiquetas(vec!["demo"]);
14881        assert!(
14882            c.validate_etiquetas().is_ok(),
14883            "validate_etiquetas must accept etiquetas == vec![\"demo\"] \
14884             (the canonical single-tag shape every `feira init` \
14885             template scaffolds)",
14886        );
14887    }
14888
14889    #[test]
14890    fn etiquetas_projects_slice_by_borrow() {
14891        // The by-borrow pin: [`Caixa::etiquetas`] returns `&[String]`
14892        // by borrow — the returned slice borrows the underlying
14893        // `Vec<String>` storage of the `:etiquetas` slot and the
14894        // accessor must not clone the backing `Vec` on every call.
14895        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
14896        // (b5d813f) by-borrow pin on the sibling outer top-level
14897        // [`Caixa`] `&[String]`-return axis — the accessor's returned
14898        // slice must borrow from `&self` (the returned reference's
14899        // lifetime is tied to `&self`), and calling the accessor twice
14900        // on the same [`Caixa`] must yield slices that are pointer-
14901        // equal (the underlying byte-buffer is the storage `Vec`'s
14902        // allocation, not a fresh copy) as well as value-equal
14903        // (idempotent, no side effects on `&self`).
14904        //
14905        // Pins against a future silent detour that returned an owned
14906        // `Vec<String>` (which would type-check but silently clone on
14907        // every call, breaking the zero-cost projection every peer
14908        // sibling slice accessor carries), a `&Vec<String>` return
14909        // (which would leak the backing `Vec`'s grow/push/reserve
14910        // surface no downstream consumer reaches for), or a one-arm-
14911        // only accessor that returned a saturating value on some
14912        // sentinel input (breaking the pass-through invariant the
14913        // sibling slice accessors carry).
14914        for etiquetas in [
14915            vec![],
14916            vec!["demo"],
14917            vec!["example", "aplicacao", "mesh"],
14918            vec!["demo", "demo"],
14919        ] {
14920            let c = caixa_with_etiquetas(etiquetas.clone());
14921            let expected: Vec<String> = etiquetas.iter().map(|s| (*s).to_string()).collect();
14922            let first = c.etiquetas();
14923            let second = c.etiquetas();
14924            assert_eq!(
14925                first, second,
14926                "Caixa::etiquetas must be idempotent — two successive \
14927                 calls on the same &self must return the same \
14928                 &[String]",
14929            );
14930            assert_eq!(
14931                first.as_ptr(),
14932                second.as_ptr(),
14933                "Caixa::etiquetas must borrow the underlying \
14934                 Vec<String> storage — two successive calls must \
14935                 return slices with the same backing pointer (a fresh \
14936                 Vec<String> clone would change the pointer on every \
14937                 call)",
14938            );
14939            assert_eq!(
14940                first,
14941                expected.as_slice(),
14942                "Caixa::etiquetas must return :etiquetas verbatim by \
14943                 borrow — got {first:?}, expected {expected:?}",
14944            );
14945        }
14946    }
14947
14948    // ── Caixa::bibliotecas — outer top-level &[T] slice accessor ──────
14949
14950    #[test]
14951    fn bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations() {
14952        // The canonical per-`Caixa` `:bibliotecas` universal-axis
14953        // library-source-path-list slice pin: [`Caixa::bibliotecas`]
14954        // must return the `:bibliotecas` typed [`Vec<String>`] list
14955        // verbatim as a `&[String]`, byte-equal to the raw
14956        // `self.bibliotecas.as_slice()` access across every
14957        // representative value in the accept-set — `[]` (the "no
14958        // libraries declared" arm every `:kind` other than `Biblioteca`
14959        // + every `Biblioteca` relying on the canonical
14960        // `lib/<nome>.lisp` implicit-default path carries; the
14961        // layout's [`crate::LayoutInvariants`] `MissingLib` arm-gate
14962        // fires exactly on this empty-slot + `Biblioteca`-kind
14963        // combination), `[""]` (a past-the-guard sentinel that pins
14964        // the accessor doesn't perform a silent `[""] → []` collapse
14965        // on the empty-entry arm — validate rejects `[""]` through
14966        // `CodePathEmpty { slot: ":bibliotecas" }` but the accessor
14967        // must ship the raw slot verbatim so a validate-time gate
14968        // regression surfaces at the `feira build` phase-1 parse
14969        // boundary rather than being silently absorbed into a
14970        // library-drop), `["lib/demo.lisp"]` (the canonical single-
14971        // entry form `Caixa::template` scaffolds and every `feira init`
14972        // template emits), `["lib/demo.lisp", "lib/helpers.lisp"]`
14973        // (the canonical multi-library form the
14974        // `validate_code_paths_accepts_explicit_relative_paths_on_
14975        // every_slot` fixture emits), and `["lib/foo.lisp",
14976        // "lib/foo.lisp"]` (a past-the-guard duplicate sentinel —
14977        // validate rejects through `CodePathDuplicate { slot:
14978        // ":bibliotecas" }` per the per-slot set-not-multiset gate,
14979        // but the accessor must ship the raw slot verbatim so the
14980        // `feira build` `for entry in caixa.bibliotecas()` parse walk
14981        // sees the duplicate at the accessor boundary and struct-
14982        // literal `Caixa { bibliotecas: vec!["lib/foo.lisp".into(),
14983        // "lib/foo.lisp".into()], .. }` fixtures continue to expose
14984        // the duplicate at the accessor).
14985        //
14986        // Third outer top-level [`Caixa`] `&[T]`-return slice accessor
14987        // pin on the substrate primitive — folds on the "outer
14988        // [`Caixa`] `&[T]` slice" projection pattern
14989        // `autores_returns_autores_slice_verbatim_across_permutations`
14990        // (b5d813f) opened and
14991        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
14992        // (78c7d3c) folded on, sibling in shape and idiom. Pins
14993        // against a future silent detour that returned an owned
14994        // `Vec<String>` (which would type-check but silently clone on
14995        // every accessor call, breaking the zero-cost projection
14996        // every peer sibling slice accessor carries), a `[""] → []`
14997        // collapse (which would silently absorb the `CodePathEmpty`
14998        // refusal case at the accessor boundary), or a `["lib/foo.lisp",
14999        // "lib/foo.lisp"] → ["lib/foo.lisp"]` dedup collapse (which
15000        // would silently absorb the `CodePathDuplicate` refusal case
15001        // at the accessor boundary — the per-slot set-not-multiset
15002        // gate is downstream of the accessor and must not be silently
15003        // promoted into it).
15004        for bibliotecas in [
15005            vec![],
15006            vec![""],
15007            vec!["lib/demo.lisp"],
15008            vec!["lib/demo.lisp", "lib/helpers.lisp"],
15009            vec!["lib/foo.lisp", "lib/foo.lisp"],
15010        ] {
15011            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
15012            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
15013            assert_eq!(
15014                c.bibliotecas(),
15015                expected.as_slice(),
15016                "Caixa::bibliotecas must return :bibliotecas verbatim \
15017                 (got {:?}, expected {expected:?})",
15018                c.bibliotecas(),
15019            );
15020            assert_eq!(
15021                c.bibliotecas(),
15022                c.bibliotecas.as_slice(),
15023                "Caixa::bibliotecas must byte-equal the raw \
15024                 `self.bibliotecas.as_slice()` field access across \
15025                 every value in the Vec<String> accept-set",
15026            );
15027        }
15028    }
15029
15030    #[test]
15031    fn validate_code_paths_bibliotecas_empty_arm_routes_through_accessor() {
15032        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15033        // empty-arm gate on the `:bibliotecas` slot must key off
15034        // [`Caixa::bibliotecas`], not a divergent raw
15035        // `&self.bibliotecas` field-borrow walk. Structurally: a
15036        // `Caixa { bibliotecas: vec!["".into()], .. }` must surface
15037        // the `CodePathEmpty { slot: ":bibliotecas" }` refusal
15038        // exactly, and a `Caixa { bibliotecas: vec!["lib/demo.lisp".
15039        // into()], .. }` (the canonical single-library form
15040        // `Caixa::template` scaffolds) must pass validate. The pair
15041        // jointly pins the accessor + validate-gate composition: any
15042        // future silent detour that had the accessor return an empty
15043        // slice on the `[""]` arm (a `.iter().filter(|s|
15044        // !s.is_empty()).collect()` collapse) would silently absorb
15045        // the `CodePathEmpty` refusal at the accessor boundary and
15046        // the validate gate would accept a struct-literal
15047        // `Caixa { bibliotecas: vec!["".into()], .. }` — the
15048        // composition pin catches that at caixa-core build time.
15049        //
15050        // Peer of the per-`Caixa` `validate_autores_empty_arm_routes_
15051        // through_accessor` (b5d813f) and
15052        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15053        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15054        // composition axes — same "the validate / shape-gate
15055        // predicate must route through the substrate-primitive typed
15056        // dispatch" discipline extended onto the sibling outer top-
15057        // level [`Caixa`] `&[T]`-composition surface. Nominally the
15058        // in-tree `validate_code_paths` production body still keys
15059        // off the internal `[(":bibliotecas", &self.bibliotecas,
15060        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15061        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15062        // (the tuple's homogeneous slice-typed shape blocks a per-
15063        // element accessor swap in isolation — a future companion
15064        // lift for `:exe` and `:servicos` on the same outer-`Caixa`
15065        // `&[T]` slice-accessor axis closes that tuple onto the
15066        // triple of typed dispatches as a unit); the composition pin
15067        // catches any future accessor-side silent filter drop against
15068        // that eventual tuple-closure regardless of whether the
15069        // `:bibliotecas` slot is threaded through the accessor or the
15070        // raw field access at the tuple's construction site.
15071        let c = caixa_with_code_paths(vec![""], vec![], vec![]);
15072        assert!(
15073            matches!(
15074                c.validate_code_paths(),
15075                Err(ManifestError::CodePathEmpty {
15076                    slot: ":bibliotecas"
15077                })
15078            ),
15079            "validate_code_paths must reject bibliotecas == vec![\"\"] \
15080             with CodePathEmpty {{ slot: \":bibliotecas\" }} — the \
15081             accessor and the validate gate must route through the \
15082             same substrate-primitive typed dispatch on the \
15083             :bibliotecas per-entry empty arm",
15084        );
15085        let c = caixa_with_code_paths(vec!["lib/demo.lisp"], vec![], vec![]);
15086        assert!(
15087            c.validate_code_paths().is_ok(),
15088            "validate_code_paths must accept bibliotecas == \
15089             vec![\"lib/demo.lisp\"] (the canonical single-library \
15090             shape every `feira init` template scaffolds)",
15091        );
15092    }
15093
15094    #[test]
15095    fn bibliotecas_projects_slice_by_borrow() {
15096        // The by-borrow pin: [`Caixa::bibliotecas`] returns
15097        // `&[String]` by borrow — the returned slice borrows the
15098        // underlying `Vec<String>` storage of the `:bibliotecas` slot
15099        // and the accessor must not clone the backing `Vec` on every
15100        // call. Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15101        // (b5d813f) and `etiquetas_projects_slice_by_borrow` (78c7d3c)
15102        // by-borrow pins on the sibling outer top-level [`Caixa`]
15103        // `&[String]`-return axes — the accessor's returned slice
15104        // must borrow from `&self` (the returned reference's lifetime
15105        // is tied to `&self`), and calling the accessor twice on the
15106        // same [`Caixa`] must yield slices that are pointer-equal
15107        // (the underlying byte-buffer is the storage `Vec`'s
15108        // allocation, not a fresh copy) as well as value-equal
15109        // (idempotent, no side effects on `&self`).
15110        //
15111        // Pins against a future silent detour that returned an owned
15112        // `Vec<String>` (which would type-check but silently clone on
15113        // every call, breaking the zero-cost projection every peer
15114        // sibling slice accessor carries), a `&Vec<String>` return
15115        // (which would leak the backing `Vec`'s grow/push/reserve
15116        // surface no downstream consumer reaches for), or a one-arm-
15117        // only accessor that returned a saturating value on some
15118        // sentinel input (breaking the pass-through invariant the
15119        // sibling slice accessors carry).
15120        for bibliotecas in [
15121            vec![],
15122            vec!["lib/demo.lisp"],
15123            vec!["lib/demo.lisp", "lib/helpers.lisp"],
15124            vec!["lib/foo.lisp", "lib/foo.lisp"],
15125        ] {
15126            let c = caixa_with_code_paths(bibliotecas.clone(), vec![], vec![]);
15127            let expected: Vec<String> = bibliotecas.iter().map(|s| (*s).to_string()).collect();
15128            let first = c.bibliotecas();
15129            let second = c.bibliotecas();
15130            assert_eq!(
15131                first, second,
15132                "Caixa::bibliotecas must be idempotent — two \
15133                 successive calls on the same &self must return the \
15134                 same &[String]",
15135            );
15136            assert_eq!(
15137                first.as_ptr(),
15138                second.as_ptr(),
15139                "Caixa::bibliotecas must borrow the underlying \
15140                 Vec<String> storage — two successive calls must \
15141                 return slices with the same backing pointer (a \
15142                 fresh Vec<String> clone would change the pointer on \
15143                 every call)",
15144            );
15145            assert_eq!(
15146                first,
15147                expected.as_slice(),
15148                "Caixa::bibliotecas must return :bibliotecas verbatim \
15149                 by borrow — got {first:?}, expected {expected:?}",
15150            );
15151        }
15152    }
15153
15154    // ── Caixa::exe — outer top-level &[T] slice accessor ──────────────
15155
15156    #[test]
15157    fn exe_returns_exe_slice_verbatim_across_permutations() {
15158        // The canonical per-`Caixa` `:exe` universal-axis
15159        // nix-built-executable-entry-path-list slice pin: [`Caixa::exe`]
15160        // must return the `:exe` typed [`Vec<String>`] list verbatim as
15161        // a `&[String]`, byte-equal to the raw `self.exe.as_slice()`
15162        // access across every representative value in the accept-set —
15163        // `[]` (the "no executable declared" arm every `:kind` other
15164        // than `Binario` carries; the layout's [`crate::LayoutInvariants`]
15165        // `BinarioWithoutExe` arm-gate fires exactly on this empty-slot
15166        // + `Binario`-kind combination), `[""]` (a past-the-guard
15167        // sentinel that pins the accessor doesn't perform a silent
15168        // `[""] → []` collapse on the empty-entry arm — validate rejects
15169        // `[""]` through `CodePathEmpty { slot: ":exe" }` but the
15170        // accessor must ship the raw slot verbatim so a validate-time
15171        // gate regression surfaces at the layout / `feira nix` boundary
15172        // rather than being silently absorbed into an executable-drop),
15173        // `["exe/cli"]` (the canonical single-entry Binario form every
15174        // in-tree `caixa_with_code_paths` positive control uses),
15175        // `["exe/cli", "exe/serve"]` (the canonical multi-executable
15176        // form the `validate_code_paths_accepts_explicit_relative_paths_
15177        // on_every_slot` fixture emits), and `["exe/cli", "exe/cli"]`
15178        // (a past-the-guard duplicate sentinel — validate rejects
15179        // through `CodePathDuplicate { slot: ":exe" }` per the per-slot
15180        // set-not-multiset gate, but the accessor must ship the raw
15181        // slot verbatim so struct-literal `Caixa { exe: vec!["exe/cli".
15182        // into(), "exe/cli".into()], .. }` fixtures continue to expose
15183        // the duplicate at the accessor).
15184        //
15185        // Fourth outer top-level [`Caixa`] `&[T]`-return slice accessor
15186        // pin on the substrate primitive — folds on the "outer
15187        // [`Caixa`] `&[T]` slice" projection pattern
15188        // `autores_returns_autores_slice_verbatim_across_permutations`
15189        // (b5d813f) opened,
15190        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15191        // (78c7d3c) folded on, and
15192        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15193        // (8a36c23) closed the universal-axis text-tag family of.
15194        // Opens the outer-`Caixa` foreign-code-slot `&[T]` sub-family
15195        // the sibling `:servicos` future lift closes onto. Pins against
15196        // a future silent detour that returned an owned `Vec<String>`
15197        // (which would type-check but silently clone on every accessor
15198        // call, breaking the zero-cost projection every peer sibling
15199        // slice accessor carries), a `[""] → []` collapse (which would
15200        // silently absorb the `CodePathEmpty` refusal case at the
15201        // accessor boundary), or an `["exe/cli", "exe/cli"] →
15202        // ["exe/cli"]` dedup collapse (which would silently absorb the
15203        // `CodePathDuplicate` refusal case at the accessor boundary —
15204        // the per-slot set-not-multiset gate is downstream of the
15205        // accessor and must not be silently promoted into it).
15206        for exe in [
15207            vec![],
15208            vec![""],
15209            vec!["exe/cli"],
15210            vec!["exe/cli", "exe/serve"],
15211            vec!["exe/cli", "exe/cli"],
15212        ] {
15213            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15214            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15215            assert_eq!(
15216                c.exe(),
15217                expected.as_slice(),
15218                "Caixa::exe must return :exe verbatim (got {:?}, \
15219                 expected {expected:?})",
15220                c.exe(),
15221            );
15222            assert_eq!(
15223                c.exe(),
15224                c.exe.as_slice(),
15225                "Caixa::exe must byte-equal the raw \
15226                 `self.exe.as_slice()` field access across every value \
15227                 in the Vec<String> accept-set",
15228            );
15229        }
15230    }
15231
15232    #[test]
15233    fn validate_code_paths_exe_empty_arm_routes_through_accessor() {
15234        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15235        // empty-arm gate on the `:exe` slot must key off
15236        // [`Caixa::exe`], not a divergent raw `&self.exe` field-borrow
15237        // walk. Structurally: a `Caixa { exe: vec!["".into()], .. }`
15238        // must surface the `CodePathEmpty { slot: ":exe" }` refusal
15239        // exactly, and a `Caixa { exe: vec!["exe/cli".into()], .. }`
15240        // (the canonical single-executable form every in-tree
15241        // `caixa_with_code_paths` positive control uses) must pass
15242        // validate. The pair jointly pins the accessor + validate-gate
15243        // composition: any future silent detour that had the accessor
15244        // return an empty slice on the `[""]` arm (a
15245        // `.iter().filter(|s| !s.is_empty()).collect()` collapse) would
15246        // silently absorb the `CodePathEmpty` refusal at the accessor
15247        // boundary and the validate gate would accept a struct-literal
15248        // `Caixa { exe: vec!["".into()], .. }` — the composition pin
15249        // catches that at caixa-core build time.
15250        //
15251        // Peer of the per-`Caixa`
15252        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15253        // (8a36c23), `validate_autores_empty_arm_routes_through_accessor`
15254        // (b5d813f), and
15255        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15256        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15257        // composition axes — same "the validate / shape-gate predicate
15258        // must route through the substrate-primitive typed dispatch"
15259        // discipline extended onto the sibling outer top-level [`Caixa`]
15260        // `&[T]`-composition surface. Nominally the in-tree
15261        // `validate_code_paths` production body still keys off the
15262        // internal `[(":bibliotecas", &self.bibliotecas,
15263        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15264        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15265        // (the tuple's homogeneous slice-typed shape blocks a per-
15266        // element accessor swap in isolation — a future companion lift
15267        // for `:servicos` on the same outer-`Caixa` `&[T]` slice-
15268        // accessor axis closes that tuple onto the triple of typed
15269        // dispatches as a unit); the composition pin catches any future
15270        // accessor-side silent filter drop against that eventual tuple-
15271        // closure regardless of whether the `:exe` slot is threaded
15272        // through the accessor or the raw field access at the tuple's
15273        // construction site.
15274        let c = caixa_with_code_paths(vec![], vec![""], vec![]);
15275        assert!(
15276            matches!(
15277                c.validate_code_paths(),
15278                Err(ManifestError::CodePathEmpty { slot: ":exe" })
15279            ),
15280            "validate_code_paths must reject exe == vec![\"\"] \
15281             with CodePathEmpty {{ slot: \":exe\" }} — the \
15282             accessor and the validate gate must route through the \
15283             same substrate-primitive typed dispatch on the \
15284             :exe per-entry empty arm",
15285        );
15286        let c = caixa_with_code_paths(vec![], vec!["exe/cli"], vec![]);
15287        assert!(
15288            c.validate_code_paths().is_ok(),
15289            "validate_code_paths must accept exe == vec![\"exe/cli\"] \
15290             (the canonical single-executable shape every in-tree \
15291             `caixa_with_code_paths` positive control uses)",
15292        );
15293    }
15294
15295    #[test]
15296    fn exe_projects_slice_by_borrow() {
15297        // The by-borrow pin: [`Caixa::exe`] returns `&[String]` by
15298        // borrow — the returned slice borrows the underlying
15299        // `Vec<String>` storage of the `:exe` slot and the accessor
15300        // must not clone the backing `Vec` on every call. Peer of the
15301        // per-`Caixa` `autores_projects_slice_by_borrow` (b5d813f),
15302        // `etiquetas_projects_slice_by_borrow` (78c7d3c), and
15303        // `bibliotecas_projects_slice_by_borrow` (8a36c23) by-borrow
15304        // pins on the sibling outer top-level [`Caixa`] `&[String]`-
15305        // return axes — the accessor's returned slice must borrow from
15306        // `&self` (the returned reference's lifetime is tied to
15307        // `&self`), and calling the accessor twice on the same
15308        // [`Caixa`] must yield slices that are pointer-equal (the
15309        // underlying byte-buffer is the storage `Vec`'s allocation,
15310        // not a fresh copy) as well as value-equal (idempotent, no
15311        // side effects on `&self`).
15312        //
15313        // Pins against a future silent detour that returned an owned
15314        // `Vec<String>` (which would type-check but silently clone on
15315        // every call, breaking the zero-cost projection every peer
15316        // sibling slice accessor carries), a `&Vec<String>` return
15317        // (which would leak the backing `Vec`'s grow/push/reserve
15318        // surface no downstream consumer reaches for), or a one-arm-
15319        // only accessor that returned a saturating value on some
15320        // sentinel input (breaking the pass-through invariant the
15321        // sibling slice accessors carry).
15322        for exe in [
15323            vec![],
15324            vec!["exe/cli"],
15325            vec!["exe/cli", "exe/serve"],
15326            vec!["exe/cli", "exe/cli"],
15327        ] {
15328            let c = caixa_with_code_paths(vec![], exe.clone(), vec![]);
15329            let expected: Vec<String> = exe.iter().map(|s| (*s).to_string()).collect();
15330            let first = c.exe();
15331            let second = c.exe();
15332            assert_eq!(
15333                first, second,
15334                "Caixa::exe must be idempotent — two successive calls \
15335                 on the same &self must return the same &[String]",
15336            );
15337            assert_eq!(
15338                first.as_ptr(),
15339                second.as_ptr(),
15340                "Caixa::exe must borrow the underlying Vec<String> \
15341                 storage — two successive calls must return slices \
15342                 with the same backing pointer (a fresh Vec<String> \
15343                 clone would change the pointer on every call)",
15344            );
15345            assert_eq!(
15346                first,
15347                expected.as_slice(),
15348                "Caixa::exe must return :exe verbatim by borrow — \
15349                 got {first:?}, expected {expected:?}",
15350            );
15351        }
15352    }
15353
15354    // ── Caixa::servicos — outer top-level &[T] slice accessor ─────────
15355
15356    #[test]
15357    fn servicos_returns_servicos_slice_verbatim_across_permutations() {
15358        // The canonical per-`Caixa` `:servicos` universal-axis
15359        // ComputeUnit-CR-YAML-entry-path-list slice pin:
15360        // [`Caixa::servicos`] must return the `:servicos` typed
15361        // [`Vec<String>`] list verbatim as a `&[String]`, byte-equal to
15362        // the raw `self.servicos.as_slice()` access across every
15363        // representative value in the accept-set — `[]` (the "no
15364        // ComputeUnit-CR declared" arm every `:kind` other than
15365        // `Servico` carries; the layout's [`crate::LayoutInvariants`]
15366        // `ServicoWithoutServicos` arm-gate fires exactly on this
15367        // empty-slot + `Servico`-kind combination), `[""]` (a past-the-
15368        // guard sentinel that pins the accessor doesn't perform a
15369        // silent `[""] → []` collapse on the empty-entry arm — validate
15370        // rejects `[""]` through `CodePathEmpty { slot: ":servicos" }`
15371        // but the accessor must ship the raw slot verbatim so a
15372        // validate-time gate regression surfaces at the layout /
15373        // per-Servico renderer boundary rather than being silently
15374        // absorbed into a component-drop),
15375        // `["servicos/demo.computeunit.yaml"]` (the canonical
15376        // singleton V0-shape every in-tree `caixa_with_code_paths`
15377        // positive control uses; the same shape
15378        // [`crate::require_single_servico`] admits),
15379        // `["servicos/a.computeunit.yaml", "servicos/b.computeunit.
15380        // yaml"]` (a past-the-guard `len != 1` sentinel — the V0
15381        // singularity gate rejects through `ServicoCountMismatch
15382        // { count: 2 }` but the accessor must ship the raw slot
15383        // verbatim so struct-literal `Caixa { servicos: vec![...,
15384        // ...], .. }` fixtures continue to expose the count at the
15385        // accessor), and `["servicos/a.computeunit.yaml",
15386        // "servicos/a.computeunit.yaml"]` (a past-the-guard duplicate
15387        // sentinel — validate rejects through
15388        // `CodePathDuplicate { slot: ":servicos" }` per the per-slot
15389        // set-not-multiset gate, but the accessor must ship the raw
15390        // slot verbatim so struct-literal fixtures continue to expose
15391        // the duplicate at the accessor).
15392        //
15393        // Fifth and final outer top-level [`Caixa`] `&[T]`-return
15394        // slice accessor pin on the substrate primitive — folds on the
15395        // "outer [`Caixa`] `&[T]` slice" projection pattern
15396        // `autores_returns_autores_slice_verbatim_across_permutations`
15397        // (b5d813f) opened,
15398        // `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15399        // (78c7d3c) folded on,
15400        // `bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15401        // (8a36c23) closed the universal-axis text-tag family of, and
15402        // `exe_returns_exe_slice_verbatim_across_permutations`
15403        // (65d9527) opened the foreign-code-slot sub-family of. Closes
15404        // the outer-`Caixa` foreign-code-slot `&[T]` sub-family — the
15405        // trio of code-surface list slots (`:bibliotecas` + `:exe` +
15406        // `:servicos`) now each carries a substrate-canonical slice
15407        // accessor. Pins against a future silent detour that returned
15408        // an owned `Vec<String>` (which would type-check but silently
15409        // clone on every accessor call, breaking the zero-cost
15410        // projection every peer sibling slice accessor carries), a
15411        // `[""] → []` collapse (which would silently absorb the
15412        // `CodePathEmpty` refusal case at the accessor boundary), an
15413        // `[a, a] → [a]` dedup collapse (which would silently absorb
15414        // the `CodePathDuplicate` refusal case at the accessor
15415        // boundary — the per-slot set-not-multiset gate is downstream
15416        // of the accessor and must not be silently promoted into it),
15417        // or a `[a, b] → [a]` singleton collapse (which would silently
15418        // absorb the V0 `ServicoCountMismatch` refusal case at the
15419        // accessor boundary — the V0 singularity gate is downstream of
15420        // the accessor and must not be silently promoted into it).
15421        for servicos in [
15422            vec![],
15423            vec![""],
15424            vec!["servicos/demo.computeunit.yaml"],
15425            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15426            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15427        ] {
15428            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15429            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15430            assert_eq!(
15431                c.servicos(),
15432                expected.as_slice(),
15433                "Caixa::servicos must return :servicos verbatim (got \
15434                 {:?}, expected {expected:?})",
15435                c.servicos(),
15436            );
15437            assert_eq!(
15438                c.servicos(),
15439                c.servicos.as_slice(),
15440                "Caixa::servicos must byte-equal the raw \
15441                 `self.servicos.as_slice()` field access across every \
15442                 value in the Vec<String> accept-set",
15443            );
15444        }
15445    }
15446
15447    #[test]
15448    fn validate_code_paths_servicos_empty_arm_routes_through_accessor() {
15449        // Composition pin: [`Caixa::validate_code_paths`]'s per-entry
15450        // empty-arm gate on the `:servicos` slot must key off
15451        // [`Caixa::servicos`], not a divergent raw `&self.servicos`
15452        // field-borrow walk. Structurally: a `Caixa { servicos:
15453        // vec!["".into()], .. }` must surface the `CodePathEmpty
15454        // { slot: ":servicos" }` refusal exactly, and a `Caixa
15455        // { servicos: vec!["servicos/demo.computeunit.yaml".into()],
15456        // .. }` (the canonical singleton V0-shape every in-tree
15457        // `caixa_with_code_paths` positive control uses) must pass
15458        // validate. The pair jointly pins the accessor + validate-gate
15459        // composition: any future silent detour that had the accessor
15460        // return an empty slice on the `[""]` arm (a `.iter().filter
15461        // (|s| !s.is_empty()).collect()` collapse) would silently
15462        // absorb the `CodePathEmpty` refusal at the accessor boundary
15463        // and the validate gate would accept a struct-literal
15464        // `Caixa { servicos: vec!["".into()], .. }` — the composition
15465        // pin catches that at caixa-core build time.
15466        //
15467        // Peer of the per-`Caixa`
15468        // `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15469        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15470        // (65d9527), `validate_autores_empty_arm_routes_through_accessor`
15471        // (b5d813f), and
15472        // `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15473        // (78c7d3c) accessor-composition pins on the sibling `&[T]`-
15474        // composition axes — same "the validate / shape-gate predicate
15475        // must route through the substrate-primitive typed dispatch"
15476        // discipline extended onto the sibling outer top-level
15477        // [`Caixa`] `&[T]`-composition surface, closing the trio of
15478        // code-surface accessor-composition pins on the same axis.
15479        // Nominally the in-tree `validate_code_paths` production body
15480        // still keys off the internal
15481        // `[(":bibliotecas", &self.bibliotecas,
15482        // CodePathFileType::LispSource), (":exe", &self.exe, ..),
15483        // (":servicos", &self.servicos, ..)]` per-slot dispatch tuple
15484        // (the tuple's homogeneous `&Vec<String>`-typed shape blocks a
15485        // per-element accessor swap in isolation — a future companion
15486        // lift promotes the tuple's element type to `&[String]` and
15487        // threads the triple of typed dispatches through as a unit);
15488        // the composition pin catches any future accessor-side silent
15489        // filter drop against that eventual tuple-closure regardless
15490        // of whether the `:servicos` slot is threaded through the
15491        // accessor or the raw field access at the tuple's construction
15492        // site.
15493        let c = caixa_with_code_paths(vec![], vec![], vec![""]);
15494        assert!(
15495            matches!(
15496                c.validate_code_paths(),
15497                Err(ManifestError::CodePathEmpty { slot: ":servicos" })
15498            ),
15499            "validate_code_paths must reject servicos == vec![\"\"] \
15500             with CodePathEmpty {{ slot: \":servicos\" }} — the \
15501             accessor and the validate gate must route through the \
15502             same substrate-primitive typed dispatch on the \
15503             :servicos per-entry empty arm",
15504        );
15505        let c = caixa_with_code_paths(vec![], vec![], vec!["servicos/demo.computeunit.yaml"]);
15506        assert!(
15507            c.validate_code_paths().is_ok(),
15508            "validate_code_paths must accept servicos == \
15509             vec![\"servicos/demo.computeunit.yaml\"] (the canonical \
15510             singleton V0-shape every in-tree `caixa_with_code_paths` \
15511             positive control uses)",
15512        );
15513    }
15514
15515    #[test]
15516    fn servicos_projects_slice_by_borrow() {
15517        // The by-borrow pin: [`Caixa::servicos`] returns `&[String]` by
15518        // borrow — the returned slice borrows the underlying
15519        // `Vec<String>` storage of the `:servicos` slot and the
15520        // accessor must not clone the backing `Vec` on every call.
15521        // Peer of the per-`Caixa` `autores_projects_slice_by_borrow`
15522        // (b5d813f), `etiquetas_projects_slice_by_borrow` (78c7d3c),
15523        // `bibliotecas_projects_slice_by_borrow` (8a36c23), and
15524        // `exe_projects_slice_by_borrow` (65d9527) by-borrow pins on
15525        // the sibling outer top-level [`Caixa`] `&[String]`-return
15526        // axes — the accessor's returned slice must borrow from
15527        // `&self` (the returned reference's lifetime is tied to
15528        // `&self`), and calling the accessor twice on the same
15529        // [`Caixa`] must yield slices that are pointer-equal (the
15530        // underlying byte-buffer is the storage `Vec`'s allocation,
15531        // not a fresh copy) as well as value-equal (idempotent, no
15532        // side effects on `&self`).
15533        //
15534        // Pins against a future silent detour that returned an owned
15535        // `Vec<String>` (which would type-check but silently clone on
15536        // every call, breaking the zero-cost projection every peer
15537        // sibling slice accessor carries), a `&Vec<String>` return
15538        // (which would leak the backing `Vec`'s grow/push/reserve
15539        // surface no downstream consumer reaches for), or a one-arm-
15540        // only accessor that returned a saturating value on some
15541        // sentinel input (breaking the pass-through invariant the
15542        // sibling slice accessors carry).
15543        for servicos in [
15544            vec![],
15545            vec!["servicos/demo.computeunit.yaml"],
15546            vec!["servicos/a.computeunit.yaml", "servicos/b.computeunit.yaml"],
15547            vec!["servicos/a.computeunit.yaml", "servicos/a.computeunit.yaml"],
15548        ] {
15549            let c = caixa_with_code_paths(vec![], vec![], servicos.clone());
15550            let expected: Vec<String> = servicos.iter().map(|s| (*s).to_string()).collect();
15551            let first = c.servicos();
15552            let second = c.servicos();
15553            assert_eq!(
15554                first, second,
15555                "Caixa::servicos must be idempotent — two successive \
15556                 calls on the same &self must return the same &[String]",
15557            );
15558            assert_eq!(
15559                first.as_ptr(),
15560                second.as_ptr(),
15561                "Caixa::servicos must borrow the underlying \
15562                 Vec<String> storage — two successive calls must \
15563                 return slices with the same backing pointer (a fresh \
15564                 Vec<String> clone would change the pointer on every \
15565                 call)",
15566            );
15567            assert_eq!(
15568                first,
15569                expected.as_slice(),
15570                "Caixa::servicos must return :servicos verbatim by \
15571                 borrow — got {first:?}, expected {expected:?}",
15572            );
15573        }
15574    }
15575
15576    // ── Caixa::deps — outer top-level &[Dep] slice accessor ───────────
15577
15578    fn caixa_with_deps(deps: Vec<Dep>) -> Caixa {
15579        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15580        c.deps = deps;
15581        c
15582    }
15583
15584    #[test]
15585    fn deps_returns_deps_slice_verbatim_across_permutations() {
15586        // The canonical per-`Caixa` `:deps` universal-axis runtime-
15587        // dependency-declaration-list slice pin: [`Caixa::deps`] must
15588        // return the `:deps` typed [`Vec<Dep>`] list verbatim as a
15589        // `&[Dep]`, element-equal to the raw `self.deps.as_slice()`
15590        // access across every representative value in the accept-set —
15591        // `[]` (the "no runtime deps declared" arm every existing
15592        // fixture without a `:deps` line carries; the
15593        // [`Caixa::template`] scaffold emits `:deps ()`), a canonical
15594        // single-entry list (the shape most consumer caixas carry), a
15595        // canonical two-entry list (the multi-dep runtime closure), and
15596        // two past-the-guard sentinels — a `[""]`-`:nome` entry
15597        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15598        // `NomeInvalid` but the accessor must ship the raw slot
15599        // verbatim) and a `[a, a]` duplicate (validate rejects through
15600        // `DuplicateNome { list: ":deps" }` but the accessor must ship
15601        // the raw slot verbatim so struct-literal fixtures continue to
15602        // expose the duplicate at the accessor).
15603        //
15604        // First outer top-level [`Caixa`] `&[Dep]`-return slice accessor
15605        // pin on the substrate primitive — opens the outer-`Caixa`
15606        // dependency-slot `&[Dep]` sub-family the sibling `:deps-dev`
15607        // future lift closes on. Peer of the closed outer-`Caixa`
15608        // foreign-code-slot `&[String]` sub-family
15609        // (`bibliotecas_returns_bibliotecas_slice_verbatim_across_permutations`
15610        // 8a36c23, `exe_returns_exe_slice_verbatim_across_permutations`
15611        // 65d9527, `servicos_returns_servicos_slice_verbatim_across_permutations`
15612        // 611f78b) and the outer-`Caixa` universal-axis text-tag family
15613        // (`autores_returns_autores_slice_verbatim_across_permutations`
15614        // b5d813f, `etiquetas_returns_etiquetas_slice_verbatim_across_permutations`
15615        // 78c7d3c) — extends the "outer [`Caixa`] `&[T]` slice"
15616        // projection pattern onto a novel element-type axis (`Dep`
15617        // composite vs the prior sibling family's `String` scalar).
15618        // Pins against a future silent detour that returned an owned
15619        // `Vec<Dep>` (which would type-check but silently clone on every
15620        // accessor call, breaking the zero-cost projection every peer
15621        // sibling slice accessor carries), a `[""] → []` collapse (which
15622        // would silently absorb the `NomeEmpty` refusal case at the
15623        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15624        // would silently absorb the `DuplicateNome` refusal case at the
15625        // accessor boundary).
15626        for deps in [
15627            vec![],
15628            vec![Dep::simple("", "^0.1")],
15629            vec![Dep::simple("caixa-teia", "^0.1")],
15630            vec![
15631                Dep::simple("caixa-teia", "^0.1"),
15632                Dep::simple("caixa-core", "^0.1"),
15633            ],
15634            vec![
15635                Dep::simple("caixa-teia", "^0.1"),
15636                Dep::simple("caixa-teia", "^0.2"),
15637            ],
15638        ] {
15639            let c = caixa_with_deps(deps.clone());
15640            assert_eq!(
15641                c.deps(),
15642                deps.as_slice(),
15643                "Caixa::deps must return :deps verbatim (got {:?}, \
15644                 expected {deps:?})",
15645                c.deps(),
15646            );
15647            assert_eq!(
15648                c.deps(),
15649                c.deps.as_slice(),
15650                "Caixa::deps must element-equal the raw \
15651                 `self.deps.as_slice()` field access across every \
15652                 value in the Vec<Dep> accept-set",
15653            );
15654        }
15655    }
15656
15657    #[test]
15658    fn validate_deps_duplicate_arm_routes_through_accessor() {
15659        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps`
15660        // duplicate-`:nome` gate must key off [`Caixa::deps`], not the
15661        // raw `&self.deps` field-borrow walk. Structurally: a `Caixa
15662        // { deps: vec![Dep::simple("d", "^0.1"), Dep::simple("d",
15663        // "^0.2")], .. }` must surface the `DuplicateNome { list:
15664        // ":deps" }` refusal exactly, and a `Caixa { deps: vec![
15665        // Dep::simple("d", "^0.1")], .. }` (the canonical single-entry
15666        // form) must pass validate. The pair jointly pins the accessor +
15667        // validate-gate composition: any future silent detour that had
15668        // the accessor return a dedupped slice on the `[a, a]` arm (a
15669        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15670        // would silently absorb the `DuplicateNome` refusal at the
15671        // accessor boundary and the validate gate would accept a
15672        // struct-literal `Caixa` carrying the drift — the composition
15673        // pin catches that at caixa-core build time.
15674        //
15675        // Peer of the per-`Caixa`
15676        // `validate_autores_empty_entry_arm_routes_through_accessor`
15677        // (b5d813f), `validate_etiquetas_empty_entry_arm_routes_through_accessor`
15678        // (78c7d3c), `validate_code_paths_bibliotecas_empty_arm_routes_through_accessor`
15679        // (8a36c23), `validate_code_paths_exe_empty_arm_routes_through_accessor`
15680        // (65d9527), and `validate_code_paths_servicos_empty_arm_routes_through_accessor`
15681        // (611f78b) accessor-composition pins on the sibling `&[T]`-
15682        // composition axes — same "the validate gate must route through
15683        // the substrate-primitive typed dispatch" discipline extended
15684        // onto the sibling outer top-level [`Caixa`] `&[Dep]`-
15685        // composition surface, opening the outer-`Caixa` dependency-slot
15686        // arm of the composition-pin family.
15687        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15688        let err = c.validate_deps().unwrap_err();
15689        assert!(
15690            matches!(
15691                err,
15692                DepError::DuplicateNome { ref nome, list } if nome == "d"
15693                    && list == crate::render::DEP_AUTHOR_KEY_DEPS
15694            ),
15695            "validate_deps must reject deps == \
15696             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15697             DuplicateNome {{ nome: \"d\", list: \":deps\" }} — the \
15698             accessor and the validate gate must route through the \
15699             same substrate-primitive typed dispatch on the :deps \
15700             within-list duplicate arm (got {err:?})",
15701        );
15702        let c = caixa_with_deps(vec![Dep::simple("d", "^0.1")]);
15703        assert!(
15704            c.validate_deps().is_ok(),
15705            "validate_deps must accept deps == vec![Dep(\"d\",\"^0.1\")] \
15706             (the canonical single-entry form)",
15707        );
15708    }
15709
15710    #[test]
15711    fn deps_projects_slice_by_borrow() {
15712        // The by-borrow pin: [`Caixa::deps`] returns `&[Dep]` by borrow
15713        // — the returned slice borrows the underlying `Vec<Dep>` storage
15714        // of the `:deps` slot and the accessor must not clone the
15715        // backing `Vec` on every call. Peer of the per-`Caixa`
15716        // `autores_projects_slice_by_borrow` (b5d813f),
15717        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15718        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15719        // `exe_projects_slice_by_borrow` (65d9527), and
15720        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15721        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15722        // axes — the accessor's returned slice must borrow from `&self`
15723        // (the returned reference's lifetime is tied to `&self`), and
15724        // calling the accessor twice on the same [`Caixa`] must yield
15725        // slices that are pointer-equal (the underlying byte-buffer is
15726        // the storage `Vec`'s allocation, not a fresh copy) as well as
15727        // value-equal (idempotent, no side effects on `&self`).
15728        //
15729        // Pins against a future silent detour that returned an owned
15730        // `Vec<Dep>` (which would type-check but silently clone on
15731        // every call), a `&Vec<Dep>` return (which would leak the
15732        // backing `Vec`'s grow/push/reserve surface no downstream
15733        // consumer reaches for), or a one-arm-only accessor that
15734        // returned a saturating value on some sentinel input.
15735        for deps in [
15736            vec![],
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        ] {
15743            let c = caixa_with_deps(deps.clone());
15744            let first = c.deps();
15745            let second = c.deps();
15746            assert_eq!(
15747                first, second,
15748                "Caixa::deps must be idempotent — two successive calls \
15749                 on the same &self must return the same &[Dep]",
15750            );
15751            assert_eq!(
15752                first.as_ptr(),
15753                second.as_ptr(),
15754                "Caixa::deps must borrow the underlying Vec<Dep> \
15755                 storage — two successive calls must return slices \
15756                 with the same backing pointer (a fresh Vec<Dep> clone \
15757                 would change the pointer on every call)",
15758            );
15759            assert_eq!(
15760                first,
15761                deps.as_slice(),
15762                "Caixa::deps must return :deps verbatim by borrow — \
15763                 got {first:?}, expected {deps:?}",
15764            );
15765        }
15766    }
15767
15768    // ── Caixa::deps_dev — outer top-level &[Dep] slice accessor ──────
15769
15770    fn caixa_with_deps_dev(deps_dev: Vec<Dep>) -> Caixa {
15771        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15772        c.deps_dev = deps_dev;
15773        c
15774    }
15775
15776    #[test]
15777    fn deps_dev_returns_deps_dev_slice_verbatim_across_permutations() {
15778        // The canonical per-`Caixa` `:deps-dev` universal-axis dev-only-
15779        // dependency-declaration-list slice pin: [`Caixa::deps_dev`]
15780        // must return the `:deps-dev` typed [`Vec<Dep>`] list verbatim as
15781        // a `&[Dep]`, element-equal to the raw `self.deps_dev.as_slice()`
15782        // access across every representative value in the accept-set —
15783        // `[]` (the "no dev deps declared" arm every existing fixture
15784        // without a `:deps-dev` line carries; the [`Caixa::template`]
15785        // scaffold emits `:deps-dev ()`), a canonical single-entry list
15786        // (the shape most consumer caixas carry — a `tatara-check` dev
15787        // pin), a canonical two-entry list (the multi-dev-dep closure),
15788        // and two past-the-guard sentinels — a `[""]`-`:nome` entry
15789        // ([`Self::validate_deps`] rejects through `NomeEmpty` /
15790        // `NomeInvalid` but the accessor must ship the raw slot
15791        // verbatim) and a `[a, a]` duplicate (validate rejects through
15792        // `DuplicateNome { list: ":deps-dev" }` but the accessor must
15793        // ship the raw slot verbatim so struct-literal fixtures continue
15794        // to expose the duplicate at the accessor).
15795        //
15796        // Second outer top-level [`Caixa`] `&[Dep]`-return slice-accessor
15797        // pin on the substrate primitive — closes the outer-`Caixa`
15798        // dependency-slot `&[Dep]` sub-family the sibling
15799        // `deps_returns_deps_slice_verbatim_across_permutations`
15800        // (ad34b4e) opened on. Folds the "outer [`Caixa`] `&[Dep]`
15801        // slice" projection pattern onto the sibling dev-dep axis —
15802        // pins against a future silent detour that returned an owned
15803        // `Vec<Dep>` (which would type-check but silently clone on every
15804        // accessor call, breaking the zero-cost projection every peer
15805        // sibling slice accessor carries), a `[""] → []` collapse (which
15806        // would silently absorb the `NomeEmpty` refusal case at the
15807        // accessor boundary), or a `[a, a] → [a]` dedup collapse (which
15808        // would silently absorb the `DuplicateNome` refusal case at the
15809        // accessor boundary).
15810        for deps_dev in [
15811            vec![],
15812            vec![Dep::simple("", "^0.1")],
15813            vec![Dep::simple("tatara-check", "^0.1")],
15814            vec![
15815                Dep::simple("tatara-check", "^0.1"),
15816                Dep::simple("caixa-lint", "^0.1"),
15817            ],
15818            vec![
15819                Dep::simple("tatara-check", "^0.1"),
15820                Dep::simple("tatara-check", "^0.2"),
15821            ],
15822        ] {
15823            let c = caixa_with_deps_dev(deps_dev.clone());
15824            assert_eq!(
15825                c.deps_dev(),
15826                deps_dev.as_slice(),
15827                "Caixa::deps_dev must return :deps-dev verbatim (got \
15828                 {:?}, expected {deps_dev:?})",
15829                c.deps_dev(),
15830            );
15831            assert_eq!(
15832                c.deps_dev(),
15833                c.deps_dev.as_slice(),
15834                "Caixa::deps_dev must element-equal the raw \
15835                 `self.deps_dev.as_slice()` field access across every \
15836                 value in the Vec<Dep> accept-set",
15837            );
15838        }
15839    }
15840
15841    #[test]
15842    fn validate_deps_duplicate_deps_dev_arm_routes_through_accessor() {
15843        // Composition pin: [`Caixa::validate_deps`]'s within-`:deps-dev`
15844        // duplicate-`:nome` gate must key off [`Caixa::deps_dev`], not
15845        // the raw `&self.deps_dev` field-borrow walk. Structurally: a
15846        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1"),
15847        // Dep::simple("d", "^0.2")], .. }` must surface the
15848        // `DuplicateNome { list: ":deps-dev" }` refusal exactly, and a
15849        // `Caixa { deps_dev: vec![Dep::simple("d", "^0.1")], .. }` (the
15850        // canonical single-entry form) must pass validate. The pair
15851        // jointly pins the accessor + validate-gate composition: any
15852        // future silent detour that had the accessor return a dedupped
15853        // slice on the `[a, a]` arm (a
15854        // `.iter().unique_by(|d| d.nome.as_str()).collect()` collapse)
15855        // would silently absorb the `DuplicateNome` refusal at the
15856        // accessor boundary and the validate gate would accept a
15857        // struct-literal `Caixa` carrying the drift — the composition
15858        // pin catches that at caixa-core build time.
15859        //
15860        // Peer of `validate_deps_duplicate_arm_routes_through_accessor`
15861        // (ad34b4e) on the sibling `:deps` axis — same "the validate
15862        // gate must route through the substrate-primitive typed
15863        // dispatch" discipline folded onto the sibling `:deps-dev`
15864        // axis, closing the two-list dep-graph composition-pin family.
15865        // The `:deps-dev` diagnostic must carry the
15866        // `DEP_AUTHOR_KEY_DEPS_DEV` list-tag (not
15867        // `DEP_AUTHOR_KEY_DEPS`) so the emitted error names the
15868        // offending list unambiguously.
15869        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1"), Dep::simple("d", "^0.2")]);
15870        let err = c.validate_deps().unwrap_err();
15871        assert!(
15872            matches!(
15873                err,
15874                DepError::DuplicateNome { ref nome, list } if nome == "d"
15875                    && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
15876            ),
15877            "validate_deps must reject deps_dev == \
15878             vec![Dep(\"d\",\"^0.1\"), Dep(\"d\",\"^0.2\")] with \
15879             DuplicateNome {{ nome: \"d\", list: \":deps-dev\" }} — the \
15880             accessor and the validate gate must route through the \
15881             same substrate-primitive typed dispatch on the :deps-dev \
15882             within-list duplicate arm (got {err:?})",
15883        );
15884        let c = caixa_with_deps_dev(vec![Dep::simple("d", "^0.1")]);
15885        assert!(
15886            c.validate_deps().is_ok(),
15887            "validate_deps must accept deps_dev == \
15888             vec![Dep(\"d\",\"^0.1\")] (the canonical single-entry form)",
15889        );
15890    }
15891
15892    #[test]
15893    fn deps_dev_projects_slice_by_borrow() {
15894        // The by-borrow pin: [`Caixa::deps_dev`] returns `&[Dep]` by
15895        // borrow — the returned slice borrows the underlying `Vec<Dep>`
15896        // storage of the `:deps-dev` slot and the accessor must not
15897        // clone the backing `Vec` on every call. Peer of
15898        // `deps_projects_slice_by_borrow` (ad34b4e) on the sibling
15899        // `:deps` axis, and of the per-`Caixa`
15900        // `autores_projects_slice_by_borrow` (b5d813f),
15901        // `etiquetas_projects_slice_by_borrow` (78c7d3c),
15902        // `bibliotecas_projects_slice_by_borrow` (8a36c23),
15903        // `exe_projects_slice_by_borrow` (65d9527), and
15904        // `servicos_projects_slice_by_borrow` (611f78b) by-borrow pins
15905        // on the sibling outer top-level [`Caixa`] `&[String]`-return
15906        // axes — the accessor's returned slice must borrow from `&self`
15907        // (the returned reference's lifetime is tied to `&self`), and
15908        // calling the accessor twice on the same [`Caixa`] must yield
15909        // slices that are pointer-equal (the underlying byte-buffer is
15910        // the storage `Vec`'s allocation, not a fresh copy) as well as
15911        // value-equal (idempotent, no side effects on `&self`).
15912        //
15913        // Pins against a future silent detour that returned an owned
15914        // `Vec<Dep>` (which would type-check but silently clone on
15915        // every call), a `&Vec<Dep>` return (which would leak the
15916        // backing `Vec`'s grow/push/reserve surface no downstream
15917        // consumer reaches for), or a one-arm-only accessor that
15918        // returned a saturating value on some sentinel input.
15919        for deps_dev in [
15920            vec![],
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        ] {
15927            let c = caixa_with_deps_dev(deps_dev.clone());
15928            let first = c.deps_dev();
15929            let second = c.deps_dev();
15930            assert_eq!(
15931                first, second,
15932                "Caixa::deps_dev must be idempotent — two successive \
15933                 calls on the same &self must return the same &[Dep]",
15934            );
15935            assert_eq!(
15936                first.as_ptr(),
15937                second.as_ptr(),
15938                "Caixa::deps_dev must borrow the underlying Vec<Dep> \
15939                 storage — two successive calls must return slices \
15940                 with the same backing pointer (a fresh Vec<Dep> clone \
15941                 would change the pointer on every call)",
15942            );
15943            assert_eq!(
15944                first,
15945                deps_dev.as_slice(),
15946                "Caixa::deps_dev must return :deps-dev verbatim by \
15947                 borrow — got {first:?}, expected {deps_dev:?}",
15948            );
15949        }
15950    }
15951
15952    // ── Caixa::limits — outer top-level Option<&LimitsSpec> composite-reference accessor ──
15953
15954    fn caixa_with_limits(limits: Option<crate::LimitsSpec>) -> Caixa {
15955        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
15956        c.limits = limits;
15957        c
15958    }
15959
15960    #[test]
15961    fn limits_returns_limits_option_ref_verbatim_across_permutations() {
15962        // The canonical per-`Caixa` `:limits` M2 typed-slot outer-
15963        // composite optional-composite-reference-shape pin:
15964        // [`Caixa::limits`] must return the `:limits` typed
15965        // `Option<LimitsSpec>` verbatim as an `Option<&LimitsSpec>`
15966        // reference over the same backing storage the raw
15967        // `self.limits.as_ref()` field access borrows from, byte-equal
15968        // across every representative fixture in the accept-set — the
15969        // author-omitted `None` shape (the "engine-default applies"
15970        // partition every downstream Servico M2 overlay emitter treats
15971        // as "emit nothing"), the empty-composite `Some(LimitsSpec {
15972        // .. default })` shape ([`LimitsSpec::is_empty`] holds — every
15973        // per-axis cap is `None`, so the peer M2 overlay emitter's
15974        // `.is_empty()`-gated projection still emits nothing but the
15975        // outer presence-bit is `Some`, so [`Caixa::declared_servico_slots`]
15976        // still pushes the `M2_AUTHOR_KEY_LIMITS` label), a single-axis
15977        // fixture (only `:memory` set — the canonical shape most
15978        // memory-heavy Servicos carry), and a fully-populated composite
15979        // (every per-axis cap set — the canonical shape a
15980        // sandboxed-by-default Servico carries).
15981        //
15982        // Pins against a future silent detour that returned a fresh-
15983        // cloned [`LimitsSpec`] copy (which would type-check via the
15984        // `Clone` impl but silently break every downstream caller that
15985        // relied on the reference sharing the composite's backing
15986        // identity), a reference to an operator-resolved overlay (the
15987        // future per-cluster `:limits-overrides` slot — its resolution
15988        // must land at exactly this accessor body, not silently divert
15989        // the raw slot away from a second consumer), a
15990        // `None` → `Some(LimitsSpec::default)` cluster-default
15991        // projection (which would collapse the load-bearing
15992        // "author-omitted `:limits` ⇒ engine-default applies" partition
15993        // the peer [`crate::render::servico_m2_overlay`] emitter and
15994        // the peer [`Caixa::declared_servico_slots`] enumerator both
15995        // read), or an axis-shuffled projection (a future detour that
15996        // swapped `memory` and `fuel` through the accessor would
15997        // silently split the paired [`crate::StandardLayout::verify`]
15998        // per-`:limits` shape gate's traversal input from the peer
15999        // `servico_m2_overlay` emitter's projection input).
16000        //
16001        // First outer top-level [`Caixa`] `Option<&Composite>`-return
16002        // composite-reference accessor pin on the substrate primitive
16003        // — opens the outer-`Caixa` `Option<&Composite>` composite-
16004        // reference projection pattern the sibling `:behavior`
16005        // [`crate::BehaviorSpec`] / `:politicas`
16006        // [`crate::aplicacao::MeshPolicy`] / `:placement`
16007        // [`crate::aplicacao::Placement`] / `:entrada`
16008        // [`crate::aplicacao::Entrada`] future outer-composite lifts
16009        // fold on. Peer of the closed M3 outer-composite family the
16010        // sibling [`crate::AplicacaoSpec::politicas`] (534dc21) /
16011        // [`crate::AplicacaoSpec::placement`] (9abb8f0) /
16012        // [`crate::AplicacaoSpec::entrada`] (d32111c) composite-
16013        // reference accessor pins already carry on the outer
16014        // [`crate::AplicacaoSpec`] altitude — extends the outer-
16015        // accessor byte-equal-projection discipline onto the outer
16016        // top-level [`Caixa`] M2 Servico-runtime slot altitude.
16017        use crate::LimitsSpec;
16018        use std::time::Duration;
16019        let fixtures: Vec<Option<LimitsSpec>> = vec![
16020            None,
16021            Some(LimitsSpec::default()),
16022            Some(LimitsSpec {
16023                memory: Some(64 * 1024 * 1024),
16024                ..Default::default()
16025            }),
16026            Some(LimitsSpec {
16027                memory: Some(64 * 1024 * 1024),
16028                fuel: Some(1_000_000),
16029                wall_clock: Some(Duration::from_secs(30)),
16030                cpu: Some(500),
16031            }),
16032        ];
16033        for limits in fixtures {
16034            let c = caixa_with_limits(limits.clone());
16035            assert_eq!(
16036                c.limits(),
16037                limits.as_ref(),
16038                "Caixa::limits must return :limits verbatim (got {:?}, \
16039                 expected {:?})",
16040                c.limits(),
16041                limits.as_ref(),
16042            );
16043            match (c.limits(), c.limits.as_ref()) {
16044                (Some(a), Some(b)) => assert!(
16045                    std::ptr::eq(a, b),
16046                    "Caixa::limits accessor and self.limits.as_ref() \
16047                     field access must borrow the same backing storage \
16048                     — the accessor is the substrate-primitive typed \
16049                     dispatch every downstream Servico-M2-overlay \
16050                     composite consumer must route through, and a \
16051                     reference-identity split would silently break \
16052                     every consumer that relied on the borrow sharing \
16053                     the composite's storage",
16054                ),
16055                (None, None) => {}
16056                _ => panic!(
16057                    "Caixa::limits presence bit must byte-equal \
16058                     self.limits.is_some() — a presence-bit drift would \
16059                     silently split the paired StandardLayout::verify \
16060                     per-`:limits` shape gate's traversal head from \
16061                     the peer render::servico_m2_overlay M2 overlay \
16062                     emitter's traversal head from the peer \
16063                     Caixa::declared_servico_slots M2 declared-slot \
16064                     enumerator's presence probe",
16065                ),
16066            }
16067            assert_eq!(
16068                c.limits().is_some(),
16069                c.limits.is_some(),
16070                "Caixa::limits().is_some() must byte-equal \
16071                 self.limits.is_some() — a presence-bit drift would \
16072                 silently split every downstream Option<&LimitsSpec> \
16073                 consumer's partition on the engine-default arm",
16074            );
16075        }
16076    }
16077
16078    #[test]
16079    fn declared_servico_slots_limits_arm_routes_through_accessor() {
16080        // Composition pin: [`Caixa::declared_servico_slots`]'s
16081        // `:limits` presence-probe arm must key off [`Caixa::limits`],
16082        // not the raw `self.limits.is_some()` field-probe. Structurally:
16083        // a `Caixa { limits: Some(LimitsSpec::default()), .. }` must
16084        // still push `M2_AUTHOR_KEY_LIMITS` onto the declared-slot list
16085        // (the presence bit is `Some`, so the M2 kind-coherence gate
16086        // must surface the slot as "declared" even when every per-axis
16087        // cap is unset), and a `Caixa { limits: None, .. }` must NOT
16088        // push the label (the "author omitted the slot entirely"
16089        // partition). The pair jointly pins the accessor + declared-
16090        // slot enumerator composition: any future silent detour that
16091        // had the accessor collapse `Some(LimitsSpec::default())` to
16092        // `None` (a `.filter(|l| !l.is_empty())` projection) would
16093        // silently absorb the "declared but empty" arm at the
16094        // accessor boundary and the [`crate::LayoutError::ServicoSlotsOnNonServico`]
16095        // kind-coherence gate would silently accept a
16096        // struct-literal `Caixa` carrying the drift.
16097        //
16098        // Peer of the sibling per-`Caixa`
16099        // `validate_deps_duplicate_arm_routes_through_accessor` (ad34b4e)
16100        // and `validate_deps_duplicate_deps_dev_arm_routes_through_accessor`
16101        // (f7fd81e) accessor-composition pins on the sibling `:deps` /
16102        // `:deps-dev` outer-`&[Dep]`-composition axes — same "the
16103        // enumerator gate must route through the substrate-primitive
16104        // typed dispatch" discipline extended onto the outer top-level
16105        // [`Caixa`] `Option<&LimitsSpec>`-composition surface, opening
16106        // the outer-`Caixa` M2 Servico-runtime-slot arm of the
16107        // composition-pin family.
16108        use crate::LimitsSpec;
16109        let c = caixa_with_limits(Some(LimitsSpec::default()));
16110        let slots = c.declared_servico_slots();
16111        assert!(
16112            slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
16113            "declared_servico_slots must push M2_AUTHOR_KEY_LIMITS \
16114             when `:limits` is Some (even for LimitsSpec::default()) \
16115             — the accessor and the enumerator gate must route through \
16116             the same substrate-primitive typed dispatch on the outer \
16117             :limits presence bit (got slots={slots:?})",
16118        );
16119        let c = caixa_with_limits(None);
16120        let slots = c.declared_servico_slots();
16121        assert!(
16122            !slots.contains(&crate::render::M2_AUTHOR_KEY_LIMITS),
16123            "declared_servico_slots must NOT push M2_AUTHOR_KEY_LIMITS \
16124             when `:limits` is None — the author-omitted arm must \
16125             route through the accessor's None-return unchanged (got \
16126             slots={slots:?})",
16127        );
16128    }
16129
16130    #[test]
16131    fn servico_m2_overlay_limits_arm_routes_through_accessor() {
16132        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16133        // per-`:limits` M2 overlay emit arm must key off
16134        // [`Caixa::limits`], not the raw `&caixa.limits` field-borrow.
16135        // Structurally: a `Caixa { limits: Some(LimitsSpec { memory:
16136        // Some(64 MiB), .. default }), .. }` must surface the
16137        // `M2_KEY_LIMITS` key with the per-axis
16138        // `memory: "64MiB"` sub-mapping in the overlay, a `Caixa {
16139        // limits: Some(LimitsSpec::default()), .. }` must omit the
16140        // key entirely (the `.is_empty()`-gated inner arm elides an
16141        // empty composite even when the outer presence bit is `Some`),
16142        // and a `Caixa { limits: None, .. }` must also omit the key
16143        // (the "author omitted the slot entirely" partition). The
16144        // three-fixture family jointly pins the accessor + M2 overlay
16145        // emitter composition: any future silent detour that had the
16146        // accessor return a fresh-cloned copy on the `Some` arm (a
16147        // `LimitsSpec::clone()` projection) would silently break the
16148        // reference-identity pin the peer per-axis
16149        // `serde_yaml::to_value(limits)` projection reads from.
16150        use crate::LimitsSpec;
16151        use crate::render::{M2_KEY_LIMITS, servico_m2_overlay};
16152        let c = caixa_with_limits(Some(LimitsSpec {
16153            memory: Some(64 * 1024 * 1024),
16154            ..Default::default()
16155        }));
16156        let overlay = servico_m2_overlay(&c).unwrap();
16157        assert!(
16158            overlay.contains_key(M2_KEY_LIMITS),
16159            "servico_m2_overlay must surface M2_KEY_LIMITS when \
16160             `:limits` carries a non-empty composite — the accessor \
16161             and the M2 overlay emitter must route through the same \
16162             substrate-primitive typed dispatch on the outer :limits \
16163             composite (got overlay={overlay:?})",
16164        );
16165        let c = caixa_with_limits(Some(LimitsSpec::default()));
16166        let overlay = servico_m2_overlay(&c).unwrap();
16167        assert!(
16168            !overlay.contains_key(M2_KEY_LIMITS),
16169            "servico_m2_overlay must omit M2_KEY_LIMITS when \
16170             `:limits` is Some(LimitsSpec::default()) — the empty \
16171             composite's `.is_empty()`-gated inner arm must elide \
16172             the key regardless of the outer presence bit (got \
16173             overlay={overlay:?})",
16174        );
16175        let c = caixa_with_limits(None);
16176        let overlay = servico_m2_overlay(&c).unwrap();
16177        assert!(
16178            !overlay.contains_key(M2_KEY_LIMITS),
16179            "servico_m2_overlay must omit M2_KEY_LIMITS when \
16180             `:limits` is None — the author-omitted arm must route \
16181             through the accessor's None-return unchanged (got \
16182             overlay={overlay:?})",
16183        );
16184    }
16185
16186    #[test]
16187    fn limits_projects_option_ref_by_borrow() {
16188        // The by-borrow pin: [`Caixa::limits`] returns
16189        // `Option<&LimitsSpec>` by borrow — the returned reference
16190        // borrows the underlying `Option<LimitsSpec>` storage of the
16191        // `:limits` slot and the accessor must not clone the backing
16192        // composite on every call. Peer of the sibling
16193        // `deps_projects_slice_by_borrow` (ad34b4e) /
16194        // `deps_dev_projects_slice_by_borrow` (f7fd81e) by-borrow pins
16195        // on the outer top-level [`Caixa`] `&[Dep]`-return axes —
16196        // extended here to the outer [`Caixa`] `Option<&Composite>`-
16197        // return axis: the accessor's returned reference must borrow
16198        // from `&self` (the returned reference's lifetime is tied to
16199        // `&self`), and calling the accessor twice on the same
16200        // [`Caixa`] must yield references that are pointer-equal (the
16201        // underlying byte-buffer is the storage `LimitsSpec`'s
16202        // allocation, not a fresh copy) as well as value-equal
16203        // (idempotent, no side effects on `&self`).
16204        //
16205        // Pins against a future silent detour that returned an owned
16206        // `LimitsSpec` (which would type-check via the `Clone` impl
16207        // but silently clone on every call), a `&LimitsSpec` panic-
16208        // return on the `None` arm (which would collapse the load-
16209        // bearing `Option` presence-bit into a runtime panic), or a
16210        // one-arm-only accessor that returned a saturating composite
16211        // on some sentinel input.
16212        use crate::LimitsSpec;
16213        use std::time::Duration;
16214        for limits in [
16215            Some(LimitsSpec::default()),
16216            Some(LimitsSpec {
16217                memory: Some(64 * 1024 * 1024),
16218                fuel: Some(1_000_000),
16219                wall_clock: Some(Duration::from_secs(30)),
16220                cpu: Some(500),
16221            }),
16222        ] {
16223            let c = caixa_with_limits(limits.clone());
16224            let first = c.limits().unwrap();
16225            let second = c.limits().unwrap();
16226            assert_eq!(
16227                first, second,
16228                "Caixa::limits must be idempotent — two successive \
16229                 calls on the same &self must return the same \
16230                 &LimitsSpec",
16231            );
16232            assert!(
16233                std::ptr::eq(first, second),
16234                "Caixa::limits must borrow the underlying \
16235                 Option<LimitsSpec> storage — two successive calls \
16236                 must return references with the same backing pointer \
16237                 (a fresh LimitsSpec clone would change the pointer \
16238                 on every call)",
16239            );
16240            assert_eq!(
16241                Some(first),
16242                limits.as_ref(),
16243                "Caixa::limits must return :limits verbatim by borrow \
16244                 — got {first:?}, expected {:?}",
16245                limits.as_ref(),
16246            );
16247        }
16248        let c = caixa_with_limits(None);
16249        assert!(
16250            c.limits().is_none(),
16251            "Caixa::limits must return None when :limits is absent — \
16252             the author-omitted arm must project through the \
16253             accessor's Option::None unchanged",
16254        );
16255    }
16256
16257    // ── Caixa::behavior — outer top-level Option<&BehaviorSpec> composite-reference accessor ──
16258
16259    fn caixa_with_behavior(behavior: Option<crate::BehaviorSpec>) -> Caixa {
16260        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16261        c.behavior = behavior;
16262        c
16263    }
16264
16265    #[test]
16266    fn behavior_returns_behavior_option_ref_verbatim_across_permutations() {
16267        // The canonical per-`Caixa` `:behavior` M2 typed-slot outer-
16268        // composite optional-composite-reference-shape pin:
16269        // [`Caixa::behavior`] must return the `:behavior` typed
16270        // `Option<BehaviorSpec>` verbatim as an `Option<&BehaviorSpec>`
16271        // reference over the same backing storage the raw
16272        // `self.behavior.as_ref()` field access borrows from, byte-equal
16273        // across every representative fixture in the accept-set — the
16274        // author-omitted `None` shape (the "runtime-default applies"
16275        // partition every downstream Servico M2 overlay emitter treats
16276        // as "emit nothing"), the empty-composite `Some(BehaviorSpec {
16277        // .. default })` shape ([`BehaviorSpec::is_empty`] holds —
16278        // every per-callback path is `None`, so the peer M2 overlay
16279        // emitter's `.is_empty()`-gated projection still emits nothing
16280        // but the outer presence-bit is `Some`, so
16281        // [`Caixa::declared_servico_slots`] still pushes the
16282        // `M2_AUTHOR_KEY_BEHAVIOR` label), a single-callback fixture
16283        // (only `:on-state-change` set — the canonical shape a caixa
16284        // that only wires the hot-upgrade migration path carries), and
16285        // a fully-populated composite (every per-callback path set —
16286        // the canonical shape a fully-instrumented gen_server-shaped
16287        // Servico carries).
16288        //
16289        // Peer of the sibling
16290        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16291        // (b2bd9d7) opening fixture-family + reference-identity +
16292        // presence-bit tetrad pin on the outer top-level [`Caixa`]
16293        // `Option<&Composite>`-return sub-family — extended here to the
16294        // second axis of that sub-family so both of the currently-lifted
16295        // M2 Servico-runtime `Option<&Composite>` slots (`:limits` /
16296        // `:behavior`) carry the same "byte-equal, borrow-shared,
16297        // presence-bit-preserved" outer-accessor discipline.
16298        //
16299        // Pins against a future silent detour that returned a fresh-
16300        // cloned [`crate::BehaviorSpec`] copy (which would type-check
16301        // via the `Clone` impl but silently break every downstream
16302        // caller that relied on the reference sharing the composite's
16303        // backing identity), a reference to an operator-resolved
16304        // overlay (a future per-cluster `:behavior-overrides` slot —
16305        // its resolution must land at exactly this accessor body, not
16306        // silently divert the raw slot away from a second consumer), a
16307        // `None` → `Some(BehaviorSpec::default)` cluster-default
16308        // projection (which would collapse the load-bearing
16309        // "author-omitted `:behavior` ⇒ runtime-default applies"
16310        // partition the peer [`crate::render::servico_m2_overlay`]
16311        // emitter, the peer [`Caixa::declared_servico_slots`]
16312        // enumerator, and the cross-slot
16313        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
16314        // gate all read), or a callback-shuffled projection (a future
16315        // detour that swapped `on_init` and `on_terminate` through the
16316        // accessor would silently split the paired
16317        // [`crate::StandardLayout::verify`] per-`:behavior` shape gate's
16318        // traversal input from the peer `servico_m2_overlay` emitter's
16319        // projection input from the cross-slot `:state-change`
16320        // composition gate's traversal input).
16321        use crate::BehaviorSpec;
16322        use std::path::PathBuf;
16323        let fixtures: Vec<Option<BehaviorSpec>> = vec![
16324            None,
16325            Some(BehaviorSpec::default()),
16326            Some(BehaviorSpec {
16327                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16328                ..Default::default()
16329            }),
16330            Some(BehaviorSpec {
16331                on_init: Some(PathBuf::from("lib/init.lisp")),
16332                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16333                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16334                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16335                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16336                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16337            }),
16338        ];
16339        for behavior in fixtures {
16340            let c = caixa_with_behavior(behavior.clone());
16341            assert_eq!(
16342                c.behavior(),
16343                behavior.as_ref(),
16344                "Caixa::behavior must return :behavior verbatim (got \
16345                 {:?}, expected {:?})",
16346                c.behavior(),
16347                behavior.as_ref(),
16348            );
16349            match (c.behavior(), c.behavior.as_ref()) {
16350                (Some(a), Some(b)) => assert!(
16351                    std::ptr::eq(a, b),
16352                    "Caixa::behavior accessor and self.behavior.as_ref() \
16353                     field access must borrow the same backing storage \
16354                     — the accessor is the substrate-primitive typed \
16355                     dispatch every downstream Servico-M2-overlay \
16356                     composite consumer must route through, and a \
16357                     reference-identity split would silently break \
16358                     every consumer that relied on the borrow sharing \
16359                     the composite's storage",
16360                ),
16361                (None, None) => {}
16362                _ => panic!(
16363                    "Caixa::behavior presence bit must byte-equal \
16364                     self.behavior.is_some() — a presence-bit drift \
16365                     would silently split the paired \
16366                     StandardLayout::verify per-`:behavior` shape \
16367                     gate's traversal head from the peer \
16368                     render::servico_m2_overlay M2 overlay emitter's \
16369                     traversal head from the cross-slot \
16370                     validate_upgrade_from_against_behavior \
16371                     composition gate's traversal head from the peer \
16372                     Caixa::declared_servico_slots M2 declared-slot \
16373                     enumerator's presence probe",
16374                ),
16375            }
16376            assert_eq!(
16377                c.behavior().is_some(),
16378                c.behavior.is_some(),
16379                "Caixa::behavior().is_some() must byte-equal \
16380                 self.behavior.is_some() — a presence-bit drift would \
16381                 silently split every downstream Option<&BehaviorSpec> \
16382                 consumer's partition on the runtime-default arm",
16383            );
16384        }
16385    }
16386
16387    #[test]
16388    fn declared_servico_slots_behavior_arm_routes_through_accessor() {
16389        // Composition pin: [`Caixa::declared_servico_slots`]'s
16390        // `:behavior` presence-probe arm must key off
16391        // [`Caixa::behavior`], not the raw `self.behavior.is_some()`
16392        // field-probe. Structurally: a `Caixa { behavior:
16393        // Some(BehaviorSpec::default()), .. }` must still push
16394        // `M2_AUTHOR_KEY_BEHAVIOR` onto the declared-slot list (the
16395        // presence bit is `Some`, so the M2 kind-coherence gate must
16396        // surface the slot as "declared" even when every per-callback
16397        // path is unset), and a `Caixa { behavior: None, .. }` must
16398        // NOT push the label (the "author omitted the slot entirely"
16399        // partition). The pair jointly pins the accessor + declared-
16400        // slot enumerator composition: any future silent detour that
16401        // had the accessor collapse `Some(BehaviorSpec::default())`
16402        // to `None` (a `.filter(|b| !b.is_empty())` projection) would
16403        // silently absorb the "declared but empty" arm at the
16404        // accessor boundary and the
16405        // [`crate::LayoutError::ServicoSlotsOnNonServico`]
16406        // kind-coherence gate would silently accept a struct-literal
16407        // `Caixa` carrying the drift.
16408        //
16409        // Peer of the sibling
16410        // `declared_servico_slots_limits_arm_routes_through_accessor`
16411        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16412        // `Option<&LimitsSpec>` arm of the same
16413        // [`Caixa::declared_servico_slots`] M2 declared-slot
16414        // enumerator's traversal — same "the enumerator gate must
16415        // route through the substrate-primitive typed dispatch"
16416        // discipline extended onto the outer top-level [`Caixa`]
16417        // `Option<&BehaviorSpec>`-composition surface.
16418        use crate::BehaviorSpec;
16419        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16420        let slots = c.declared_servico_slots();
16421        assert!(
16422            slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16423            "declared_servico_slots must push M2_AUTHOR_KEY_BEHAVIOR \
16424             when `:behavior` is Some (even for BehaviorSpec::default()) \
16425             — the accessor and the enumerator gate must route through \
16426             the same substrate-primitive typed dispatch on the outer \
16427             :behavior presence bit (got slots={slots:?})",
16428        );
16429        let c = caixa_with_behavior(None);
16430        let slots = c.declared_servico_slots();
16431        assert!(
16432            !slots.contains(&crate::render::M2_AUTHOR_KEY_BEHAVIOR),
16433            "declared_servico_slots must NOT push M2_AUTHOR_KEY_BEHAVIOR \
16434             when `:behavior` is None — the author-omitted arm must \
16435             route through the accessor's None-return unchanged (got \
16436             slots={slots:?})",
16437        );
16438    }
16439
16440    #[test]
16441    fn servico_m2_overlay_behavior_arm_routes_through_accessor() {
16442        // Composition pin: [`crate::render::servico_m2_overlay`]'s
16443        // per-`:behavior` M2 overlay emit arm must key off
16444        // [`Caixa::behavior`], not the raw `&caixa.behavior`
16445        // field-borrow. Structurally: a `Caixa { behavior:
16446        // Some(BehaviorSpec { on_state_change: Some(...), .. default
16447        // }), .. }` must surface the `M2_KEY_BEHAVIOR` key with the
16448        // per-callback `onStateChange` sub-mapping in the overlay, a
16449        // `Caixa { behavior: Some(BehaviorSpec::default()), .. }`
16450        // must omit the key entirely (the `.is_empty()`-gated inner
16451        // arm elides an empty composite even when the outer presence
16452        // bit is `Some`), and a `Caixa { behavior: None, .. }` must
16453        // also omit the key (the "author omitted the slot entirely"
16454        // partition). The three-fixture family jointly pins the
16455        // accessor + M2 overlay emitter composition: any future
16456        // silent detour that had the accessor return a fresh-cloned
16457        // copy on the `Some` arm (a `BehaviorSpec::clone()`
16458        // projection) would silently break the reference-identity
16459        // pin the peer per-callback `serde_yaml::to_value(behavior)`
16460        // projection reads from.
16461        //
16462        // Peer of the sibling
16463        // `servico_m2_overlay_limits_arm_routes_through_accessor`
16464        // (b2bd9d7) composition pin on the sibling `:limits` outer-
16465        // `Option<&LimitsSpec>` arm of the same
16466        // [`crate::render::servico_m2_overlay`] M2 overlay emitter's
16467        // traversal — same "the emitter must route through the
16468        // substrate-primitive typed dispatch on the outer composite"
16469        // discipline extended onto the outer top-level [`Caixa`]
16470        // `Option<&BehaviorSpec>`-composition surface.
16471        use crate::BehaviorSpec;
16472        use crate::render::{M2_KEY_BEHAVIOR, servico_m2_overlay};
16473        use std::path::PathBuf;
16474        let c = caixa_with_behavior(Some(BehaviorSpec {
16475            on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16476            ..Default::default()
16477        }));
16478        let overlay = servico_m2_overlay(&c).unwrap();
16479        assert!(
16480            overlay.contains_key(M2_KEY_BEHAVIOR),
16481            "servico_m2_overlay must surface M2_KEY_BEHAVIOR when \
16482             `:behavior` carries a non-empty composite — the accessor \
16483             and the M2 overlay emitter must route through the same \
16484             substrate-primitive typed dispatch on the outer :behavior \
16485             composite (got overlay={overlay:?})",
16486        );
16487        let c = caixa_with_behavior(Some(BehaviorSpec::default()));
16488        let overlay = servico_m2_overlay(&c).unwrap();
16489        assert!(
16490            !overlay.contains_key(M2_KEY_BEHAVIOR),
16491            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16492             `:behavior` is Some(BehaviorSpec::default()) — the empty \
16493             composite's `.is_empty()`-gated inner arm must elide the \
16494             key regardless of the outer presence bit (got \
16495             overlay={overlay:?})",
16496        );
16497        let c = caixa_with_behavior(None);
16498        let overlay = servico_m2_overlay(&c).unwrap();
16499        assert!(
16500            !overlay.contains_key(M2_KEY_BEHAVIOR),
16501            "servico_m2_overlay must omit M2_KEY_BEHAVIOR when \
16502             `:behavior` is None — the author-omitted arm must route \
16503             through the accessor's None-return unchanged (got \
16504             overlay={overlay:?})",
16505        );
16506    }
16507
16508    #[test]
16509    fn behavior_projects_option_ref_by_borrow() {
16510        // The by-borrow pin: [`Caixa::behavior`] returns
16511        // `Option<&BehaviorSpec>` by borrow — the returned reference
16512        // borrows the underlying `Option<BehaviorSpec>` storage of the
16513        // `:behavior` slot and the accessor must not clone the backing
16514        // composite on every call. Peer of the sibling
16515        // `limits_projects_option_ref_by_borrow` (b2bd9d7) by-borrow
16516        // pin on the outer top-level [`Caixa`] `Option<&Composite>`-
16517        // return sub-family — extended here to the second axis of the
16518        // same sub-family: the accessor's returned reference must
16519        // borrow from `&self` (the returned reference's lifetime is
16520        // tied to `&self`), and calling the accessor twice on the same
16521        // [`Caixa`] must yield references that are pointer-equal (the
16522        // underlying byte-buffer is the storage `BehaviorSpec`'s
16523        // allocation, not a fresh copy) as well as value-equal
16524        // (idempotent, no side effects on `&self`).
16525        //
16526        // Pins against a future silent detour that returned an owned
16527        // `BehaviorSpec` (which would type-check via the `Clone` impl
16528        // but silently clone on every call), a `&BehaviorSpec` panic-
16529        // return on the `None` arm (which would collapse the load-
16530        // bearing `Option` presence-bit into a runtime panic), or a
16531        // one-arm-only accessor that returned a saturating composite
16532        // on some sentinel input.
16533        use crate::BehaviorSpec;
16534        use std::path::PathBuf;
16535        for behavior in [
16536            Some(BehaviorSpec::default()),
16537            Some(BehaviorSpec {
16538                on_init: Some(PathBuf::from("lib/init.lisp")),
16539                on_call: Some(PathBuf::from("lib/handlers.lisp")),
16540                on_cast: Some(PathBuf::from("lib/handlers.lisp")),
16541                on_info: Some(PathBuf::from("lib/handlers.lisp")),
16542                on_state_change: Some(PathBuf::from("lib/migrations.lisp")),
16543                on_terminate: Some(PathBuf::from("lib/cleanup.lisp")),
16544            }),
16545        ] {
16546            let c = caixa_with_behavior(behavior.clone());
16547            let first = c.behavior().unwrap();
16548            let second = c.behavior().unwrap();
16549            assert_eq!(
16550                first, second,
16551                "Caixa::behavior must be idempotent — two successive \
16552                 calls on the same &self must return the same \
16553                 &BehaviorSpec",
16554            );
16555            assert!(
16556                std::ptr::eq(first, second),
16557                "Caixa::behavior must borrow the underlying \
16558                 Option<BehaviorSpec> storage — two successive calls \
16559                 must return references with the same backing pointer \
16560                 (a fresh BehaviorSpec clone would change the pointer \
16561                 on every call)",
16562            );
16563            assert_eq!(
16564                Some(first),
16565                behavior.as_ref(),
16566                "Caixa::behavior must return :behavior verbatim by \
16567                 borrow — got {first:?}, expected {:?}",
16568                behavior.as_ref(),
16569            );
16570        }
16571        let c = caixa_with_behavior(None);
16572        assert!(
16573            c.behavior().is_none(),
16574            "Caixa::behavior must return None when :behavior is absent \
16575             — the author-omitted arm must project through the \
16576             accessor's Option::None unchanged",
16577        );
16578    }
16579
16580    // ── Caixa::politicas — outer top-level Option<&MeshPolicy> composite-reference accessor ──
16581
16582    fn caixa_aplicacao_with_politicas(politicas: Option<crate::aplicacao::MeshPolicy>) -> Caixa {
16583        use crate::aplicacao::{Membro, WitContract};
16584        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16585        c.kind = CaixaKind::Aplicacao;
16586        c.membros = vec![Membro {
16587            caixa: "a".into(),
16588            versao: "^0.1".into(),
16589        }];
16590        c.contratos = vec![WitContract {
16591            de: "a".into(),
16592            para: "a".into(),
16593            wit: "wasi:http/proxy".into(),
16594            endpoint: Some("/x".into()),
16595            subject: None,
16596            slot: None,
16597        }];
16598        c.politicas = politicas;
16599        c
16600    }
16601
16602    #[test]
16603    fn politicas_returns_politicas_option_ref_verbatim_across_permutations() {
16604        // The canonical per-`Caixa` `:politicas` M3 mesh-slot outer-
16605        // composite optional-composite-reference-shape pin:
16606        // [`Caixa::politicas`] must return the `:politicas` typed
16607        // `Option<MeshPolicy>` verbatim as an `Option<&MeshPolicy>`
16608        // reference over the same backing storage the raw
16609        // `self.politicas.as_ref()` field access borrows from,
16610        // byte-equal across every representative fixture in the
16611        // accept-set — the author-omitted `None` shape (the "cluster-
16612        // default applies" partition every downstream mesh-artifact
16613        // emitter treats as "emit no `:politicas` overlay"), the
16614        // empty-composite `Some(MeshPolicy { .. default })` shape
16615        // ([`crate::aplicacao::MeshPolicy::is_empty`] holds — every
16616        // per-axis mesh-policy scalar is `None`, so the peer inner
16617        // [`crate::AplicacaoSpec::politicas`] `.is_empty()`-gated
16618        // caixa-mesh overlay elides every per-axis emit but the outer
16619        // presence-bit is `Some`, so [`Caixa::declared_mesh_slots`]
16620        // still pushes the `M3_AUTHOR_KEY_POLITICAS` label), a
16621        // single-axis fixture (only `:timeout` set — the canonical
16622        // shape a latency-sensitive Aplicacao carries), and a
16623        // fully-populated composite (every per-axis mesh-policy
16624        // scalar set — the canonical shape a fully-governed
16625        // Aplicacao carries).
16626        //
16627        // Pins against a future silent detour that returned a fresh-
16628        // cloned [`crate::aplicacao::MeshPolicy`] copy (which would
16629        // type-check via the `Clone` impl but silently break every
16630        // downstream caller that relied on the reference sharing the
16631        // composite's backing identity), a reference to an operator-
16632        // resolved overlay (the future per-cluster
16633        // `:politicas-overrides` slot — its resolution must land at
16634        // exactly this accessor body, not silently divert the raw
16635        // slot away from the peer [`Caixa::declared_mesh_slots`]
16636        // enumerator's presence probe), a
16637        // `None` → `Some(MeshPolicy::default)` cluster-default
16638        // projection (which would collapse the load-bearing
16639        // "author-omitted `:politicas` ⇒ cluster-default applies"
16640        // partition the peer [`Caixa::declared_mesh_slots`]
16641        // enumerator and the peer [`Caixa::aplicacao_view`]
16642        // Aplicacao-composition seed both read), or an axis-shuffled
16643        // projection (a future detour that swapped `timeout` and
16644        // `retries` through the accessor would silently split the
16645        // paired [`Caixa::aplicacao_view`] seed's fold input from the
16646        // sibling M3 mesh-artifact emitter's projection input).
16647        //
16648        // Third outer top-level [`Caixa`] `Option<&Composite>`-return
16649        // composite-reference accessor pin on the substrate primitive
16650        // — peer of the sibling
16651        // `limits_returns_limits_option_ref_verbatim_across_permutations`
16652        // (b2bd9d7) and
16653        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
16654        // (35d8b52) opening tetrad pins on the outer top-level
16655        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
16656        // here to the first of the three M3 mesh-slot axes so the
16657        // opening third of the outer `Option<&Composite>` sub-family
16658        // carries the same "byte-equal, borrow-shared, presence-bit-
16659        // preserved" outer-accessor discipline.
16660        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16661        use std::time::Duration;
16662        let fixtures: Vec<Option<MeshPolicy>> = vec![
16663            None,
16664            Some(MeshPolicy::default()),
16665            Some(MeshPolicy {
16666                timeout: Some(Duration::from_secs(30)),
16667                ..Default::default()
16668            }),
16669            Some(MeshPolicy {
16670                timeout: Some(Duration::from_secs(30)),
16671                retries: Some(3),
16672                circuit_breaker: Some(CircuitBreaker {
16673                    max_failures: 5,
16674                    window: Duration::from_secs(60),
16675                }),
16676                mtls_required: Some(true),
16677                rate_limit: Some(RateLimit {
16678                    rate: 100,
16679                    window: Duration::from_secs(1),
16680                }),
16681            }),
16682        ];
16683        for politicas in fixtures {
16684            let c = caixa_aplicacao_with_politicas(politicas.clone());
16685            assert_eq!(
16686                c.politicas(),
16687                politicas.as_ref(),
16688                "Caixa::politicas must return :politicas verbatim (got \
16689                 {:?}, expected {:?})",
16690                c.politicas(),
16691                politicas.as_ref(),
16692            );
16693            match (c.politicas(), c.politicas.as_ref()) {
16694                (Some(a), Some(b)) => assert!(
16695                    std::ptr::eq(a, b),
16696                    "Caixa::politicas accessor and self.politicas.as_ref() \
16697                     field access must borrow the same backing storage \
16698                     — the accessor is the substrate-primitive typed \
16699                     dispatch every downstream Aplicacao-mesh-overlay \
16700                     composite consumer must route through, and a \
16701                     reference-identity split would silently break \
16702                     every consumer that relied on the borrow sharing \
16703                     the composite's storage",
16704                ),
16705                (None, None) => {}
16706                _ => panic!(
16707                    "Caixa::politicas presence bit must byte-equal \
16708                     self.politicas.is_some() — a presence-bit drift \
16709                     would silently split the paired \
16710                     Caixa::aplicacao_view Aplicacao-composition seed's \
16711                     traversal head from the peer \
16712                     Caixa::declared_mesh_slots M3 declared-slot \
16713                     enumerator's presence probe",
16714                ),
16715            }
16716            assert_eq!(
16717                c.politicas().is_some(),
16718                c.politicas.is_some(),
16719                "Caixa::politicas().is_some() must byte-equal \
16720                 self.politicas.is_some() — a presence-bit drift would \
16721                 silently split every downstream Option<&MeshPolicy> \
16722                 consumer's partition on the cluster-default arm",
16723            );
16724        }
16725    }
16726
16727    #[test]
16728    fn declared_mesh_slots_politicas_arm_routes_through_accessor() {
16729        // Composition pin: [`Caixa::declared_mesh_slots`]'s
16730        // `:politicas` presence-probe arm must key off
16731        // [`Caixa::politicas`], not the raw `self.politicas.is_some()`
16732        // field-probe. Structurally: a `Caixa { politicas:
16733        // Some(MeshPolicy::default()), .. }` must still push
16734        // `M3_AUTHOR_KEY_POLITICAS` onto the declared-slot list (the
16735        // presence bit is `Some`, so the M3 kind-coherence gate must
16736        // surface the slot as "declared" even when every per-axis
16737        // scalar is unset), and a `Caixa { politicas: None, .. }` must
16738        // NOT push the label (the "author omitted the slot entirely"
16739        // partition). The pair jointly pins the accessor + declared-
16740        // slot enumerator composition: any future silent detour that
16741        // had the accessor collapse `Some(MeshPolicy::default())` to
16742        // `None` (a `.filter(|p| !p.is_empty())` projection) would
16743        // silently absorb the "declared but empty" arm at the
16744        // accessor boundary and the
16745        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
16746        // coherence gate would silently accept a struct-literal
16747        // `Caixa` carrying the drift.
16748        //
16749        // Peer of the sibling
16750        // `declared_servico_slots_limits_arm_routes_through_accessor`
16751        // (b2bd9d7) and
16752        // `declared_servico_slots_behavior_arm_routes_through_accessor`
16753        // (35d8b52) composition pins on the sibling `:limits` /
16754        // `:behavior` outer-`Option<&Composite>` arms of the peer
16755        // [`Caixa::declared_servico_slots`] M2 declared-slot
16756        // enumerator's traversal — same "the enumerator gate must
16757        // route through the substrate-primitive typed dispatch"
16758        // discipline extended onto the outer top-level [`Caixa`] M3
16759        // mesh-slot family so the [`Caixa::declared_mesh_slots`]
16760        // enumerator carries the same routing invariant as its M2
16761        // sibling.
16762        use crate::aplicacao::MeshPolicy;
16763        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16764        let slots = c.declared_mesh_slots();
16765        assert!(
16766            slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16767            "declared_mesh_slots must push M3_AUTHOR_KEY_POLITICAS \
16768             when `:politicas` is Some (even for MeshPolicy::default()) \
16769             — the accessor and the enumerator gate must route through \
16770             the same substrate-primitive typed dispatch on the outer \
16771             :politicas presence bit (got slots={slots:?})",
16772        );
16773        let c = caixa_aplicacao_with_politicas(None);
16774        let slots = c.declared_mesh_slots();
16775        assert!(
16776            !slots.contains(&crate::render::M3_AUTHOR_KEY_POLITICAS),
16777            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_POLITICAS \
16778             when `:politicas` is None — the author-omitted arm must \
16779             route through the accessor's None-return unchanged (got \
16780             slots={slots:?})",
16781        );
16782    }
16783
16784    #[test]
16785    fn aplicacao_view_politicas_arm_folds_through_accessor() {
16786        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:politicas`
16787        // Aplicacao-composition seed must fold through
16788        // [`Caixa::politicas`], not the raw
16789        // `self.politicas.clone().unwrap_or_default()` field-borrow.
16790        // Structurally: a `Caixa { politicas: Some(MeshPolicy {
16791        // timeout: Some(30s), .. default }), kind: Aplicacao, .. }`
16792        // must surface a projected [`crate::AplicacaoSpec`] whose
16793        // `politicas().timeout()` field byte-equals the outer
16794        // composite's `timeout` scalar (the fold must project the
16795        // authored composite verbatim), a `Caixa { politicas:
16796        // Some(MeshPolicy::default()), kind: Aplicacao, .. }` must
16797        // surface an [`crate::AplicacaoSpec`] whose `politicas()`
16798        // byte-equals [`crate::aplicacao::MeshPolicy::default`] (the
16799        // fold's empty-composite arm collapses to the same default the
16800        // author-omitted arm does), and a `Caixa { politicas: None,
16801        // kind: Aplicacao, .. }` must surface an
16802        // [`crate::AplicacaoSpec`] whose `politicas()` byte-equals
16803        // [`crate::aplicacao::MeshPolicy::default`] (the "author
16804        // omitted the slot entirely" arm folds through the
16805        // `unwrap_or_default` onto the cluster-default). The triad
16806        // jointly pins the accessor + Aplicacao-composition seed
16807        // composition: any future silent detour that had the accessor
16808        // divert the raw slot away from the seed's fold (an operator-
16809        // resolved overlay's default-fold arm silently differing from
16810        // the raw slot's default-fold arm) would silently split the
16811        // build-time mesh-artifact emission gate from the caixa-mesh
16812        // renderer's Aplicacao-view input at the composition boundary.
16813        use crate::aplicacao::MeshPolicy;
16814        use std::time::Duration;
16815        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy {
16816            timeout: Some(Duration::from_secs(30)),
16817            ..Default::default()
16818        }));
16819        let view = c.aplicacao_view().unwrap();
16820        assert_eq!(
16821            view.politicas().timeout(),
16822            Some(Duration::from_secs(30)),
16823            "Caixa::aplicacao_view must fold the authored :politicas \
16824             :timeout scalar through the accessor verbatim onto the \
16825             projected AplicacaoSpec — a future silent detour at the \
16826             seed's fold arm would surface here as a projected-scalar \
16827             drift (got {:?})",
16828            view.politicas().timeout(),
16829        );
16830        let c = caixa_aplicacao_with_politicas(Some(MeshPolicy::default()));
16831        let view = c.aplicacao_view().unwrap();
16832        assert_eq!(
16833            view.politicas(),
16834            &MeshPolicy::default(),
16835            "Caixa::aplicacao_view must fold Some(MeshPolicy::default()) \
16836             through the accessor onto MeshPolicy::default — the empty- \
16837             composite arm collapses to the same default the author- \
16838             omitted arm does (got {:?})",
16839            view.politicas(),
16840        );
16841        let c = caixa_aplicacao_with_politicas(None);
16842        let view = c.aplicacao_view().unwrap();
16843        assert_eq!(
16844            view.politicas(),
16845            &MeshPolicy::default(),
16846            "Caixa::aplicacao_view must fold None through the accessor's \
16847             unwrap_or_default onto MeshPolicy::default — the author- \
16848             omitted arm must route through the accessor's None-return \
16849             unchanged (got {:?})",
16850            view.politicas(),
16851        );
16852    }
16853
16854    #[test]
16855    fn politicas_projects_option_ref_by_borrow() {
16856        // The by-borrow pin: [`Caixa::politicas`] returns
16857        // `Option<&MeshPolicy>` by borrow — the returned reference
16858        // borrows the underlying `Option<MeshPolicy>` storage of the
16859        // `:politicas` slot and the accessor must not clone the
16860        // backing composite on every call. Peer of the sibling
16861        // `limits_projects_option_ref_by_borrow` (b2bd9d7) and
16862        // `behavior_projects_option_ref_by_borrow` (35d8b52) by-borrow
16863        // pins on the outer top-level [`Caixa`]
16864        // `Option<&Composite>`-return sub-family — extended here to
16865        // the third axis of the same sub-family: the accessor's
16866        // returned reference must borrow from `&self` (the returned
16867        // reference's lifetime is tied to `&self`), and calling the
16868        // accessor twice on the same [`Caixa`] must yield references
16869        // that are pointer-equal (the underlying byte-buffer is the
16870        // storage `MeshPolicy`'s allocation, not a fresh copy) as
16871        // well as value-equal (idempotent, no side effects on
16872        // `&self`).
16873        //
16874        // Pins against a future silent detour that returned an owned
16875        // `MeshPolicy` (which would type-check via the `Clone` impl
16876        // but silently clone on every call), a `&MeshPolicy` panic-
16877        // return on the `None` arm (which would collapse the load-
16878        // bearing `Option` presence-bit into a runtime panic), or a
16879        // one-arm-only accessor that returned a saturating composite
16880        // on some sentinel input.
16881        use crate::aplicacao::{CircuitBreaker, MeshPolicy, RateLimit};
16882        use std::time::Duration;
16883        for politicas in [
16884            Some(MeshPolicy::default()),
16885            Some(MeshPolicy {
16886                timeout: Some(Duration::from_secs(30)),
16887                retries: Some(3),
16888                circuit_breaker: Some(CircuitBreaker {
16889                    max_failures: 5,
16890                    window: Duration::from_secs(60),
16891                }),
16892                mtls_required: Some(true),
16893                rate_limit: Some(RateLimit {
16894                    rate: 100,
16895                    window: Duration::from_secs(1),
16896                }),
16897            }),
16898        ] {
16899            let c = caixa_aplicacao_with_politicas(politicas.clone());
16900            let first = c.politicas().unwrap();
16901            let second = c.politicas().unwrap();
16902            assert_eq!(
16903                first, second,
16904                "Caixa::politicas must be idempotent — two successive \
16905                 calls on the same &self must return the same \
16906                 &MeshPolicy",
16907            );
16908            assert!(
16909                std::ptr::eq(first, second),
16910                "Caixa::politicas must borrow the underlying \
16911                 Option<MeshPolicy> storage — two successive calls \
16912                 must return references with the same backing pointer \
16913                 (a fresh MeshPolicy clone would change the pointer on \
16914                 every call)",
16915            );
16916            assert_eq!(
16917                Some(first),
16918                politicas.as_ref(),
16919                "Caixa::politicas must return :politicas verbatim by \
16920                 borrow — got {first:?}, expected {:?}",
16921                politicas.as_ref(),
16922            );
16923        }
16924        let c = caixa_aplicacao_with_politicas(None);
16925        assert!(
16926            c.politicas().is_none(),
16927            "Caixa::politicas must return None when :politicas is \
16928             absent — the author-omitted arm must project through the \
16929             accessor's Option::None unchanged",
16930        );
16931    }
16932
16933    // ── Caixa::placement — outer top-level Option<&Placement> composite-reference accessor ──
16934
16935    fn caixa_aplicacao_with_placement(placement: Option<crate::aplicacao::Placement>) -> Caixa {
16936        use crate::aplicacao::{Membro, WitContract};
16937        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
16938        c.kind = CaixaKind::Aplicacao;
16939        c.membros = vec![Membro {
16940            caixa: "a".into(),
16941            versao: "^0.1".into(),
16942        }];
16943        c.contratos = vec![WitContract {
16944            de: "a".into(),
16945            para: "a".into(),
16946            wit: "wasi:http/proxy".into(),
16947            endpoint: Some("/x".into()),
16948            subject: None,
16949            slot: None,
16950        }];
16951        c.placement = placement;
16952        c
16953    }
16954
16955    #[test]
16956    fn placement_returns_placement_option_ref_verbatim_across_permutations() {
16957        // The canonical per-`Caixa` `:placement` M3 mesh-slot outer-
16958        // composite optional-composite-reference-shape pin:
16959        // [`Caixa::placement`] must return the `:placement` typed
16960        // `Option<Placement>` verbatim as an `Option<&Placement>`
16961        // reference over the same backing storage the raw
16962        // `self.placement.as_ref()` field access borrows from,
16963        // byte-equal across every representative fixture in the
16964        // accept-set — the author-omitted `None` shape (the
16965        // "cluster-default applies" partition every downstream mesh-
16966        // artifact emitter treats as "emit no `:placement` overlay"),
16967        // the empty-composite `Some(Placement { .. default })` shape
16968        // (`estrategia: SingleNode`, empty clusters, no shard-key /
16969        // affinity — the outer presence-bit is `Some` so
16970        // [`Caixa::declared_mesh_slots`] still pushes the
16971        // `M3_AUTHOR_KEY_PLACEMENT` label), a single-axis
16972        // `Replicated`-on-two-clusters fixture (the canonical shape a
16973        // stateless HTTP Aplicacao carries), and a fully-populated
16974        // `Sharded`-with-shard-key-and-affinity fixture (the canonical
16975        // shape a stateful Akka-style cluster-sharding Aplicacao
16976        // carries).
16977        //
16978        // Pins against a future silent detour that returned a fresh-
16979        // cloned [`crate::aplicacao::Placement`] copy (which would
16980        // type-check via the `Clone` impl but silently break every
16981        // downstream caller that relied on the reference sharing the
16982        // composite's backing identity), a reference to an operator-
16983        // resolved overlay (the future per-cluster
16984        // `:placement-overrides` slot — its resolution must land at
16985        // exactly this accessor body, not silently divert the raw
16986        // slot away from the peer [`Caixa::declared_mesh_slots`]
16987        // enumerator's presence probe), a `None` →
16988        // `Some(Placement::default)` cluster-default projection (which
16989        // would collapse the load-bearing "author-omitted `:placement`
16990        // ⇒ cluster-default applies" partition the peer
16991        // [`Caixa::declared_mesh_slots`] enumerator and the peer
16992        // [`Caixa::aplicacao_view`] Aplicacao-composition seed both
16993        // read), or an axis-shuffled projection (a future detour that
16994        // swapped `clusters` and `affinity` through the accessor would
16995        // silently split the paired [`Caixa::aplicacao_view`] seed's
16996        // fold input from the sibling M3 mesh-artifact emitter's
16997        // projection input).
16998        //
16999        // Fourth outer top-level [`Caixa`] `Option<&Composite>`-return
17000        // composite-reference accessor pin on the substrate primitive
17001        // — peer of the sibling
17002        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17003        // (b2bd9d7),
17004        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17005        // (35d8b52), and
17006        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17007        // (5d23d29) opening triad pins on the outer top-level
17008        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17009        // here to the second of the three M3 mesh-slot axes so the
17010        // opening four-fifths of the outer `Option<&Composite>` sub-
17011        // family carries the same "byte-equal, borrow-shared,
17012        // presence-bit-preserved" outer-accessor discipline.
17013        use crate::aplicacao::{Placement, PlacementStrategy};
17014        let fixtures: Vec<Option<Placement>> = vec![
17015            None,
17016            Some(Placement::default()),
17017            Some(Placement {
17018                estrategia: PlacementStrategy::Replicated,
17019                clusters: vec!["rio".into(), "sao-paulo".into()],
17020                affinity: None,
17021                shard_key: None,
17022            }),
17023            Some(Placement {
17024                estrategia: PlacementStrategy::Sharded,
17025                clusters: vec!["rio".into(), "sao-paulo".into(), "brasilia".into()],
17026                affinity: Some("data-locality".into()),
17027                shard_key: Some("$tenantId".into()),
17028            }),
17029        ];
17030        for placement in fixtures {
17031            let c = caixa_aplicacao_with_placement(placement.clone());
17032            assert_eq!(
17033                c.placement(),
17034                placement.as_ref(),
17035                "Caixa::placement must return :placement verbatim (got \
17036                 {:?}, expected {:?})",
17037                c.placement(),
17038                placement.as_ref(),
17039            );
17040            match (c.placement(), c.placement.as_ref()) {
17041                (Some(a), Some(b)) => assert!(
17042                    std::ptr::eq(a, b),
17043                    "Caixa::placement accessor and self.placement.as_ref() \
17044                     field access must borrow the same backing storage \
17045                     — the accessor is the substrate-primitive typed \
17046                     dispatch every downstream Aplicacao-distribution- \
17047                     overlay composite consumer must route through, and \
17048                     a reference-identity split would silently break \
17049                     every consumer that relied on the borrow sharing \
17050                     the composite's storage",
17051                ),
17052                (None, None) => {}
17053                _ => panic!(
17054                    "Caixa::placement presence bit must byte-equal \
17055                     self.placement.is_some() — a presence-bit drift \
17056                     would silently split the paired \
17057                     Caixa::aplicacao_view Aplicacao-composition seed's \
17058                     traversal head from the peer \
17059                     Caixa::declared_mesh_slots M3 declared-slot \
17060                     enumerator's presence probe",
17061                ),
17062            }
17063            assert_eq!(
17064                c.placement().is_some(),
17065                c.placement.is_some(),
17066                "Caixa::placement().is_some() must byte-equal \
17067                 self.placement.is_some() — a presence-bit drift would \
17068                 silently split every downstream Option<&Placement> \
17069                 consumer's partition on the cluster-default arm",
17070            );
17071        }
17072    }
17073
17074    #[test]
17075    fn declared_mesh_slots_placement_arm_routes_through_accessor() {
17076        // Composition pin: [`Caixa::declared_mesh_slots`]'s
17077        // `:placement` presence-probe arm must key off
17078        // [`Caixa::placement`], not the raw `self.placement.is_some()`
17079        // field-probe. Structurally: a `Caixa { placement:
17080        // Some(Placement::default()), .. }` must still push
17081        // `M3_AUTHOR_KEY_PLACEMENT` onto the declared-slot list (the
17082        // presence bit is `Some`, so the M3 kind-coherence gate must
17083        // surface the slot as "declared" even when every per-axis
17084        // scalar defers to the cluster-default arm), and a `Caixa {
17085        // placement: None, .. }` must NOT push the label (the "author
17086        // omitted the slot entirely" partition). The pair jointly pins
17087        // the accessor + declared-slot enumerator composition: any
17088        // future silent detour that had the accessor collapse
17089        // `Some(Placement::default())` to `None` (a `.filter(|p|
17090        // p.clusters().is_empty().not())` projection) would silently
17091        // absorb the "declared but empty" arm at the accessor boundary
17092        // and the [`crate::LayoutError::MeshSlotsOnNonAplicacao`]
17093        // kind-coherence gate would silently accept a struct-literal
17094        // `Caixa` carrying the drift.
17095        //
17096        // Peer of the sibling
17097        // `declared_servico_slots_limits_arm_routes_through_accessor`
17098        // (b2bd9d7),
17099        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17100        // (35d8b52), and
17101        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17102        // (5d23d29) composition pins on the sibling `:limits` /
17103        // `:behavior` / `:politicas` outer-`Option<&Composite>` arms
17104        // — same "the enumerator gate must route through the
17105        // substrate-primitive typed dispatch" discipline extended onto
17106        // the second of the three M3 mesh-slot axes so the
17107        // [`Caixa::declared_mesh_slots`] enumerator carries the same
17108        // routing invariant on the `:placement` arm as the peer
17109        // `:politicas` arm.
17110        use crate::aplicacao::Placement;
17111        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
17112        let slots = c.declared_mesh_slots();
17113        assert!(
17114            slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
17115            "declared_mesh_slots must push M3_AUTHOR_KEY_PLACEMENT \
17116             when `:placement` is Some (even for Placement::default()) \
17117             — the accessor and the enumerator gate must route through \
17118             the same substrate-primitive typed dispatch on the outer \
17119             :placement presence bit (got slots={slots:?})",
17120        );
17121        let c = caixa_aplicacao_with_placement(None);
17122        let slots = c.declared_mesh_slots();
17123        assert!(
17124            !slots.contains(&crate::render::M3_AUTHOR_KEY_PLACEMENT),
17125            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_PLACEMENT \
17126             when `:placement` is None — the author-omitted arm must \
17127             route through the accessor's None-return unchanged (got \
17128             slots={slots:?})",
17129        );
17130    }
17131
17132    #[test]
17133    fn aplicacao_view_placement_arm_folds_through_accessor() {
17134        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:placement`
17135        // Aplicacao-composition seed must fold through
17136        // [`Caixa::placement`], not the raw
17137        // `self.placement.clone().unwrap_or_default()` field-borrow.
17138        // Structurally: a `Caixa { placement: Some(Placement {
17139        // estrategia: Replicated, clusters: ["rio"], .. default }),
17140        // kind: Aplicacao, .. }` must surface a projected
17141        // [`crate::AplicacaoSpec`] whose `placement().estrategia()` +
17142        // `placement().clusters()` byte-equal the outer composite's
17143        // authored values (the fold must project the authored
17144        // composite verbatim), a `Caixa { placement:
17145        // Some(Placement::default()), kind: Aplicacao, .. }` must
17146        // surface an [`crate::AplicacaoSpec`] whose `placement()`
17147        // byte-equals [`crate::aplicacao::Placement::default`] (the
17148        // fold's empty-composite arm collapses to the same default
17149        // the author-omitted arm does), and a `Caixa { placement:
17150        // None, kind: Aplicacao, .. }` must surface an
17151        // [`crate::AplicacaoSpec`] whose `placement()` byte-equals
17152        // [`crate::aplicacao::Placement::default`] (the "author
17153        // omitted the slot entirely" arm folds through the
17154        // `unwrap_or_default` onto the cluster-default). The triad
17155        // jointly pins the accessor + Aplicacao-composition seed
17156        // composition: any future silent detour that had the accessor
17157        // divert the raw slot away from the seed's fold (an operator-
17158        // resolved overlay's default-fold arm silently differing from
17159        // the raw slot's default-fold arm) would silently split the
17160        // build-time distribution-artifact emission gate from the
17161        // caixa-mesh renderer's Aplicacao-view input at the
17162        // composition boundary.
17163        use crate::aplicacao::{Placement, PlacementStrategy};
17164        let c = caixa_aplicacao_with_placement(Some(Placement {
17165            estrategia: PlacementStrategy::Replicated,
17166            clusters: vec!["rio".into()],
17167            affinity: None,
17168            shard_key: None,
17169        }));
17170        let view = c.aplicacao_view().unwrap();
17171        assert_eq!(
17172            view.placement().estrategia(),
17173            PlacementStrategy::Replicated,
17174            "Caixa::aplicacao_view must fold the authored :placement \
17175             :estrategia scalar through the accessor verbatim onto the \
17176             projected AplicacaoSpec — a future silent detour at the \
17177             seed's fold arm would surface here as a projected-scalar \
17178             drift (got {:?})",
17179            view.placement().estrategia(),
17180        );
17181        assert_eq!(
17182            view.placement().clusters(),
17183            &["rio"],
17184            "Caixa::aplicacao_view must fold the authored :placement \
17185             :clusters list through the accessor verbatim onto the \
17186             projected AplicacaoSpec — a future silent detour at the \
17187             seed's fold arm would surface here as a projected-list \
17188             drift (got {:?})",
17189            view.placement().clusters(),
17190        );
17191        let c = caixa_aplicacao_with_placement(Some(Placement::default()));
17192        let view = c.aplicacao_view().unwrap();
17193        assert_eq!(
17194            view.placement(),
17195            &Placement::default(),
17196            "Caixa::aplicacao_view must fold Some(Placement::default()) \
17197             through the accessor onto Placement::default — the empty- \
17198             composite arm collapses to the same default the author- \
17199             omitted arm does (got {:?})",
17200            view.placement(),
17201        );
17202        let c = caixa_aplicacao_with_placement(None);
17203        let view = c.aplicacao_view().unwrap();
17204        assert_eq!(
17205            view.placement(),
17206            &Placement::default(),
17207            "Caixa::aplicacao_view must fold None through the accessor's \
17208             unwrap_or_default onto Placement::default — the author- \
17209             omitted arm must route through the accessor's None-return \
17210             unchanged (got {:?})",
17211            view.placement(),
17212        );
17213    }
17214
17215    #[test]
17216    fn placement_projects_option_ref_by_borrow() {
17217        // The by-borrow pin: [`Caixa::placement`] returns
17218        // `Option<&Placement>` by borrow — the returned reference
17219        // borrows the underlying `Option<Placement>` storage of the
17220        // `:placement` slot and the accessor must not clone the
17221        // backing composite on every call. Peer of the sibling
17222        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17223        // `behavior_projects_option_ref_by_borrow` (35d8b52), and
17224        // `politicas_projects_option_ref_by_borrow` (5d23d29) by-borrow
17225        // pins on the outer top-level [`Caixa`]
17226        // `Option<&Composite>`-return sub-family — extended here to
17227        // the fourth axis of the same sub-family: the accessor's
17228        // returned reference must borrow from `&self` (the returned
17229        // reference's lifetime is tied to `&self`), and calling the
17230        // accessor twice on the same [`Caixa`] must yield references
17231        // that are pointer-equal (the underlying byte-buffer is the
17232        // storage `Placement`'s allocation, not a fresh copy) as well
17233        // as value-equal (idempotent, no side effects on `&self`).
17234        //
17235        // Pins against a future silent detour that returned an owned
17236        // `Placement` (which would type-check via the `Clone` impl
17237        // but silently clone on every call), a `&Placement` panic-
17238        // return on the `None` arm (which would collapse the load-
17239        // bearing `Option` presence-bit into a runtime panic), or a
17240        // one-arm-only accessor that returned a saturating composite
17241        // on some sentinel input.
17242        use crate::aplicacao::{Placement, PlacementStrategy};
17243        for placement in [
17244            Some(Placement::default()),
17245            Some(Placement {
17246                estrategia: PlacementStrategy::Sharded,
17247                clusters: vec!["rio".into(), "sao-paulo".into()],
17248                affinity: Some("data-locality".into()),
17249                shard_key: Some("$tenantId".into()),
17250            }),
17251        ] {
17252            let c = caixa_aplicacao_with_placement(placement.clone());
17253            let first = c.placement().unwrap();
17254            let second = c.placement().unwrap();
17255            assert_eq!(
17256                first, second,
17257                "Caixa::placement must be idempotent — two successive \
17258                 calls on the same &self must return the same \
17259                 &Placement",
17260            );
17261            assert!(
17262                std::ptr::eq(first, second),
17263                "Caixa::placement must borrow the underlying \
17264                 Option<Placement> storage — two successive calls \
17265                 must return references with the same backing pointer \
17266                 (a fresh Placement clone would change the pointer on \
17267                 every call)",
17268            );
17269            assert_eq!(
17270                Some(first),
17271                placement.as_ref(),
17272                "Caixa::placement must return :placement verbatim by \
17273                 borrow — got {first:?}, expected {:?}",
17274                placement.as_ref(),
17275            );
17276        }
17277        let c = caixa_aplicacao_with_placement(None);
17278        assert!(
17279            c.placement().is_none(),
17280            "Caixa::placement must return None when :placement is \
17281             absent — the author-omitted arm must project through the \
17282             accessor's Option::None unchanged",
17283        );
17284    }
17285
17286    // ── Caixa::entrada — outer top-level Option<&Entrada> composite-reference accessor ──
17287
17288    fn caixa_aplicacao_with_entrada(entrada: Option<crate::aplicacao::Entrada>) -> Caixa {
17289        use crate::aplicacao::{Membro, WitContract};
17290        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17291        c.kind = CaixaKind::Aplicacao;
17292        c.membros = vec![Membro {
17293            caixa: "a".into(),
17294            versao: "^0.1".into(),
17295        }];
17296        c.contratos = vec![WitContract {
17297            de: "a".into(),
17298            para: "a".into(),
17299            wit: "wasi:http/proxy".into(),
17300            endpoint: Some("/x".into()),
17301            subject: None,
17302            slot: None,
17303        }];
17304        c.entrada = entrada;
17305        c
17306    }
17307
17308    #[test]
17309    fn entrada_returns_entrada_option_ref_verbatim_across_permutations() {
17310        // The canonical per-`Caixa` `:entrada` M3 mesh-slot outer-
17311        // composite optional-composite-reference-shape pin:
17312        // [`Caixa::entrada`] must return the `:entrada` typed
17313        // `Option<Entrada>` verbatim as an `Option<&Entrada>`
17314        // reference over the same backing storage the raw
17315        // `self.entrada.as_ref()` field access borrows from,
17316        // byte-equal across every representative fixture in the
17317        // accept-set — the author-omitted `None` shape (the
17318        // "cluster-internal Aplicacao" partition every downstream
17319        // Gateway-API emitter treats as "emit no listener + no
17320        // HTTPRoute"), a bare-`host`/`para` minimum-composite fixture
17321        // (empty `paths` — the resolved-paths fallback the peer
17322        // [`crate::aplicacao::Entrada::resolved_paths`] cascade folds
17323        // onto the substrate catch-all), and a fully-populated
17324        // multi-path-with-non-default-port fixture (the canonical
17325        // shape a public HTTP Aplicacao carries).
17326        //
17327        // Pins against a future silent detour that returned a fresh-
17328        // cloned [`crate::aplicacao::Entrada`] copy (which would
17329        // type-check via the `Clone` impl but silently break every
17330        // downstream caller that relied on the reference sharing the
17331        // composite's backing identity), a reference to an operator-
17332        // resolved overlay (the future per-cluster
17333        // `:entrada-overrides` slot — its resolution must land at
17334        // exactly this accessor body, not silently divert the raw
17335        // slot away from the peer [`Caixa::declared_mesh_slots`]
17336        // enumerator's presence probe), or an axis-shuffled projection
17337        // (a future detour that swapped `host` and `para` through the
17338        // accessor would silently split the paired
17339        // [`Caixa::aplicacao_view`] seed's forward input from the
17340        // sibling M3 gateway-artifact emitter's projection input).
17341        //
17342        // Fifth and final outer top-level [`Caixa`]
17343        // `Option<&Composite>`-return composite-reference accessor pin
17344        // on the substrate primitive — peer of the sibling
17345        // `limits_returns_limits_option_ref_verbatim_across_permutations`
17346        // (b2bd9d7),
17347        // `behavior_returns_behavior_option_ref_verbatim_across_permutations`
17348        // (35d8b52),
17349        // `politicas_returns_politicas_option_ref_verbatim_across_permutations`
17350        // (5d23d29), and
17351        // `placement_returns_placement_option_ref_verbatim_across_permutations`
17352        // (4fb8074) opening tetrad pins on the outer top-level
17353        // [`Caixa`] `Option<&Composite>`-return sub-family — extended
17354        // here to the third and final M3 mesh-slot axis so the closed
17355        // outer `Option<&Composite>` sub-family carries the same
17356        // "byte-equal, borrow-shared, presence-bit-preserved" outer-
17357        // accessor discipline across all five arms.
17358        use crate::aplicacao::Entrada;
17359        let fixtures: Vec<Option<Entrada>> = vec![
17360            None,
17361            Some(Entrada {
17362                host: "checkout.quero.cloud".into(),
17363                para: "gateway".into(),
17364                paths: Vec::new(),
17365                port: crate::DEFAULT_SERVICO_PORT,
17366            }),
17367            Some(Entrada {
17368                host: "api.pleme.io".into(),
17369                para: "public-api".into(),
17370                paths: vec!["/v1".into(), "/v2".into()],
17371                port: 8080,
17372            }),
17373        ];
17374        for entrada in fixtures {
17375            let c = caixa_aplicacao_with_entrada(entrada.clone());
17376            assert_eq!(
17377                c.entrada(),
17378                entrada.as_ref(),
17379                "Caixa::entrada must return :entrada verbatim (got \
17380                 {:?}, expected {:?})",
17381                c.entrada(),
17382                entrada.as_ref(),
17383            );
17384            match (c.entrada(), c.entrada.as_ref()) {
17385                (Some(a), Some(b)) => assert!(
17386                    std::ptr::eq(a, b),
17387                    "Caixa::entrada accessor and self.entrada.as_ref() \
17388                     field access must borrow the same backing storage \
17389                     — the accessor is the substrate-primitive typed \
17390                     dispatch every downstream Aplicacao-external- \
17391                     gateway composite consumer must route through, and \
17392                     a reference-identity split would silently break \
17393                     every consumer that relied on the borrow sharing \
17394                     the composite's storage",
17395                ),
17396                (None, None) => {}
17397                _ => panic!(
17398                    "Caixa::entrada presence bit must byte-equal \
17399                     self.entrada.is_some() — a presence-bit drift \
17400                     would silently split the paired \
17401                     Caixa::aplicacao_view Aplicacao-composition seed's \
17402                     traversal head from the peer \
17403                     Caixa::declared_mesh_slots M3 declared-slot \
17404                     enumerator's presence probe",
17405                ),
17406            }
17407            assert_eq!(
17408                c.entrada().is_some(),
17409                c.entrada.is_some(),
17410                "Caixa::entrada().is_some() must byte-equal \
17411                 self.entrada.is_some() — a presence-bit drift would \
17412                 silently split every downstream Option<&Entrada> \
17413                 consumer's partition on the cluster-internal arm",
17414            );
17415        }
17416    }
17417
17418    #[test]
17419    fn declared_mesh_slots_entrada_arm_routes_through_accessor() {
17420        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:entrada`
17421        // presence-probe arm must key off [`Caixa::entrada`], not the
17422        // raw `self.entrada.is_some()` field-probe. Structurally: a
17423        // `Caixa { entrada: Some(Entrada { host: "...", para: "...",
17424        // paths: [], port: DEFAULT_SERVICO_PORT }), .. }` must push
17425        // `M3_AUTHOR_KEY_ENTRADA` onto the declared-slot list (the
17426        // presence bit is `Some`, so the M3 kind-coherence gate must
17427        // surface the slot as "declared" even when every per-axis
17428        // scalar defers to the substrate catch-all / default port),
17429        // and a `Caixa { entrada: None, .. }` must NOT push the label
17430        // (the "author omitted the slot entirely" partition). The pair
17431        // jointly pins the accessor + declared-slot enumerator
17432        // composition: any future silent detour that had the accessor
17433        // collapse `Some(Entrada { paths: [], .. })` to `None` (a
17434        // `.filter(|e| !e.paths.is_empty())` projection) would silently
17435        // absorb the "declared but empty-paths" arm at the accessor
17436        // boundary and the
17437        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
17438        // coherence gate would silently accept a struct-literal
17439        // `Caixa` carrying the drift.
17440        //
17441        // Peer of the sibling
17442        // `declared_servico_slots_limits_arm_routes_through_accessor`
17443        // (b2bd9d7),
17444        // `declared_servico_slots_behavior_arm_routes_through_accessor`
17445        // (35d8b52),
17446        // `declared_mesh_slots_politicas_arm_routes_through_accessor`
17447        // (5d23d29), and
17448        // `declared_mesh_slots_placement_arm_routes_through_accessor`
17449        // (4fb8074) composition pins on the sibling `:limits` /
17450        // `:behavior` / `:politicas` / `:placement` outer-
17451        // `Option<&Composite>` arms — same "the enumerator gate must
17452        // route through the substrate-primitive typed dispatch"
17453        // discipline extended onto the third and final M3 mesh-slot
17454        // axis so the [`Caixa::declared_mesh_slots`] enumerator now
17455        // carries the routing invariant on every M3 mesh-slot arm.
17456        use crate::aplicacao::Entrada;
17457        let c = caixa_aplicacao_with_entrada(Some(Entrada {
17458            host: "checkout.quero.cloud".into(),
17459            para: "gateway".into(),
17460            paths: Vec::new(),
17461            port: crate::DEFAULT_SERVICO_PORT,
17462        }));
17463        let slots = c.declared_mesh_slots();
17464        assert!(
17465            slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17466            "declared_mesh_slots must push M3_AUTHOR_KEY_ENTRADA when \
17467             `:entrada` is Some (even for empty-paths / default-port) \
17468             — the accessor and the enumerator gate must route through \
17469             the same substrate-primitive typed dispatch on the outer \
17470             :entrada presence bit (got slots={slots:?})",
17471        );
17472        let c = caixa_aplicacao_with_entrada(None);
17473        let slots = c.declared_mesh_slots();
17474        assert!(
17475            !slots.contains(&crate::render::M3_AUTHOR_KEY_ENTRADA),
17476            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_ENTRADA \
17477             when `:entrada` is None — the author-omitted arm must \
17478             route through the accessor's None-return unchanged (got \
17479             slots={slots:?})",
17480        );
17481    }
17482
17483    #[test]
17484    fn aplicacao_view_entrada_arm_folds_through_accessor() {
17485        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:entrada`
17486        // Aplicacao-composition seed must fold through
17487        // [`Caixa::entrada`], not the raw `self.entrada.clone()` field-
17488        // borrow. Structurally: a `Caixa { entrada: Some(Entrada {
17489        // host: "api.pleme.io", para: "public-api", paths: ["/v1"],
17490        // port: 8080 }), kind: Aplicacao, .. }` must surface a projected
17491        // [`crate::AplicacaoSpec`] whose `entrada().unwrap()` byte-
17492        // equals the outer composite's authored value (the fold must
17493        // project the authored composite verbatim), and a `Caixa {
17494        // entrada: None, kind: Aplicacao, .. }` must surface an
17495        // [`crate::AplicacaoSpec`] whose `entrada()` is `None` (the
17496        // "author omitted the slot entirely" arm folds through the
17497        // accessor's `Option::cloned` onto the same `None` presence
17498        // bit — unlike the peer `:politicas` / `:placement` arms
17499        // `:entrada` has no cluster-default fold, the omitted arm
17500        // stays omitted). The pair jointly pins the accessor +
17501        // Aplicacao-composition seed composition: any future silent
17502        // detour that had the accessor divert the raw slot away from
17503        // the seed's fold (an operator-resolved overlay's forward arm
17504        // silently differing from the raw slot's forward arm) would
17505        // silently split the build-time gateway-artifact emission gate
17506        // from the caixa-mesh renderer's Aplicacao-view input at the
17507        // composition boundary.
17508        use crate::aplicacao::Entrada;
17509        let authored = Entrada {
17510            host: "api.pleme.io".into(),
17511            para: "public-api".into(),
17512            paths: vec!["/v1".into()],
17513            port: 8080,
17514        };
17515        let c = caixa_aplicacao_with_entrada(Some(authored.clone()));
17516        let view = c.aplicacao_view().unwrap();
17517        assert_eq!(
17518            view.entrada(),
17519            Some(&authored),
17520            "Caixa::aplicacao_view must fold the authored :entrada \
17521             composite through the accessor verbatim onto the \
17522             projected AplicacaoSpec — a future silent detour at the \
17523             seed's fold arm would surface here as a projected- \
17524             composite drift (got {:?})",
17525            view.entrada(),
17526        );
17527        let c = caixa_aplicacao_with_entrada(None);
17528        let view = c.aplicacao_view().unwrap();
17529        assert!(
17530            view.entrada().is_none(),
17531            "Caixa::aplicacao_view must fold None through the \
17532             accessor's Option::cloned onto None — the author- \
17533             omitted arm must route through the accessor's None-return \
17534             unchanged (got {:?})",
17535            view.entrada(),
17536        );
17537    }
17538
17539    #[test]
17540    fn entrada_projects_option_ref_by_borrow() {
17541        // The by-borrow pin: [`Caixa::entrada`] returns
17542        // `Option<&Entrada>` by borrow — the returned reference
17543        // borrows the underlying `Option<Entrada>` storage of the
17544        // `:entrada` slot and the accessor must not clone the backing
17545        // composite on every call. Peer of the sibling
17546        // `limits_projects_option_ref_by_borrow` (b2bd9d7),
17547        // `behavior_projects_option_ref_by_borrow` (35d8b52),
17548        // `politicas_projects_option_ref_by_borrow` (5d23d29), and
17549        // `placement_projects_option_ref_by_borrow` (4fb8074) by-
17550        // borrow pins on the outer top-level [`Caixa`]
17551        // `Option<&Composite>`-return sub-family — extended here to
17552        // the fifth and final axis of the same sub-family, closing
17553        // the discipline: the accessor's returned reference must
17554        // borrow from `&self` (the returned reference's lifetime is
17555        // tied to `&self`), and calling the accessor twice on the
17556        // same [`Caixa`] must yield references that are pointer-equal
17557        // (the underlying byte-buffer is the storage `Entrada`'s
17558        // allocation, not a fresh copy) as well as value-equal
17559        // (idempotent, no side effects on `&self`).
17560        //
17561        // Pins against a future silent detour that returned an owned
17562        // `Entrada` (which would type-check via the `Clone` impl but
17563        // silently clone on every call), a `&Entrada` panic-return on
17564        // the `None` arm (which would collapse the load-bearing
17565        // `Option` presence-bit into a runtime panic), or a one-arm-
17566        // only accessor that returned a saturating composite on some
17567        // sentinel input.
17568        use crate::aplicacao::Entrada;
17569        for entrada in [
17570            Some(Entrada {
17571                host: "checkout.quero.cloud".into(),
17572                para: "gateway".into(),
17573                paths: Vec::new(),
17574                port: crate::DEFAULT_SERVICO_PORT,
17575            }),
17576            Some(Entrada {
17577                host: "api.pleme.io".into(),
17578                para: "public-api".into(),
17579                paths: vec!["/v1".into(), "/v2".into()],
17580                port: 8080,
17581            }),
17582        ] {
17583            let c = caixa_aplicacao_with_entrada(entrada.clone());
17584            let first = c.entrada().unwrap();
17585            let second = c.entrada().unwrap();
17586            assert_eq!(
17587                first, second,
17588                "Caixa::entrada must be idempotent — two successive \
17589                 calls on the same &self must return the same &Entrada",
17590            );
17591            assert!(
17592                std::ptr::eq(first, second),
17593                "Caixa::entrada must borrow the underlying \
17594                 Option<Entrada> storage — two successive calls must \
17595                 return references with the same backing pointer (a \
17596                 fresh Entrada clone would change the pointer on every \
17597                 call)",
17598            );
17599            assert_eq!(
17600                Some(first),
17601                entrada.as_ref(),
17602                "Caixa::entrada must return :entrada verbatim by \
17603                 borrow — got {first:?}, expected {:?}",
17604                entrada.as_ref(),
17605            );
17606        }
17607        let c = caixa_aplicacao_with_entrada(None);
17608        assert!(
17609            c.entrada().is_none(),
17610            "Caixa::entrada must return None when :entrada is absent \
17611             — the author-omitted arm must project through the \
17612             accessor's Option::None unchanged",
17613        );
17614    }
17615
17616    // ── Caixa::estrategia — outer top-level Option<RestartStrategy> flat-spread supervisor-tree accessor ──
17617
17618    fn caixa_with_estrategia(estrategia: Option<crate::supervisor::RestartStrategy>) -> Caixa {
17619        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17620        c.estrategia = estrategia;
17621        c
17622    }
17623
17624    #[test]
17625    fn estrategia_returns_estrategia_option_verbatim_across_permutations() {
17626        // The canonical per-`Caixa` `:estrategia` M2 supervisor-tree-slot
17627        // flat-spread `Option<RestartStrategy>`-return `Copy`-composite-
17628        // enum-arm scalar shape pin: [`Caixa::estrategia`] must return
17629        // the `:estrategia` typed `Option<crate::supervisor::RestartStrategy>`
17630        // verbatim as an `Option<RestartStrategy>` `Copy`-projected value
17631        // over the same discriminant the raw `self.estrategia` field
17632        // access carries, byte-equal across every representative fixture
17633        // in the accept-set — the author-omitted `None` shape (the
17634        // "defer to [`RestartStrategy::default`] through the
17635        // [`Self::supervisor_view`] `unwrap_or_default()` fold" partition
17636        // every non-`Supervisor`-kind `defcaixa` carries by
17637        // `#[serde(default)]`), and each of the four closed-set variants
17638        // [`RestartStrategy::OneForOne`] / [`RestartStrategy::OneForAll`]
17639        // / [`RestartStrategy::RestForOne`] /
17640        // [`RestartStrategy::SimpleOneForOne`] the author-declared arm
17641        // partitions on.
17642        //
17643        // Pins against a future silent detour that re-derived the
17644        // strategy from a peer axis (an accidental fallback to
17645        // `if children.is_empty() { SimpleOneForOne } else { OneForOne }`
17646        // collapse that read the outer `:children` list-length axis into
17647        // the strategy discriminator at the accessor boundary), a
17648        // stale-derive detour that substituted [`RestartStrategy::default`]
17649        // when the outer `Option` held `None` (which would silently
17650        // collapse the load-bearing "author explicitly declared
17651        // `:estrategia OneForOne`" vs "author omitted the slot and
17652        // inherited the default" partition the [`Self::declared_supervisor_slots`]
17653        // presence-probe reads — the enumerator gate would still push
17654        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` on the omitted arm, silently
17655        // splitting the paired [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
17656        // kind-coherence gate's traversal head from the
17657        // [`Self::supervisor_view`] `unwrap_or_default()` fold's
17658        // composition head), a reference to an operator-resolved overlay
17659        // (the future per-cluster `:estrategia-overrides` slot — its
17660        // resolution must land at exactly this accessor body, not
17661        // silently divert the raw slot away from a second consumer), or
17662        // an axis-remap projection (a future detour that mapped
17663        // `OneForAll` through the accessor onto `OneForOne` would
17664        // silently split every downstream sibling-restart-strategy
17665        // consumer's per-arm fan-out).
17666        //
17667        // First outer top-level [`Caixa`] `Option<Copy>`-return
17668        // supervisor-tree-slot flat-spread accessor pin on the substrate
17669        // primitive — opens the outer-`Caixa` `Option<Copy>` flat-spread
17670        // projection pattern the sibling per-`Caixa` `:max-restarts` /
17671        // `:restart-window` future outer-scalar pins fold on. Peer of
17672        // the inner-altitude
17673        // `supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17674        // (eafb619) pin on the post-composition [`SupervisorSpec`]
17675        // altitude — same "the substrate-primitive accessor must byte-
17676        // equal the raw field access verbatim across every author-
17677        // declared value" discipline extended onto the pre-composition
17678        // outer author-surface [`Caixa`] altitude. Peer of the closed
17679        // outer-`Caixa` `Option<&Composite>` composite-reference family
17680        // the sibling `limits` / `behavior` / `politicas` / `placement` /
17681        // `entrada`
17682        // `..._returns_..._option_ref_verbatim_across_permutations` pins
17683        // already carry on the outer `Option<&Composite>` altitude.
17684        use crate::supervisor::RestartStrategy;
17685        let fixtures: Vec<Option<RestartStrategy>> = vec![
17686            None,
17687            Some(RestartStrategy::OneForOne),
17688            Some(RestartStrategy::OneForAll),
17689            Some(RestartStrategy::RestForOne),
17690            Some(RestartStrategy::SimpleOneForOne),
17691        ];
17692        for estrategia in fixtures {
17693            let c = caixa_with_estrategia(estrategia);
17694            assert_eq!(
17695                c.estrategia(),
17696                estrategia,
17697                "Caixa::estrategia must return :estrategia verbatim (got \
17698                 {:?}, expected {:?})",
17699                c.estrategia(),
17700                estrategia,
17701            );
17702            assert_eq!(
17703                c.estrategia(),
17704                c.estrategia,
17705                "Caixa::estrategia accessor and self.estrategia field \
17706                 access must byte-equal — the accessor is the substrate-\
17707                 primitive typed dispatch every downstream supervisor-\
17708                 tree flat-spread consumer must route through, and a \
17709                 discriminant split would silently break every consumer \
17710                 that relied on the accessor sharing the field's own \
17711                 Option<Copy> shape",
17712            );
17713            assert_eq!(
17714                c.estrategia().is_some(),
17715                c.estrategia.is_some(),
17716                "Caixa::estrategia().is_some() must byte-equal \
17717                 self.estrategia.is_some() — a presence-bit drift would \
17718                 silently split the paired Caixa::declared_supervisor_slots \
17719                 presence-probe arm from the Caixa::supervisor_view \
17720                 unwrap_or_default() fold's composition input",
17721            );
17722        }
17723    }
17724
17725    #[test]
17726    fn declared_supervisor_slots_estrategia_arm_routes_through_accessor() {
17727        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
17728        // `:estrategia` presence-probe arm must key off
17729        // [`Caixa::estrategia`], not the raw `self.estrategia.is_some()`
17730        // field-probe. Structurally: every `Caixa { estrategia:
17731        // Some(RestartStrategy::_), .. }` variant must push
17732        // `SUPERVISOR_AUTHOR_KEY_ESTRATEGIA` onto the declared-slot list
17733        // (the presence bit is `Some` for every closed-set variant, so
17734        // the M2 supervisor-tree kind-coherence gate must surface the
17735        // slot as "declared" regardless of which variant the author
17736        // picked), and a `Caixa { estrategia: None, .. }` must NOT push
17737        // the label (the "author omitted the slot entirely, deferring
17738        // to [`RestartStrategy::default`] through the supervisor_view
17739        // fold" partition). The pair jointly pins the accessor +
17740        // declared-slot enumerator composition: any future silent detour
17741        // that had the accessor collapse `Some(RestartStrategy::default())`
17742        // to `None` (a `.filter(|e| *e != RestartStrategy::default())`
17743        // projection) would silently absorb the "declared but default-
17744        // valued" arm at the accessor boundary and the
17745        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`] kind-
17746        // coherence gate would silently accept a struct-literal `Caixa`
17747        // carrying the drift.
17748        //
17749        // Peer of the sibling per-`Caixa`
17750        // `declared_servico_slots_limits_arm_routes_through_accessor`
17751        // (b2bd9d7) accessor-composition pin on the sibling outer-`Caixa`
17752        // `Option<&LimitsSpec>` composition axis — same "the enumerator
17753        // gate must route through the substrate-primitive typed
17754        // dispatch" discipline extended onto the flat-spread M2
17755        // supervisor-tree `Option<RestartStrategy>`-composition surface,
17756        // opening the outer-`Caixa` supervisor-tree-slot arm of the
17757        // composition-pin family.
17758        use crate::supervisor::RestartStrategy;
17759        for estrategia in [
17760            RestartStrategy::OneForOne,
17761            RestartStrategy::OneForAll,
17762            RestartStrategy::RestForOne,
17763            RestartStrategy::SimpleOneForOne,
17764        ] {
17765            let c = caixa_with_estrategia(Some(estrategia));
17766            let slots = c.declared_supervisor_slots();
17767            assert!(
17768                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17769                "declared_supervisor_slots must push \
17770                 SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is \
17771                 Some({estrategia:?}) — the accessor and the enumerator \
17772                 gate must route through the same substrate-primitive \
17773                 typed dispatch on the outer :estrategia presence bit \
17774                 (got slots={slots:?})",
17775            );
17776        }
17777        let c = caixa_with_estrategia(None);
17778        let slots = c.declared_supervisor_slots();
17779        assert!(
17780            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_ESTRATEGIA),
17781            "declared_supervisor_slots must NOT push \
17782             SUPERVISOR_AUTHOR_KEY_ESTRATEGIA when `:estrategia` is None \
17783             — the author-omitted arm must route through the accessor's \
17784             None-return unchanged (got slots={slots:?})",
17785        );
17786    }
17787
17788    #[test]
17789    fn supervisor_view_estrategia_arm_routes_through_accessor() {
17790        // Composition pin: [`Caixa::supervisor_view`]'s per-`:estrategia`
17791        // [`SupervisorSpec`] construction arm must key off
17792        // [`Caixa::estrategia`]'s `unwrap_or_default()` fold, not the raw
17793        // `self.estrategia.unwrap_or_default()` field-fold. Structurally:
17794        // for every `:kind Supervisor` `Caixa` carrying an author-
17795        // declared `Some(RestartStrategy::_)` variant, the composed
17796        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal the
17797        // outer accessor's declared variant unchanged; and for a
17798        // `:kind Supervisor` `Caixa` carrying `None`, the composed
17799        // [`SupervisorSpec`]'s `.estrategia` field must byte-equal
17800        // [`RestartStrategy::default`] (the [`RestartStrategy::OneForOne`]
17801        // arm the flat-spread `unwrap_or_default()` fold projects to on
17802        // the author-omitted arm — this is the *composition* between the
17803        // outer `Option<RestartStrategy>` accessor's presence-bit
17804        // surface and the inner post-composition non-`Option`
17805        // [`SupervisorSpec::estrategia`] altitude). The pair jointly
17806        // pins the accessor + supervisor_view composition: any future
17807        // silent detour that had the accessor promote `None` to
17808        // `Some(RestartStrategy::default())` (a `.or_else(|| Some(RestartStrategy::default()))`
17809        // projection) would silently collapse the two arms into one at
17810        // the accessor boundary and the [`Self::declared_supervisor_slots`]
17811        // presence probe would silently drift from the composition site.
17812        //
17813        // Peer of the sibling M2 supervisor-slot post-composition
17814        // `validate_reads_through_lifted_estrategia_accessor` (eafb619)
17815        // pin on the [`SupervisorSpec::validate`] altitude — this pin
17816        // extends that inner-altitude accessor-routing discipline onto
17817        // the pre-composition outer author-surface [`Caixa`] altitude,
17818        // pinning the composition edge between the flat-spread outer
17819        // `Option<RestartStrategy>` and the composed [`SupervisorSpec`]
17820        // `RestartStrategy` axes.
17821        use crate::CaixaKind;
17822        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
17823        for estrategia in [
17824            RestartStrategy::OneForOne,
17825            RestartStrategy::OneForAll,
17826            RestartStrategy::RestForOne,
17827            RestartStrategy::SimpleOneForOne,
17828        ] {
17829            let mut c = caixa_with_estrategia(Some(estrategia));
17830            c.kind = CaixaKind::Supervisor;
17831            // Route the `SimpleOneForOne ↔ non-SimpleOneForOne` fixture-
17832            // shape partition through the [`gen_platform::IsVariant`]
17833            // derive-generated
17834            // [`RestartStrategy::is_simple_one_for_one`] predicate rather
17835            // than the raw `matches!(estrategia, RestartStrategy::
17836            // SimpleOneForOne)` open-coded pattern-match — same closed-
17837            // set-typed-enum arm-discriminator dispatch discipline the
17838            // sibling [`crate::upgrade::UpgradeInstruction::is_restart`]
17839            // convergence (915a934) extended onto its two paired positive
17840            // / negated `matches!` sites and the peer
17841            // [`crate::aplicacao::PlacementStrategy`] `IsVariant`-derived
17842            // predicate convergence (766ec63) extended onto the M3 mesh-
17843            // slot per-`:placement` distribution-strategy discriminator
17844            // axis. See the sibling `supervisor::tests::
17845            // round_trip_all_strategies` and
17846            // `supervisor::tests::supervisor_spec_estrategia_returns_estrategia_verbatim_across_permutations`
17847            // fixtures — the three sites (all test-only,
17848            // acknowledged in 915a934's Prior-commits footnote as the
17849            // outstanding follow-up) now consult one typed dispatch on
17850            // the substrate primitive.
17851            c.children = if estrategia.is_simple_one_for_one() {
17852                Vec::new()
17853            } else {
17854                vec![ChildSpec {
17855                    caixa: "worker".into(),
17856                    versao: "^0.1".into(),
17857                    restart: RestartPolicy::Permanent,
17858                }]
17859            };
17860            let view = c.supervisor_view().expect(
17861                "supervisor_view must materialize a SupervisorSpec for a \
17862                 :kind Supervisor Caixa carrying a Some(:estrategia) slot",
17863            );
17864            assert_eq!(
17865                view.estrategia(),
17866                c.estrategia().unwrap(),
17867                "supervisor_view must carry the outer Caixa::estrategia() \
17868                 declared variant onto the composed SupervisorSpec.estrategia \
17869                 field verbatim on the Some arm (got {:?}, expected {:?})",
17870                view.estrategia(),
17871                c.estrategia().unwrap(),
17872            );
17873        }
17874        // The author-omitted arm: outer `None` → composed
17875        // `RestartStrategy::default()` through the flat-spread
17876        // `unwrap_or_default()` fold.
17877        let mut c = caixa_with_estrategia(None);
17878        c.kind = CaixaKind::Supervisor;
17879        // Populate children so the sibling supervisor slots are coherent
17880        // for the [`Self::supervisor_view`] projection; the `:estrategia`
17881        // arm still defers to [`RestartStrategy::default`] on the
17882        // author-omitted arm even when the sibling slots carry values.
17883        c.children = vec![ChildSpec {
17884            caixa: "worker".into(),
17885            versao: "^0.1".into(),
17886            restart: RestartPolicy::Permanent,
17887        }];
17888        let view = c.supervisor_view().expect(
17889            "supervisor_view must materialize a SupervisorSpec for a \
17890             :kind Supervisor Caixa carrying a None `:estrategia` slot",
17891        );
17892        assert_eq!(
17893            view.estrategia(),
17894            RestartStrategy::default(),
17895            "supervisor_view must project the outer Caixa::estrategia() \
17896             None arm onto RestartStrategy::default() through the flat-\
17897             spread unwrap_or_default() fold (got {:?}, expected {:?})",
17898            view.estrategia(),
17899            RestartStrategy::default(),
17900        );
17901        assert!(
17902            c.estrategia().is_none(),
17903            "Caixa::estrategia() must remain None on the author-omitted \
17904             arm — the supervisor_view fold must not mutate the outer \
17905             flat-spread presence bit",
17906        );
17907    }
17908
17909    #[test]
17910    fn estrategia_projects_option_by_copy() {
17911        // The by-`Copy` pin: [`Caixa::estrategia`] returns
17912        // `Option<RestartStrategy>` by value (`RestartStrategy: Copy`) —
17913        // the accessor does not borrow `&self` past the call (no
17914        // lifetime on the return type), and calling the accessor twice
17915        // on the same [`Caixa`] must yield discriminant-equal values
17916        // (idempotent, no side effects on `&self`). Peer of the sibling
17917        // outer-`Caixa` `Option<&Composite>` by-borrow
17918        // `limits_projects_option_ref_by_borrow` (b2bd9d7) /
17919        // `behavior_projects_option_ref_by_borrow` (35d8b52) /
17920        // `politicas_projects_option_ref_by_borrow` (5d23d29) /
17921        // `placement_projects_option_ref_by_borrow` (4fb8074) /
17922        // `entrada_projects_option_ref_by_borrow` (e4128e4) by-borrow
17923        // pins on the outer-`Caixa` `Option<&Composite>`-return axes —
17924        // extended here to the outer-`Caixa` `Option<Copy>`-return
17925        // flat-spread axis. The `Copy` discipline replaces the pointer-
17926        // equality claim the by-borrow siblings pin (a fresh `Copy` of a
17927        // `Copy` discriminant is definitionally the same discriminant, so
17928        // the axis reduces to discriminant equality).
17929        //
17930        // Pins against a future silent detour that returned a fresh
17931        // `Option<&RestartStrategy>` (which would type-check but silently
17932        // introduce a borrow of `&self` past the call, collapsing the
17933        // load-bearing "no lifetime on the return type" `Copy` projection
17934        // the flat-spread axis's `Option<Copy>` shape carries), a stale-
17935        // read side effect that flipped the outer discriminant on
17936        // successive calls, or an axis-remap projection that returned a
17937        // different variant than the field storage.
17938        use crate::supervisor::RestartStrategy;
17939        for estrategia in [
17940            Some(RestartStrategy::OneForOne),
17941            Some(RestartStrategy::OneForAll),
17942            Some(RestartStrategy::RestForOne),
17943            Some(RestartStrategy::SimpleOneForOne),
17944        ] {
17945            let c = caixa_with_estrategia(estrategia);
17946            let first = c.estrategia();
17947            let second = c.estrategia();
17948            assert_eq!(
17949                first, second,
17950                "Caixa::estrategia must be idempotent — two successive \
17951                 calls on the same &self must return the same \
17952                 Option<RestartStrategy>",
17953            );
17954            assert_eq!(
17955                first, estrategia,
17956                "Caixa::estrategia must return :estrategia verbatim by \
17957                 Copy — got {first:?}, expected {estrategia:?}",
17958            );
17959        }
17960        let c = caixa_with_estrategia(None);
17961        assert!(
17962            c.estrategia().is_none(),
17963            "Caixa::estrategia must return None when :estrategia is \
17964             absent — the author-omitted arm must project through the \
17965             accessor's Option::None unchanged",
17966        );
17967    }
17968
17969    // ── Caixa::max_restarts / Caixa::restart_window —
17970    //    outer top-level M2 supervisor-tree-slot flat-spread accessors
17971    //    (Option<u32> / Option<&str>) folding on the ed04d3c
17972    //    Caixa::estrategia Option<Copy> sub-family ─────────────────────
17973
17974    fn caixa_with_max_restarts(max_restarts: Option<u32>) -> Caixa {
17975        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
17976        c.max_restarts = max_restarts;
17977        c
17978    }
17979
17980    fn caixa_supervisor_with_max_restarts_and_window(
17981        max_restarts: Option<u32>,
17982        restart_window: Option<&str>,
17983    ) -> Caixa {
17984        use crate::CaixaKind;
17985        use crate::supervisor::{ChildSpec, RestartPolicy};
17986        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
17987        c.kind = CaixaKind::Supervisor;
17988        c.max_restarts = max_restarts;
17989        c.restart_window = restart_window.map(str::to_string);
17990        c.children = vec![ChildSpec {
17991            caixa: "worker".into(),
17992            versao: "^0.1".into(),
17993            restart: RestartPolicy::Permanent,
17994        }];
17995        c
17996    }
17997
17998    #[test]
17999    fn max_restarts_returns_max_restarts_option_verbatim_across_permutations() {
18000        // Value-shape pin: [`Caixa::max_restarts`] returns the
18001        // `:max-restarts` typed `Option<u32>` verbatim, `Copy`-projected
18002        // from the typed slot's own storage, byte-equal across the
18003        // author-omitted `None` arm (the "defer to the
18004        // [`Self::supervisor_view`] `unwrap_or(5)` OTP-canonical
18005        // `{intensity, 5, 60}` default" partition every
18006        // non-`Supervisor`-kind caixa carries by `#[serde(default)]`)
18007        // and each of the representative fixtures in the accept-set —
18008        // `0` (the zero-floor arm the peer
18009        // [`crate::supervisor::SupervisorSpec::validate`]
18010        // [`crate::SupervisorError::ZeroMaxRestarts`] gate refuses on
18011        // the post-composition altitude — the accessor must ship the
18012        // raw slot verbatim so struct-literal fixtures continue to
18013        // expose the zero at the accessor boundary), the OTP-canonical
18014        // `5` default (`{intensity, 5, 60}` worker-supervisor from
18015        // Learn You Some Erlang), `1000` (the
18016        // [`SUPERVISOR_MAX_RESTARTS_MAX`] cap the peer post-composition
18017        // upper-bound gate accepts on the boundary), `u32::MAX` (a
18018        // past-the-cap sentinel that the substrate-primitive accessor
18019        // must still ship verbatim). Second outer top-level
18020        // [`Caixa`] `Option<Copy>`-return supervisor-tree flat-spread
18021        // pin — folds on the sibling
18022        // `estrategia_returns_estrategia_option_verbatim_across_permutations`
18023        // (ed04d3c) pin's `Option<Copy>` shape, extending the sub-family
18024        // onto the sibling `Option<u32>` restart-budget-count arm.
18025        let fixtures: Vec<Option<u32>> = vec![None, Some(0), Some(5), Some(1000), Some(u32::MAX)];
18026        for max_restarts in fixtures {
18027            let c = caixa_with_max_restarts(max_restarts);
18028            assert_eq!(
18029                c.max_restarts(),
18030                max_restarts,
18031                "Caixa::max_restarts must return :max-restarts verbatim \
18032                 (got {:?}, expected {max_restarts:?})",
18033                c.max_restarts(),
18034            );
18035            assert_eq!(
18036                c.max_restarts(),
18037                c.max_restarts,
18038                "Caixa::max_restarts accessor and self.max_restarts \
18039                 field access must byte-equal — a presence-bit or count \
18040                 drift would silently split the paired \
18041                 Caixa::declared_supervisor_slots presence-probe arm \
18042                 from the Caixa::supervisor_view unwrap_or(5) fold's \
18043                 composition input",
18044            );
18045        }
18046    }
18047
18048    #[test]
18049    fn max_restarts_projects_option_by_copy() {
18050        // The by-`Copy` pin: [`Caixa::max_restarts`] returns
18051        // `Option<u32>` by value (`u32: Copy`) — the accessor does not
18052        // borrow `&self` past the call (no lifetime on the return type),
18053        // and calling the accessor twice on the same [`Caixa`] must
18054        // yield equal values (idempotent, no side effects). Peer of the
18055        // sibling `estrategia_projects_option_by_copy` (ed04d3c) pin on
18056        // the outer-`Caixa` `Option<Copy>`-return flat-spread axis.
18057        for max_restarts in [Some(0u32), Some(5), Some(1000), Some(u32::MAX), None] {
18058            let c = caixa_with_max_restarts(max_restarts);
18059            let first = c.max_restarts();
18060            let second = c.max_restarts();
18061            assert_eq!(
18062                first, second,
18063                "Caixa::max_restarts must be idempotent — two successive \
18064                 calls on the same &self must return the same Option<u32>",
18065            );
18066            assert_eq!(
18067                first, max_restarts,
18068                "Caixa::max_restarts must return :max-restarts verbatim \
18069                 by Copy — got {first:?}, expected {max_restarts:?}",
18070            );
18071        }
18072    }
18073
18074    #[test]
18075    fn declared_supervisor_slots_max_restarts_arm_routes_through_accessor() {
18076        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18077        // `:max-restarts` presence-probe arm must key off
18078        // [`Caixa::max_restarts`], not the raw
18079        // `self.max_restarts.is_some()` field-probe. Structurally: every
18080        // `Caixa { max_restarts: Some(_), .. }` variant must push
18081        // `SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS` onto the declared-slot
18082        // list (the presence bit is `Some` for every representative
18083        // count, so the M2 kind-coherence gate must surface the slot as
18084        // "declared"), and a `Caixa { max_restarts: None, .. }` must
18085        // NOT push the label. Peer of the sibling
18086        // `declared_supervisor_slots_estrategia_arm_routes_through_accessor`
18087        // (ed04d3c) composition pin — same routing-through-accessor
18088        // discipline extended onto the sibling flat-spread `Option<u32>`
18089        // arm.
18090        for max_restarts in [0u32, 5, 1000, u32::MAX] {
18091            let c = caixa_with_max_restarts(Some(max_restarts));
18092            let slots = c.declared_supervisor_slots();
18093            assert!(
18094                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
18095                "declared_supervisor_slots must push \
18096                 SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` \
18097                 is Some({max_restarts}) — the accessor and the \
18098                 enumerator gate must route through the same \
18099                 substrate-primitive typed dispatch on the outer \
18100                 :max-restarts presence bit (got slots={slots:?})",
18101            );
18102        }
18103        let c = caixa_with_max_restarts(None);
18104        let slots = c.declared_supervisor_slots();
18105        assert!(
18106            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS),
18107            "declared_supervisor_slots must NOT push \
18108             SUPERVISOR_AUTHOR_KEY_MAX_RESTARTS when `:max-restarts` is \
18109             None — the author-omitted arm must route through the \
18110             accessor's None-return unchanged (got slots={slots:?})",
18111        );
18112    }
18113
18114    #[test]
18115    fn supervisor_view_max_restarts_arm_routes_through_accessor() {
18116        // Composition pin: [`Caixa::supervisor_view`]'s per-`:max-restarts`
18117        // [`SupervisorSpec`] construction arm must key off
18118        // [`Caixa::max_restarts`]'s `unwrap_or(5)` fold, not the raw
18119        // `self.max_restarts.unwrap_or(5)` field-fold. Structurally: for
18120        // every `:kind Supervisor` `Caixa` carrying an author-declared
18121        // `Some(n)`, the composed [`SupervisorSpec`]'s `.max_restarts()`
18122        // must byte-equal `n`; and for a `:kind Supervisor` `Caixa`
18123        // carrying `None`, the composed [`SupervisorSpec`]'s
18124        // `.max_restarts()` must byte-equal the OTP-canonical `5`. Peer
18125        // of the sibling
18126        // `supervisor_view_estrategia_arm_routes_through_accessor`
18127        // (ed04d3c) composition pin.
18128        for max_restarts in [1u32, 5, 1000] {
18129            let c = caixa_supervisor_with_max_restarts_and_window(Some(max_restarts), None);
18130            let view = c.supervisor_view().expect(
18131                "supervisor_view must materialize a SupervisorSpec for a \
18132                 :kind Supervisor Caixa carrying a Some(:max-restarts)",
18133            );
18134            assert_eq!(
18135                view.max_restarts(),
18136                max_restarts,
18137                "supervisor_view must carry the outer \
18138                 Caixa::max_restarts() Some arm onto the composed \
18139                 SupervisorSpec.max_restarts field verbatim (got {}, \
18140                 expected {max_restarts})",
18141                view.max_restarts(),
18142            );
18143        }
18144        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18145        let view = c.supervisor_view().expect(
18146            "supervisor_view must materialize a SupervisorSpec for a \
18147             :kind Supervisor Caixa carrying a None :max-restarts",
18148        );
18149        assert_eq!(
18150            view.max_restarts(),
18151            5,
18152            "supervisor_view must project the outer \
18153             Caixa::max_restarts() None arm onto the OTP-canonical \
18154             {{intensity, 5, 60}} default (5) through the flat-spread \
18155             unwrap_or(5) fold (got {})",
18156            view.max_restarts(),
18157        );
18158        assert!(
18159            c.max_restarts().is_none(),
18160            "Caixa::max_restarts() must remain None on the author-\
18161             omitted arm — the supervisor_view fold must not mutate \
18162             the outer flat-spread presence bit",
18163        );
18164    }
18165
18166    #[test]
18167    fn supervisor_view_estrategia_fallback_routes_through_lifted_default() {
18168        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
18169        // `:estrategia` arm must degrade onto the substrate-canonical
18170        // [`crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT`] typed
18171        // `pub const` — the Erlang/OTP-canonical `one_for_one` strategy
18172        // half of Learn You Some Erlang's `{one_for_one, intensity, 5, 60}`
18173        // worker-supervisor default — rather than the transitively-
18174        // derived [`crate::supervisor::RestartStrategy::default`] route
18175        // the prior `.unwrap_or_default()` fold reached for. Prior to the
18176        // lift the composition site carried `.unwrap_or_default()` with
18177        // no compile-time link back to the shared OTP-canonical strategy
18178        // default that the paired [`crate::supervisor::Default for
18179        // RestartStrategy`] impl and the [`crate::supervisor::Default for
18180        // SupervisorSpec`] impl's struct-literal `estrategia` field both
18181        // (now) route through the same lifted constant — so a future
18182        // rebrand of the OTP-canonical strategy default (an OTP
18183        // `rest_for_one` widening once the substrate discovers startup-
18184        // order-coupled child cohorts as the more common worker-
18185        // supervisor shape, a per-cluster overlay the operator pins
18186        // through the MESH-COMPOSITION §III.2 supervision-canary
18187        // `:estrategia-overrides` roadmap slot) would have had to migrate
18188        // the paired `MaxIntensity` + `Period` halves through the lifted
18189        // constants and the `one_for_one` half through a
18190        // `RestartStrategy::default()` route in lockstep or a
18191        // `:kind Supervisor` caixa carrying an author-omitted
18192        // `:estrategia` slot would silently resolve to a `SupervisorSpec`
18193        // whose `estrategia` disagreed with the paired
18194        // `SupervisorSpec::default()` view. Byte-parity against the
18195        // lifted constant closes the split. Peer of the sibling
18196        // [`supervisor_view_max_restarts_fallback_routes_through_lifted_default`]
18197        // composition pin on the paired `MaxIntensity` half + the
18198        // [`crate::supervisor::restart_strategy_default_routes_through_lifted_default`]
18199        // + [`crate::supervisor::supervisor_spec_default_estrategia_routes_through_lifted_default`]
18200        // pins on the sibling entry points onto the shared substrate
18201        // constant.
18202        use crate::CaixaKind;
18203        use crate::supervisor::{ChildSpec, RestartPolicy};
18204        let mut c = Caixa::from_lisp(&Caixa::template("root")).unwrap();
18205        c.kind = CaixaKind::Supervisor;
18206        c.estrategia = None;
18207        c.children = vec![ChildSpec {
18208            caixa: "worker".into(),
18209            versao: "^0.1".into(),
18210            restart: RestartPolicy::Permanent,
18211        }];
18212        let view = c.supervisor_view().expect(
18213            "supervisor_view must materialize a SupervisorSpec for a \
18214             :kind Supervisor Caixa carrying a None :estrategia",
18215        );
18216        assert_eq!(
18217            view.estrategia(),
18218            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
18219            "supervisor_view must degrade the outer \
18220             Caixa::estrategia() None arm onto the lifted \
18221             SUPERVISOR_ESTRATEGIA_DEFAULT typed pub const (got {:?}, \
18222             expected {:?})",
18223            view.estrategia(),
18224            crate::supervisor::SUPERVISOR_ESTRATEGIA_DEFAULT,
18225        );
18226    }
18227
18228    #[test]
18229    fn supervisor_view_max_restarts_fallback_routes_through_lifted_default() {
18230        // Composition pin: [`Caixa::supervisor_view`]'s author-omitted
18231        // `:max-restarts` arm must degrade onto the substrate-canonical
18232        // [`crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT`] typed
18233        // `pub const` — the Erlang/OTP-canonical `{intensity, 5, 60}`
18234        // `MaxIntensity` default — rather than a raw `5` literal. Prior
18235        // to the lift the composition site carried an inline
18236        // `.unwrap_or(5)` with no compile-time link back to the shared
18237        // OTP-canonical default that the serde-side
18238        // `#[serde(default = "default_max_restarts")]` wire-format arm
18239        // and the [`Default for crate::supervisor::SupervisorSpec`]
18240        // struct-literal default arm both key off — so a future rebrand
18241        // of the OTP-canonical default (Elixir's `Supervisor` `3`
18242        // default, a per-cluster overlay the operator pins through the
18243        // MESH-COMPOSITION §III.2 supervision-canary
18244        // `:supervisor :max-restarts-overrides` roadmap slot) would
18245        // have had to be threaded through both the serde-side helper
18246        // and this view-construction arm in lockstep or a `:kind
18247        // Supervisor` caixa carrying `:max-restarts ()` would silently
18248        // resolve to a `SupervisorSpec` whose `max_restarts` disagreed
18249        // with the same fixture's serde-side `SupervisorSpec` view (an
18250        // author-omitted slot round-tripping through
18251        // `SupervisorSpec::default()` to the lifted constant, then
18252        // splitting to a stale literal past `supervisor_view`).
18253        // Byte-parity against the lifted constant closes the split.
18254        // Peer of the sibling
18255        // [`crate::supervisor::default_max_restarts_helper_routes_through_lifted_default`]
18256        // + [`crate::supervisor::supervisor_spec_default_max_restarts_routes_through_lifted_default`]
18257        // composition pins that close the same routing on the two
18258        // sibling entry points onto the shared substrate constant.
18259        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18260        let view = c.supervisor_view().expect(
18261            "supervisor_view must materialize a SupervisorSpec for a \
18262             :kind Supervisor Caixa carrying a None :max-restarts",
18263        );
18264        assert_eq!(
18265            view.max_restarts(),
18266            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18267            "supervisor_view must degrade the outer \
18268             Caixa::max_restarts() None arm onto the lifted \
18269             SUPERVISOR_MAX_RESTARTS_DEFAULT typed pub const (got {}, \
18270             expected {})",
18271            view.max_restarts(),
18272            crate::supervisor::SUPERVISOR_MAX_RESTARTS_DEFAULT,
18273        );
18274    }
18275
18276    #[test]
18277    fn restart_window_returns_restart_window_option_verbatim_across_permutations() {
18278        // Value-shape pin: [`Caixa::restart_window`] returns the
18279        // `:restart-window` typed `Option<String>` verbatim as an
18280        // `Option<&str>`, borrowed from the typed slot's own storage,
18281        // byte-equal across the author-omitted `None` arm and each of
18282        // the representative fixtures in the accept-set — the canonical
18283        // `"60s"` from `{intensity, 5, 60}`, the sibling
18284        // canonical-magnitude forms (`"5m"` / `"1h"` / `"500ms"` / `"30"`
18285        // / `"0s"`) the shared codec's positive-set sweep pin covers,
18286        // plus a past-the-guard sentinel (`"1.5s"` — the fractional-
18287        // seconds drift the sibling [`Self::validate_restart_window`]
18288        // gate refuses; the accessor must ship the raw slot verbatim
18289        // so struct-literal fixtures continue to expose the drift at
18290        // the accessor boundary). Third outer top-level [`Caixa`]
18291        // supervisor-tree flat-spread pin — extends the sub-family onto
18292        // the sibling `Option<&str>` raw-duration-string arm.
18293        for window in [
18294            None,
18295            Some("60s"),
18296            Some("5m"),
18297            Some("1h"),
18298            Some("500ms"),
18299            Some("1.5s"),
18300            Some(""),
18301        ] {
18302            let c = caixa_with_restart_window(window);
18303            assert_eq!(
18304                c.restart_window(),
18305                window,
18306                "Caixa::restart_window must return :restart-window \
18307                 verbatim as Option<&str> (got {:?}, expected {window:?})",
18308                c.restart_window(),
18309            );
18310            assert_eq!(
18311                c.restart_window(),
18312                c.restart_window.as_deref(),
18313                "Caixa::restart_window accessor and \
18314                 self.restart_window.as_deref() field access must \
18315                 byte-equal — a byte-level drift would silently split \
18316                 the paired Caixa::declared_supervisor_slots \
18317                 presence-probe arm from the \
18318                 Caixa::validate_restart_window shared-codec gate and \
18319                 the Caixa::supervisor_view soft-swallowing fold",
18320            );
18321        }
18322    }
18323
18324    #[test]
18325    fn restart_window_projects_slice_by_borrow() {
18326        // The by-borrow pin: [`Caixa::restart_window`] returns
18327        // `Option<&str>` by borrow — the returned string slice borrows
18328        // the underlying `Option<String>` storage of the `:restart-window`
18329        // slot and the accessor must not clone on every call. Peer of
18330        // the sibling outer top-level [`Caixa`] `Option<&str>`-return
18331        // by-borrow pins on the universal-axis scalar family
18332        // (`licenca_projects_option_ref_by_borrow` /
18333        // `descricao_projects_option_ref_by_borrow` and siblings) —
18334        // extended onto the M2 supervisor-tree flat-spread
18335        // `Option<&str>` raw-duration-string axis.
18336        for window in [None, Some("60s"), Some("5m"), Some("")] {
18337            let c = caixa_with_restart_window(window);
18338            let first = c.restart_window();
18339            let second = c.restart_window();
18340            assert_eq!(
18341                first, second,
18342                "Caixa::restart_window must be idempotent — two \
18343                 successive calls on the same &self must return the \
18344                 same Option<&str>",
18345            );
18346            if let (Some(a), Some(b)) = (first, second) {
18347                assert_eq!(
18348                    a.as_ptr(),
18349                    b.as_ptr(),
18350                    "Caixa::restart_window must borrow the underlying \
18351                     String storage — two successive Some-arm calls must \
18352                     return slices with the same backing pointer (a fresh \
18353                     String clone would change the pointer on every call)",
18354                );
18355            }
18356            assert_eq!(
18357                first, window,
18358                "Caixa::restart_window must return :restart-window \
18359                 verbatim by borrow — got {first:?}, expected {window:?}",
18360            );
18361        }
18362    }
18363
18364    #[test]
18365    fn declared_supervisor_slots_restart_window_arm_routes_through_accessor() {
18366        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18367        // `:restart-window` presence-probe arm must key off
18368        // [`Caixa::restart_window`], not the raw
18369        // `self.restart_window.is_some()` field-probe. Structurally:
18370        // every `Caixa { restart_window: Some(_), .. }` must push
18371        // `SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW` onto the declared-slot
18372        // list, and a `Caixa { restart_window: None, .. }` must NOT
18373        // push the label. Peer of the sibling
18374        // `declared_supervisor_slots_max_restarts_arm_routes_through_accessor`
18375        // routing pin.
18376        for window in ["60s", "5m", "1h", "500ms", "1.5s", ""] {
18377            let c = caixa_with_restart_window(Some(window));
18378            let slots = c.declared_supervisor_slots();
18379            assert!(
18380                slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18381                "declared_supervisor_slots must push \
18382                 SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when \
18383                 `:restart-window` is Some({window:?}) — the accessor \
18384                 and the enumerator gate must route through the same \
18385                 substrate-primitive typed dispatch on the outer \
18386                 :restart-window presence bit (got slots={slots:?})",
18387            );
18388        }
18389        let c = caixa_with_restart_window(None);
18390        let slots = c.declared_supervisor_slots();
18391        assert!(
18392            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW),
18393            "declared_supervisor_slots must NOT push \
18394             SUPERVISOR_AUTHOR_KEY_RESTART_WINDOW when `:restart-window` \
18395             is None — the author-omitted arm must route through the \
18396             accessor's None-return unchanged (got slots={slots:?})",
18397        );
18398    }
18399
18400    #[test]
18401    fn validate_restart_window_arm_routes_through_accessor() {
18402        // Composition pin: [`Caixa::validate_restart_window`]'s
18403        // shared-codec fold arm must key off [`Caixa::restart_window`],
18404        // not the raw `self.restart_window.as_deref()` field-projection.
18405        // Structurally: (1) `None` → `Ok(())` (the "omit the slot to
18406        // express no reset" canonical shape); (2) a canonical `Some`
18407        // arm (`"60s"`) → `Ok(())`; (3) a codec-rejected `Some` arm
18408        // (`"1.5s"`) → `Err(RestartWindowMalformed { restart_window,
18409        // .. })` carrying the offending raw string verbatim. The three
18410        // arms jointly pin that the validator's raw-string binding is
18411        // the accessor's return, not a peer projection — any future
18412        // silent detour that had the accessor collapse `Some("")` to
18413        // `None` would silently absorb the empty-after-trim refusal
18414        // case at the accessor boundary.
18415        caixa_with_restart_window(None)
18416            .validate_restart_window()
18417            .expect("None :restart-window must validate through the accessor");
18418        caixa_with_restart_window(Some("60s"))
18419            .validate_restart_window()
18420            .expect("canonical :restart-window \"60s\" must validate through the accessor");
18421        let err = caixa_with_restart_window(Some("1.5s"))
18422            .validate_restart_window()
18423            .expect_err("fractional-seconds :restart-window must fail through the accessor");
18424        assert!(
18425            matches!(
18426                err,
18427                ManifestError::RestartWindowMalformed { ref restart_window, .. }
18428                    if restart_window == "1.5s"
18429            ),
18430            "validator must carry the offending raw string verbatim \
18431             from the accessor's borrowed &str (got {err:?})",
18432        );
18433    }
18434
18435    #[test]
18436    fn supervisor_view_restart_window_arm_routes_through_accessor() {
18437        // Composition pin: [`Caixa::supervisor_view`]'s
18438        // per-`:restart-window` [`SupervisorSpec`] construction arm
18439        // must key off [`Caixa::restart_window`]'s soft-swallowing
18440        // `.and_then(|s| duration_codec::parse(s).ok())` fold, not the
18441        // raw `self.restart_window.as_deref().and_then(…)` field-fold.
18442        // Structurally: (1) `None` → `SupervisorSpec.restart_window ==
18443        // None` (the "never reset" sentinel); (2) canonical `Some("60s")`
18444        // → `SupervisorSpec.restart_window == Some(Duration::from_secs(60))`
18445        // (the shared codec's canonical parse); (3) codec-rejected
18446        // `Some("1.5s")` → `SupervisorSpec.restart_window == None`
18447        // (the soft-swallow preserving the view's best-effort shape).
18448        let c = caixa_supervisor_with_max_restarts_and_window(None, None);
18449        let view = c.supervisor_view().expect("Supervisor kind has a view");
18450        assert_eq!(
18451            view.restart_window(),
18452            None,
18453            "supervisor_view must project outer None :restart-window \
18454             onto None on the composed SupervisorSpec (never-reset \
18455             sentinel) through the accessor's None-return unchanged",
18456        );
18457
18458        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("60s"));
18459        let view = c.supervisor_view().expect("Supervisor kind has a view");
18460        assert_eq!(
18461            view.restart_window(),
18462            Some(std::time::Duration::from_secs(60)),
18463            "supervisor_view must fold outer Some(\"60s\") through the \
18464             shared duration_codec into Duration::from_secs(60) on the \
18465             composed SupervisorSpec (accessor's Some(&str) → codec \
18466             parse → Some(Duration))",
18467        );
18468
18469        let c = caixa_supervisor_with_max_restarts_and_window(None, Some("1.5s"));
18470        let view = c.supervisor_view().expect("Supervisor kind has a view");
18471        assert_eq!(
18472            view.restart_window(),
18473            None,
18474            "supervisor_view must soft-swallow the shared-codec parse \
18475             failure to None (the view's best-effort shape the sibling \
18476             manifest-level validate_restart_window surfaces as \
18477             RestartWindowMalformed); the accessor's raw-string return \
18478             is the single input every downstream consumer keys off",
18479        );
18480    }
18481
18482    // ── Caixa::upgrade_from — outer top-level &[UpgradeFromEntry] composite-slice accessor ──
18483
18484    fn caixa_with_upgrade_from(upgrade_from: Vec<crate::upgrade::UpgradeFromEntry>) -> Caixa {
18485        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18486        c.upgrade_from = upgrade_from;
18487        c
18488    }
18489
18490    #[test]
18491    fn upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations() {
18492        // The canonical per-`Caixa` `:upgrade-from` M2 typed-slot
18493        // outer-composite `&[UpgradeFromEntry]`-return slice-shape
18494        // pin: [`Caixa::upgrade_from`] must return the `:upgrade-from`
18495        // typed `Vec<UpgradeFromEntry>` verbatim as a
18496        // `&[UpgradeFromEntry]` slice-view over the same backing
18497        // buffer the raw `self.upgrade_from.as_slice()` field access
18498        // borrows from, element-equal across every representative
18499        // fixture in the accept-set — `[]` (the "no hot-upgrade path
18500        // declared" arm every `defcaixa` without an `:upgrade-from`
18501        // block carries; `#[serde(default)]` folds an omitted slot
18502        // onto `Vec::new()`), a canonical single-entry `Restart`
18503        // fixture (the shape most Servicos carry — a single prior
18504        // version with the fallback strategy), a canonical multi-
18505        // entry list carrying every typed instruction variant
18506        // (`LoadModule` / `StateChange` / `SoftPurge` / `Purge` /
18507        // `Restart`), and a past-the-guard sentinel — a duplicate-
18508        // `:from` `[(0.1.0, Restart), (0.1.0, Restart)]` entry pair
18509        // ([`crate::upgrade::validate_upgrade_from`] rejects through
18510        // `DuplicateFrom { from: "0.1.0" }` but the accessor must
18511        // ship the raw slot verbatim so struct-literal fixtures
18512        // continue to expose the duplicate at the accessor boundary).
18513        //
18514        // Pins against a future silent detour that returned an owned
18515        // `Vec<UpgradeFromEntry>` (which would type-check but silently
18516        // clone on every accessor call, breaking the zero-cost
18517        // projection every peer sibling slice accessor carries), a
18518        // `[dup, dup] → [dup]` dedup collapse (which would silently
18519        // absorb the `DuplicateFrom` refusal case at the accessor
18520        // boundary and the [`crate::StandardLayout::verify`] cross-
18521        // entry gate would silently accept a struct-literal `Caixa`
18522        // carrying the drift), a reference to an operator-resolved
18523        // overlay (the future per-cluster `:upgrade-overrides` slot
18524        // — its resolution must land at exactly this accessor body,
18525        // not silently divert the raw slot away from a second
18526        // consumer), or an axis-shuffled projection (a future detour
18527        // that reordered entries through the accessor would silently
18528        // split the paired [`crate::StandardLayout::verify`] per-
18529        // `:upgrade-from` shape gate's traversal input from the peer
18530        // [`crate::render::servico_m2_overlay`] emitter's projection
18531        // input, since the operator's hot-upgrade dispatch matches
18532        // per-`:from` and axis reordering would silently split the
18533        // per-entry script-path existence probe's iteration order
18534        // from the M2 overlay emitter's serialized-entry order).
18535        //
18536        // First outer top-level [`Caixa`] `&[Composite]`-return
18537        // slice accessor pin on the substrate primitive for M2 / M3
18538        // typed-slot vec-carry axes — opens the outer-`Caixa`
18539        // `&[Composite]` composite-slice projection pattern the
18540        // sibling `:children` [`crate::supervisor::ChildSpec`] /
18541        // `:membros` [`crate::aplicacao::Membro`] / `:contratos`
18542        // [`crate::aplicacao::WitContract`] future outer-composite-
18543        // slice pins fold on. Peer of the closed outer-`Caixa`
18544        // scalar `Option<&Composite>` composite-reference family the
18545        // sibling `limits` / `behavior` / `politicas` / `placement`
18546        // / `entrada` `..._returns_..._option_ref_verbatim_across_
18547        // permutations` pins closed (b2bd9d7 → e4128e4) — extends
18548        // the "byte-equal, borrow-shared" outer-accessor discipline
18549        // onto the outer-`Caixa` `&[Composite]` vec-carry altitude.
18550        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18551        let fixtures: Vec<Vec<UpgradeFromEntry>> = vec![
18552            vec![],
18553            vec![UpgradeFromEntry {
18554                from: "0.0.1".into(),
18555                instructions: vec![UpgradeInstruction::Restart],
18556            }],
18557            vec![
18558                UpgradeFromEntry {
18559                    from: "0.0.1".into(),
18560                    instructions: vec![
18561                        UpgradeInstruction::LoadModule {
18562                            module: "demo".into(),
18563                        },
18564                        UpgradeInstruction::SoftPurge {
18565                            module: "demo".into(),
18566                        },
18567                    ],
18568                },
18569                UpgradeFromEntry {
18570                    from: "0.0.2".into(),
18571                    instructions: vec![
18572                        UpgradeInstruction::StateChange {
18573                            script: "servicos/upgrade.lisp".into(),
18574                        },
18575                        UpgradeInstruction::Purge {
18576                            module: "demo".into(),
18577                        },
18578                        UpgradeInstruction::Restart,
18579                    ],
18580                },
18581            ],
18582            vec![
18583                UpgradeFromEntry {
18584                    from: "0.1.0".into(),
18585                    instructions: vec![UpgradeInstruction::Restart],
18586                },
18587                UpgradeFromEntry {
18588                    from: "0.1.0".into(),
18589                    instructions: vec![UpgradeInstruction::Restart],
18590                },
18591            ],
18592        ];
18593        for upgrade_from in fixtures {
18594            let c = caixa_with_upgrade_from(upgrade_from.clone());
18595            assert_eq!(
18596                c.upgrade_from(),
18597                upgrade_from.as_slice(),
18598                "Caixa::upgrade_from must return :upgrade-from \
18599                 verbatim (got {:?}, expected {upgrade_from:?})",
18600                c.upgrade_from(),
18601            );
18602            assert_eq!(
18603                c.upgrade_from(),
18604                c.upgrade_from.as_slice(),
18605                "Caixa::upgrade_from must element-equal the raw \
18606                 `self.upgrade_from.as_slice()` field access across \
18607                 every value in the Vec<UpgradeFromEntry> accept-set",
18608            );
18609            assert_eq!(
18610                c.upgrade_from().is_empty(),
18611                c.upgrade_from.is_empty(),
18612                "Caixa::upgrade_from().is_empty() must byte-equal \
18613                 self.upgrade_from.is_empty() — a presence-bit drift \
18614                 would silently split the paired \
18615                 Caixa::declared_servico_slots M2 declared-slot \
18616                 enumerator's presence probe from the peer \
18617                 crate::render::servico_m2_overlay M2 overlay \
18618                 emitter's presence gate",
18619            );
18620        }
18621    }
18622
18623    #[test]
18624    fn declared_servico_slots_upgrade_from_arm_routes_through_accessor() {
18625        // Composition pin: [`Caixa::declared_servico_slots`]'s
18626        // `:upgrade-from` presence-probe arm must key off
18627        // [`Caixa::upgrade_from`], not the raw
18628        // `self.upgrade_from.is_empty()` field-probe. Structurally: a
18629        // `Caixa { upgrade_from: vec![UpgradeFromEntry { from: "0.0.1",
18630        // instructions: vec![Restart] }], .. }` must push
18631        // `M2_AUTHOR_KEY_UPGRADE_FROM` onto the declared-slot list
18632        // (the presence bit is non-empty, so the M2 kind-coherence
18633        // gate must surface the slot as "declared"), and a `Caixa {
18634        // upgrade_from: vec![], .. }` must NOT push the label (the
18635        // "author omitted the slot entirely" arm — the empty-slice
18636        // partition the serde-default folds onto). The pair jointly
18637        // pins the accessor + declared-slot enumerator composition:
18638        // any future silent detour that had the accessor collapse
18639        // `[Restart]` to `[]` (a `.filter(|e| !e.instructions.
18640        // is_empty())` projection) would silently absorb the
18641        // "declared but degenerate" arm at the accessor boundary and
18642        // the [`crate::LayoutError::ServicoSlotsOnNonServico`] kind-
18643        // coherence gate would silently accept a struct-literal
18644        // `Caixa` carrying the drift.
18645        //
18646        // Peer of the sibling
18647        // `declared_servico_slots_limits_arm_routes_through_accessor`
18648        // (b2bd9d7) and
18649        // `declared_servico_slots_behavior_arm_routes_through_accessor`
18650        // (35d8b52) composition pins on the sibling `:limits` /
18651        // `:behavior` outer-`Option<&Composite>` arms — same "the
18652        // enumerator gate must route through the substrate-primitive
18653        // typed dispatch" discipline extended onto the third M2
18654        // Servico-runtime slot axis, closing the enumerator's routing
18655        // invariant on every M2 arm.
18656        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18657        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18658            from: "0.0.1".into(),
18659            instructions: vec![UpgradeInstruction::Restart],
18660        }]);
18661        let slots = c.declared_servico_slots();
18662        assert!(
18663            slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18664            "declared_servico_slots must push \
18665             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18666             non-empty — the accessor and the enumerator gate must \
18667             route through the same substrate-primitive typed \
18668             dispatch on the outer :upgrade-from presence bit (got \
18669             slots={slots:?})",
18670        );
18671        let c = caixa_with_upgrade_from(vec![]);
18672        let slots = c.declared_servico_slots();
18673        assert!(
18674            !slots.contains(&crate::render::M2_AUTHOR_KEY_UPGRADE_FROM),
18675            "declared_servico_slots must NOT push \
18676             M2_AUTHOR_KEY_UPGRADE_FROM when `:upgrade-from` is \
18677             empty — the author-omitted arm must route through the \
18678             accessor's empty-slice return unchanged (got \
18679             slots={slots:?})",
18680        );
18681    }
18682
18683    #[test]
18684    fn servico_m2_overlay_upgrade_from_arm_routes_through_accessor() {
18685        // Composition pin: [`crate::render::servico_m2_overlay`]'s
18686        // per-`:upgrade-from` M2 overlay emit arm must key off
18687        // [`Caixa::upgrade_from`], not the raw
18688        // `!caixa.upgrade_from.is_empty()` presence gate + the
18689        // `serde_yaml::to_value(&caixa.upgrade_from)` projection.
18690        // Structurally: a `Caixa { upgrade_from: vec![UpgradeFromEntry
18691        // { from: "0.0.1", instructions: vec![Restart] }], .. }` must
18692        // surface the `M2_KEY_UPGRADE_FROM` key with a per-entry
18693        // sequence in the overlay (the emitter fans onto the serde
18694        // slice-serialization), and a `Caixa { upgrade_from: vec![],
18695        // .. }` must omit the key entirely (the empty-slice
18696        // partition — the `!.is_empty()` outer gate elides the key
18697        // when the author omitted the slot). The pair jointly pins
18698        // the accessor + M2 overlay emitter composition: any future
18699        // silent detour that had the accessor return a fresh-cloned
18700        // `Vec<UpgradeFromEntry>` copy would silently break the
18701        // reference-identity pin the peer per-entry
18702        // `serde_yaml::to_value(caixa.upgrade_from())` projection
18703        // reads from — the projection would clone once per accessor
18704        // call instead of borrowing the storage buffer verbatim.
18705        //
18706        // Peer of the sibling
18707        // `servico_m2_overlay_limits_arm_routes_through_accessor`
18708        // (b2bd9d7) and
18709        // `servico_m2_overlay_behavior_arm_routes_through_accessor`
18710        // (35d8b52) composition pins on the sibling `:limits` /
18711        // `:behavior` outer-`Option<&Composite>` arms — same "the
18712        // M2 overlay emitter must route through the substrate-
18713        // primitive typed dispatch" discipline extended onto the
18714        // third M2 Servico-runtime slot axis, closing the overlay
18715        // emitter's routing invariant on every M2 arm.
18716        use crate::render::{M2_KEY_UPGRADE_FROM, servico_m2_overlay};
18717        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18718        let c = caixa_with_upgrade_from(vec![UpgradeFromEntry {
18719            from: "0.0.1".into(),
18720            instructions: vec![UpgradeInstruction::Restart],
18721        }]);
18722        let overlay = servico_m2_overlay(&c).unwrap();
18723        assert!(
18724            overlay.contains_key(M2_KEY_UPGRADE_FROM),
18725            "servico_m2_overlay must surface M2_KEY_UPGRADE_FROM when \
18726             `:upgrade-from` is non-empty — the accessor and the M2 \
18727             overlay emitter must route through the same substrate- \
18728             primitive typed dispatch on the outer :upgrade-from \
18729             slice (got overlay={overlay:?})",
18730        );
18731        let c = caixa_with_upgrade_from(vec![]);
18732        let overlay = servico_m2_overlay(&c).unwrap();
18733        assert!(
18734            !overlay.contains_key(M2_KEY_UPGRADE_FROM),
18735            "servico_m2_overlay must omit M2_KEY_UPGRADE_FROM when \
18736             `:upgrade-from` is empty — the empty-slice partition \
18737             must route through the accessor's empty-slice return \
18738             unchanged (got overlay={overlay:?})",
18739        );
18740    }
18741
18742    #[test]
18743    fn upgrade_from_projects_slice_by_borrow() {
18744        // The by-borrow pin: [`Caixa::upgrade_from`] returns
18745        // `&[UpgradeFromEntry]` by borrow — the returned slice
18746        // borrows the underlying `Vec<UpgradeFromEntry>` storage of
18747        // the `:upgrade-from` slot and the accessor must not clone
18748        // the backing `Vec` on every call. Peer of the sibling
18749        // outer top-level [`Caixa`] `&[T]`-return by-borrow pins
18750        // (`autores_projects_slice_by_borrow` b5d813f,
18751        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
18752        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
18753        // `exe_projects_slice_by_borrow` 65d9527,
18754        // `servicos_projects_slice_by_borrow` 611f78b,
18755        // `deps_projects_slice_by_borrow` ad34b4e,
18756        // `deps_dev_projects_slice_by_borrow` f7fd81e) on the
18757        // sibling outer top-level [`Caixa`] scalar-element `&[T]`
18758        // axes — extended here to the first outer-`Caixa`
18759        // composite-element `&[Composite]` axis: the accessor's
18760        // returned slice must borrow from `&self` (the returned
18761        // reference's lifetime is tied to `&self`), and calling the
18762        // accessor twice on the same [`Caixa`] must yield slices
18763        // that are pointer-equal (the underlying byte-buffer is the
18764        // storage `Vec`'s allocation, not a fresh copy) as well as
18765        // value-equal (idempotent, no side effects on `&self`).
18766        //
18767        // Pins against a future silent detour that returned an owned
18768        // `Vec<UpgradeFromEntry>` (which would type-check but
18769        // silently clone on every call), a `&Vec<UpgradeFromEntry>`
18770        // return (which would leak the backing `Vec`'s
18771        // grow/push/reserve surface no downstream consumer reaches
18772        // for), or a one-arm-only accessor that returned a
18773        // saturating value on some sentinel input.
18774        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
18775        for upgrade_from in [
18776            vec![],
18777            vec![UpgradeFromEntry {
18778                from: "0.0.1".into(),
18779                instructions: vec![UpgradeInstruction::Restart],
18780            }],
18781            vec![
18782                UpgradeFromEntry {
18783                    from: "0.0.1".into(),
18784                    instructions: vec![UpgradeInstruction::Restart],
18785                },
18786                UpgradeFromEntry {
18787                    from: "0.0.2".into(),
18788                    instructions: vec![UpgradeInstruction::SoftPurge {
18789                        module: "demo".into(),
18790                    }],
18791                },
18792            ],
18793        ] {
18794            let c = caixa_with_upgrade_from(upgrade_from.clone());
18795            let first = c.upgrade_from();
18796            let second = c.upgrade_from();
18797            assert_eq!(
18798                first, second,
18799                "Caixa::upgrade_from must be idempotent — two \
18800                 successive calls on the same &self must return the \
18801                 same &[UpgradeFromEntry]",
18802            );
18803            assert_eq!(
18804                first.as_ptr(),
18805                second.as_ptr(),
18806                "Caixa::upgrade_from must borrow the underlying \
18807                 Vec<UpgradeFromEntry> storage — two successive calls \
18808                 must return slices with the same backing pointer (a \
18809                 fresh Vec<UpgradeFromEntry> clone would change the \
18810                 pointer on every call)",
18811            );
18812            assert_eq!(
18813                first,
18814                upgrade_from.as_slice(),
18815                "Caixa::upgrade_from must return :upgrade-from \
18816                 verbatim by borrow — got {first:?}, expected \
18817                 {upgrade_from:?}",
18818            );
18819        }
18820    }
18821
18822    // ── Caixa::children — outer top-level &[ChildSpec] composite-slice accessor ──
18823
18824    fn caixa_with_children(children: Vec<crate::supervisor::ChildSpec>) -> Caixa {
18825        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
18826        c.children = children;
18827        c
18828    }
18829
18830    #[test]
18831    fn children_returns_children_slice_verbatim_across_permutations() {
18832        // The canonical per-`Caixa` `:children` M2 supervisor-tree-slot
18833        // outer-composite `&[ChildSpec]`-return slice-shape pin:
18834        // [`Caixa::children`] must return the `:children` typed
18835        // `Vec<ChildSpec>` verbatim as a `&[ChildSpec]` slice-view over
18836        // the same backing buffer the raw `self.children.as_slice()`
18837        // field access borrows from, element-equal across every
18838        // representative fixture in the accept-set — `[]` (the "no
18839        // static children declared" arm every non-`Supervisor`-kind
18840        // `defcaixa` carries by `#[serde(default)]` and every
18841        // `SimpleOneForOne` supervisor carries by cross-slot refusal),
18842        // a canonical single-child `Permanent` fixture (the shape
18843        // most `OneForOne` supervisors carry — a single long-running
18844        // worker child), a canonical multi-child list carrying every
18845        // typed restart-policy variant (`Permanent` / `Transient` /
18846        // `Temporary`), and a past-the-guard sentinel — a duplicate
18847        // `:caixa` `[("w", ...), ("w", ...)]` entry pair
18848        // ([`crate::SupervisorSpec::validate`] rejects through
18849        // `DuplicateChildNome { nome: "w" }` but the accessor must
18850        // ship the raw slot verbatim so struct-literal fixtures
18851        // continue to expose the duplicate at the accessor boundary).
18852        //
18853        // Pins against a future silent detour that returned an owned
18854        // `Vec<ChildSpec>` (which would type-check but silently clone
18855        // on every accessor call, breaking the zero-cost projection
18856        // every peer sibling slice accessor carries), a `[dup, dup] →
18857        // [dup]` dedup collapse (which would silently absorb the
18858        // `DuplicateChildNome` refusal case at the accessor boundary
18859        // and the [`crate::StandardLayout::verify`] cross-child gate
18860        // would silently accept a struct-literal `Caixa` carrying the
18861        // drift), a reference to an operator-resolved overlay (the
18862        // future per-cluster `:children-overrides` slot — its
18863        // resolution must land at exactly this accessor body, not
18864        // silently divert the raw slot away from a second consumer),
18865        // or an axis-shuffled projection (a future detour that
18866        // reordered children through the accessor would silently
18867        // split the paired [`crate::StandardLayout::verify`] per-
18868        // supervisor gate's traversal input from the peer
18869        // [`Self::supervisor_view`] fold-in path's clone-order input,
18870        // since the OTP `RestForOne` restart strategy dispatches on
18871        // declared child order and axis reordering would silently
18872        // split the operator's per-cluster restart-fan-out order
18873        // from the caixa.lisp source-order).
18874        //
18875        // Second outer top-level [`Caixa`] `&[Composite]`-return slice
18876        // accessor pin on the substrate primitive for M2 / M3 typed-
18877        // slot vec-carry axes — folds on the outer-`Caixa`
18878        // `&[Composite]` composite-slice sub-family the sibling
18879        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
18880        // (2a1f907) pin opened, peer at the outer altitude of the
18881        // closed inner-`SupervisorSpec` `SupervisorSpec::children`
18882        // (bc92bce) accessor on the same OTP-supervisor static-child-
18883        // list axis.
18884        use crate::supervisor::{ChildSpec, RestartPolicy};
18885        let fixtures: Vec<Vec<ChildSpec>> = vec![
18886            vec![],
18887            vec![ChildSpec {
18888                caixa: "worker".into(),
18889                versao: "^0.1".into(),
18890                restart: RestartPolicy::Permanent,
18891            }],
18892            vec![
18893                ChildSpec {
18894                    caixa: "worker-a".into(),
18895                    versao: "^0.1".into(),
18896                    restart: RestartPolicy::Permanent,
18897                },
18898                ChildSpec {
18899                    caixa: "worker-b".into(),
18900                    versao: "^0.1".into(),
18901                    restart: RestartPolicy::Transient,
18902                },
18903                ChildSpec {
18904                    caixa: "worker-c".into(),
18905                    versao: "^0.1".into(),
18906                    restart: RestartPolicy::Temporary,
18907                },
18908            ],
18909            vec![
18910                ChildSpec {
18911                    caixa: "w".into(),
18912                    versao: "^0.1".into(),
18913                    restart: RestartPolicy::Permanent,
18914                },
18915                ChildSpec {
18916                    caixa: "w".into(),
18917                    versao: "^0.1".into(),
18918                    restart: RestartPolicy::Permanent,
18919                },
18920            ],
18921        ];
18922        for children in fixtures {
18923            let c = caixa_with_children(children.clone());
18924            assert_eq!(
18925                c.children(),
18926                children.as_slice(),
18927                "Caixa::children must return :children verbatim \
18928                 (got {:?}, expected {children:?})",
18929                c.children(),
18930            );
18931            assert_eq!(
18932                c.children(),
18933                c.children.as_slice(),
18934                "Caixa::children must element-equal the raw \
18935                 `self.children.as_slice()` field access across \
18936                 every value in the Vec<ChildSpec> accept-set",
18937            );
18938            assert_eq!(
18939                c.children().is_empty(),
18940                c.children.is_empty(),
18941                "Caixa::children().is_empty() must byte-equal \
18942                 self.children.is_empty() — a presence-bit drift \
18943                 would silently split the paired \
18944                 Caixa::declared_supervisor_slots supervisor-tree \
18945                 declared-slot enumerator's presence probe from the \
18946                 peer Caixa::supervisor_view typed-view composer's \
18947                 fold-in path",
18948            );
18949        }
18950    }
18951
18952    #[test]
18953    fn declared_supervisor_slots_children_arm_routes_through_accessor() {
18954        // Composition pin: [`Caixa::declared_supervisor_slots`]'s
18955        // `:children` presence-probe arm must key off
18956        // [`Caixa::children`], not the raw
18957        // `!self.children.is_empty()` field-probe. Structurally: a
18958        // `Caixa { children: vec![ChildSpec { caixa: "w", versao:
18959        // "^0.1", restart: Permanent }], .. }` must push
18960        // `SUPERVISOR_AUTHOR_KEY_CHILDREN` onto the declared-slot list
18961        // (the presence bit is non-empty, so the supervisor-tree
18962        // kind-coherence gate must surface the slot as "declared"),
18963        // and a `Caixa { children: vec![], .. }` must NOT push the
18964        // label (the "author omitted the slot entirely" arm — the
18965        // empty-slice partition the serde-default folds onto). The
18966        // pair jointly pins the accessor + declared-slot enumerator
18967        // composition: any future silent detour that had the accessor
18968        // collapse `[Permanent]` to `[]` (a `.filter(|c| c.nome() !=
18969        // "__reserved__")` projection) would silently absorb the
18970        // "declared but degenerate" arm at the accessor boundary and
18971        // the [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
18972        // kind-coherence gate would silently accept a struct-literal
18973        // `Caixa` carrying the drift.
18974        //
18975        // Peer of the sibling
18976        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
18977        // (2a1f907) on the M2 `:upgrade-from` composite-slice arm —
18978        // same "the enumerator gate must route through the substrate-
18979        // primitive typed dispatch" discipline extended onto the
18980        // supervisor-tree `:children` composite-slice arm.
18981        use crate::supervisor::{ChildSpec, RestartPolicy};
18982        let c = caixa_with_children(vec![ChildSpec {
18983            caixa: "w".into(),
18984            versao: "^0.1".into(),
18985            restart: RestartPolicy::Permanent,
18986        }]);
18987        let slots = c.declared_supervisor_slots();
18988        assert!(
18989            slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
18990            "declared_supervisor_slots must push \
18991             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
18992             non-empty — the accessor and the enumerator gate must \
18993             route through the same substrate-primitive typed \
18994             dispatch on the outer :children presence bit (got \
18995             slots={slots:?})",
18996        );
18997        let c = caixa_with_children(vec![]);
18998        let slots = c.declared_supervisor_slots();
18999        assert!(
19000            !slots.contains(&crate::render::SUPERVISOR_AUTHOR_KEY_CHILDREN),
19001            "declared_supervisor_slots must NOT push \
19002             SUPERVISOR_AUTHOR_KEY_CHILDREN when `:children` is \
19003             empty — the author-omitted arm must route through the \
19004             accessor's empty-slice return unchanged (got \
19005             slots={slots:?})",
19006        );
19007    }
19008
19009    #[test]
19010    fn supervisor_view_children_arm_routes_through_accessor() {
19011        // Composition pin: [`Caixa::supervisor_view`]'s per-`:children`
19012        // fold-in arm must key off [`Caixa::children`], not the raw
19013        // `self.children.clone()` field-clone. Structurally: a `Caixa {
19014        // kind: Supervisor, estrategia: Some(OneForOne), children:
19015        // vec![ChildSpec { caixa: "w", .. }], .. }` must fold the
19016        // per-child list through the accessor into the typed
19017        // [`SupervisorSpec`] view's `children` field verbatim — every
19018        // entry the accessor surfaces must land in the view's
19019        // `children` slot in the same order. The pair jointly pins the
19020        // accessor + view-composer composition: any future silent
19021        // detour that had the accessor return a fresh-cloned
19022        // `Vec<ChildSpec>` copy would silently break the reference-
19023        // identity pin the peer `supervisor_view` fold-in path reads
19024        // from — the fold would clone once more per accessor call
19025        // instead of borrowing the storage buffer verbatim once.
19026        //
19027        // Peer of the sibling
19028        // `supervisor_view_kind_gate_routes_through_accessor` (35d8b52-
19029        // family) composition pin on the peer kind-gate arm — same
19030        // "the view composer must route through the substrate-
19031        // primitive typed dispatch" discipline extended onto the
19032        // per-`:children` fold-in arm, closing the supervisor-view
19033        // composer's routing invariant on the composite-slice input.
19034        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19035        let mut c = caixa_with_children(vec![
19036            ChildSpec {
19037                caixa: "worker-a".into(),
19038                versao: "^0.1".into(),
19039                restart: RestartPolicy::Permanent,
19040            },
19041            ChildSpec {
19042                caixa: "worker-b".into(),
19043                versao: "^0.1".into(),
19044                restart: RestartPolicy::Transient,
19045            },
19046        ]);
19047        c.kind = crate::CaixaKind::Supervisor;
19048        c.estrategia = Some(RestartStrategy::OneForOne);
19049        let view = c
19050            .supervisor_view()
19051            .expect("Supervisor kind must produce a supervisor_view");
19052        assert_eq!(
19053            view.children(),
19054            c.children(),
19055            "supervisor_view must fold Caixa::children verbatim into \
19056             SupervisorSpec::children — the accessor and the view \
19057             composer must route through the same substrate-primitive \
19058             typed dispatch on the outer :children slice (got view \
19059             children={:?}, expected {:?})",
19060            view.children(),
19061            c.children(),
19062        );
19063    }
19064
19065    #[test]
19066    fn children_projects_slice_by_borrow() {
19067        // The by-borrow pin: [`Caixa::children`] returns
19068        // `&[ChildSpec]` by borrow — the returned slice borrows the
19069        // underlying `Vec<ChildSpec>` storage of the `:children` slot
19070        // and the accessor must not clone the backing `Vec` on every
19071        // call. Peer of the sibling outer top-level [`Caixa`]
19072        // `&[T]`-return by-borrow pins (`autores_projects_slice_by_borrow`
19073        // b5d813f, `etiquetas_projects_slice_by_borrow` 78c7d3c,
19074        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19075        // `exe_projects_slice_by_borrow` 65d9527,
19076        // `servicos_projects_slice_by_borrow` 611f78b,
19077        // `deps_projects_slice_by_borrow` ad34b4e,
19078        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19079        // `upgrade_from_projects_slice_by_borrow` 2a1f907) on the
19080        // sibling outer top-level [`Caixa`] scalar-element and
19081        // composite-element `&[T]` axes — folds on the outer-`Caixa`
19082        // composite-element `&[Composite]` axis: the accessor's
19083        // returned slice must borrow from `&self` (the returned
19084        // reference's lifetime is tied to `&self`), and calling the
19085        // accessor twice on the same [`Caixa`] must yield slices
19086        // that are pointer-equal (the underlying byte-buffer is the
19087        // storage `Vec`'s allocation, not a fresh copy) as well as
19088        // value-equal (idempotent, no side effects on `&self`).
19089        //
19090        // Pins against a future silent detour that returned an owned
19091        // `Vec<ChildSpec>` (which would type-check but silently clone
19092        // on every call), a `&Vec<ChildSpec>` return (which would leak
19093        // the backing `Vec`'s grow/push/reserve surface no downstream
19094        // consumer reaches for), or a one-arm-only accessor that
19095        // returned a saturating value on some sentinel input.
19096        use crate::supervisor::{ChildSpec, RestartPolicy};
19097        for children in [
19098            vec![],
19099            vec![ChildSpec {
19100                caixa: "w".into(),
19101                versao: "^0.1".into(),
19102                restart: RestartPolicy::Permanent,
19103            }],
19104            vec![
19105                ChildSpec {
19106                    caixa: "worker-a".into(),
19107                    versao: "^0.1".into(),
19108                    restart: RestartPolicy::Permanent,
19109                },
19110                ChildSpec {
19111                    caixa: "worker-b".into(),
19112                    versao: "^0.1".into(),
19113                    restart: RestartPolicy::Transient,
19114                },
19115            ],
19116        ] {
19117            let c = caixa_with_children(children.clone());
19118            let first = c.children();
19119            let second = c.children();
19120            assert_eq!(
19121                first, second,
19122                "Caixa::children must be idempotent — two successive \
19123                 calls on the same &self must return the same \
19124                 &[ChildSpec]",
19125            );
19126            assert_eq!(
19127                first.as_ptr(),
19128                second.as_ptr(),
19129                "Caixa::children must borrow the underlying \
19130                 Vec<ChildSpec> storage — two successive calls must \
19131                 return slices with the same backing pointer (a fresh \
19132                 Vec<ChildSpec> clone would change the pointer on \
19133                 every call)",
19134            );
19135            assert_eq!(
19136                first,
19137                children.as_slice(),
19138                "Caixa::children must return :children verbatim by \
19139                 borrow — got {first:?}, expected {children:?}",
19140            );
19141        }
19142    }
19143
19144    // ── Caixa::membros — outer top-level &[Membro] composite-slice accessor ──
19145
19146    fn caixa_aplicacao_with_membros(membros: Vec<crate::aplicacao::Membro>) -> Caixa {
19147        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19148        c.kind = CaixaKind::Aplicacao;
19149        c.membros = membros;
19150        c
19151    }
19152
19153    #[test]
19154    fn membros_returns_membros_slice_verbatim_across_permutations() {
19155        // The canonical per-`Caixa` `:membros` M3 mesh-slot outer-
19156        // composite `&[Membro]`-return slice-shape pin:
19157        // [`Caixa::membros`] must return the `:membros` typed
19158        // `Vec<Membro>` verbatim as a `&[Membro]` slice-view over the
19159        // same backing buffer the raw `self.membros.as_slice()` field
19160        // access borrows from, element-equal across every
19161        // representative fixture in the accept-set — `[]` (the "no
19162        // members declared" arm every non-`Aplicacao`-kind `defcaixa`
19163        // carries by `#[serde(default)]` and every partially-authored
19164        // Aplicacao carries before the
19165        // [`crate::AplicacaoError::MembrosEmpty`] gate fires), a
19166        // canonical single-member fixture (the shape a minimal
19167        // Aplicacao carries — one Servico wrapping one contained
19168        // computation), a canonical multi-member list carrying three
19169        // distinct entries (the canonical checkout-shape Aplicacao —
19170        // cart / pricing / auth — every canonical example carries), and
19171        // a past-the-guard sentinel — a duplicate `:caixa`
19172        // `[("cart", ...), ("cart", ...)]` entry pair
19173        // ([`crate::AplicacaoSpec::validate`] rejects through
19174        // `DuplicateMembro { nome: "cart" }` but the accessor must ship
19175        // the raw slot verbatim so struct-literal fixtures continue to
19176        // expose the duplicate at the accessor boundary).
19177        //
19178        // Pins against a future silent detour that returned an owned
19179        // `Vec<Membro>` (which would type-check but silently clone on
19180        // every accessor call, breaking the zero-cost projection every
19181        // peer sibling slice accessor carries), a `[dup, dup] → [dup]`
19182        // dedup collapse (which would silently absorb the
19183        // `DuplicateMembro` refusal case at the accessor boundary and
19184        // the [`crate::StandardLayout::verify`] cross-member gate would
19185        // silently accept a struct-literal `Caixa` carrying the drift),
19186        // a reference to an operator-resolved overlay (the future per-
19187        // cluster `:membros-overrides` slot — its resolution must land
19188        // at exactly this accessor body, not silently divert the raw
19189        // slot away from a second consumer), or an axis-shuffled
19190        // projection (a future detour that reordered members through
19191        // the accessor would silently split the paired
19192        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19193        // traversal input from the peer [`Self::aplicacao_view`] fold-
19194        // in path's clone-order input, since the canonical `:contratos`
19195        // `:de`/`:para` and `:entrada :para` cross-slot refusal probes
19196        // read the member set through the same slice).
19197        //
19198        // Third outer top-level [`Caixa`] `&[Composite]`-return slice
19199        // accessor pin on the substrate primitive for M2 / M3 typed-
19200        // slot vec-carry axes — opens the outer-`Caixa` M3 mesh-slot
19201        // arm of the `&[Composite]` composite-slice sub-family the
19202        // sibling M2 `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19203        // (2a1f907) and
19204        // `children_returns_children_slice_verbatim_across_permutations`
19205        // (c17b51e) pins opened, peer at the outer altitude of the
19206        // closed inner-[`crate::AplicacaoSpec::membros`] (6c77e36)
19207        // accessor on the same MESH-COMPOSITION per-Aplicacao member-
19208        // list axis.
19209        use crate::aplicacao::Membro;
19210        let fixtures: Vec<Vec<Membro>> = vec![
19211            vec![],
19212            vec![Membro {
19213                caixa: "cart".into(),
19214                versao: "^0.1".into(),
19215            }],
19216            vec![
19217                Membro {
19218                    caixa: "cart".into(),
19219                    versao: "^0.1".into(),
19220                },
19221                Membro {
19222                    caixa: "pricing".into(),
19223                    versao: "^0.2".into(),
19224                },
19225                Membro {
19226                    caixa: "auth".into(),
19227                    versao: "^1.0".into(),
19228                },
19229            ],
19230            vec![
19231                Membro {
19232                    caixa: "cart".into(),
19233                    versao: "^0.1".into(),
19234                },
19235                Membro {
19236                    caixa: "cart".into(),
19237                    versao: "^0.1".into(),
19238                },
19239            ],
19240        ];
19241        for membros in fixtures {
19242            let c = caixa_aplicacao_with_membros(membros.clone());
19243            assert_eq!(
19244                c.membros(),
19245                membros.as_slice(),
19246                "Caixa::membros must return :membros verbatim \
19247                 (got {:?}, expected {membros:?})",
19248                c.membros(),
19249            );
19250            assert_eq!(
19251                c.membros(),
19252                c.membros.as_slice(),
19253                "Caixa::membros must element-equal the raw \
19254                 `self.membros.as_slice()` field access across every \
19255                 value in the Vec<Membro> accept-set",
19256            );
19257            assert_eq!(
19258                c.membros().is_empty(),
19259                c.membros.is_empty(),
19260                "Caixa::membros().is_empty() must byte-equal \
19261                 self.membros.is_empty() — a presence-bit drift would \
19262                 silently split the paired Caixa::declared_mesh_slots \
19263                 mesh declared-slot enumerator's presence probe from \
19264                 the peer Caixa::aplicacao_view typed-view composer's \
19265                 fold-in path",
19266            );
19267        }
19268    }
19269
19270    #[test]
19271    fn declared_mesh_slots_membros_arm_routes_through_accessor() {
19272        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:membros`
19273        // presence-probe arm must key off [`Caixa::membros`], not the
19274        // raw `!self.membros.is_empty()` field-probe. Structurally: a
19275        // `Caixa { membros: vec![Membro { caixa: "cart", versao:
19276        // "^0.1" }], .. }` must push `M3_AUTHOR_KEY_MEMBROS` onto the
19277        // declared-slot list (the presence bit is non-empty, so the
19278        // mesh kind-coherence gate must surface the slot as
19279        // "declared"), and a `Caixa { membros: vec![], .. }` must NOT
19280        // push the label (the "author omitted the slot entirely" arm
19281        // — the empty-slice partition the serde-default folds onto).
19282        // The pair jointly pins the accessor + declared-slot
19283        // enumerator composition: any future silent detour that had
19284        // the accessor collapse `[Membro { .. }]` to `[]` (a
19285        // `.filter(|m| m.nome() != "__reserved__")` projection) would
19286        // silently absorb the "declared but degenerate" arm at the
19287        // accessor boundary and the
19288        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19289        // coherence gate would silently accept a struct-literal
19290        // `Caixa` carrying the drift.
19291        //
19292        // Peer of the sibling
19293        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19294        // (2a1f907) and
19295        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19296        // (c17b51e) composition pins on the M2 `:upgrade-from` /
19297        // `:children` composite-slice arms — same "the enumerator gate
19298        // must route through the substrate-primitive typed dispatch"
19299        // discipline extended onto the M3 `:membros` composite-slice
19300        // arm, opening the M3 arm of the declared-slot enumerator's
19301        // routing invariant.
19302        use crate::aplicacao::Membro;
19303        let c = caixa_aplicacao_with_membros(vec![Membro {
19304            caixa: "cart".into(),
19305            versao: "^0.1".into(),
19306        }]);
19307        let slots = c.declared_mesh_slots();
19308        assert!(
19309            slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19310            "declared_mesh_slots must push M3_AUTHOR_KEY_MEMBROS when \
19311             `:membros` is non-empty — the accessor and the enumerator \
19312             gate must route through the same substrate-primitive \
19313             typed dispatch on the outer :membros presence bit (got \
19314             slots={slots:?})",
19315        );
19316        let c = caixa_aplicacao_with_membros(vec![]);
19317        let slots = c.declared_mesh_slots();
19318        assert!(
19319            !slots.contains(&crate::render::M3_AUTHOR_KEY_MEMBROS),
19320            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_MEMBROS \
19321             when `:membros` is empty — the author-omitted arm must \
19322             route through the accessor's empty-slice return unchanged \
19323             (got slots={slots:?})",
19324        );
19325    }
19326
19327    #[test]
19328    fn aplicacao_view_membros_arm_routes_through_accessor() {
19329        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:membros`
19330        // fold-in arm must key off [`Caixa::membros`], not the raw
19331        // `self.membros.clone()` field-clone. Structurally: a `Caixa {
19332        // kind: Aplicacao, membros: vec![Membro { caixa: "cart", .. },
19333        // Membro { caixa: "pricing", .. }], .. }` must fold the per-
19334        // member list through the accessor into the typed
19335        // [`crate::AplicacaoSpec`] view's `membros` slot verbatim —
19336        // every entry the accessor surfaces must land in the view's
19337        // `membros` slot in the same order. The pair jointly pins the
19338        // accessor + view-composer composition: any future silent
19339        // detour that had the accessor return a fresh-cloned
19340        // `Vec<Membro>` copy would silently break the reference-
19341        // identity pin the peer `aplicacao_view` fold-in path reads
19342        // from — the fold would clone once more per accessor call
19343        // instead of borrowing the storage buffer verbatim once.
19344        //
19345        // Peer of the sibling
19346        // `aplicacao_view_politicas_arm_folds_through_accessor`
19347        // (5d23d29) /
19348        // `aplicacao_view_placement_arm_folds_through_accessor`
19349        // (4fb8074) /
19350        // `aplicacao_view_entrada_arm_folds_through_accessor` (e4128e4)
19351        // composition pins on the M3 `:politicas` / `:placement` /
19352        // `:entrada` outer-`Option<&Composite>` arms — extended here to
19353        // the M3 `:membros` outer-`&[Composite]` composite-slice arm,
19354        // closing the aplicacao-view composer's routing invariant on
19355        // the composite-slice input.
19356        use crate::aplicacao::Membro;
19357        let c = caixa_aplicacao_with_membros(vec![
19358            Membro {
19359                caixa: "cart".into(),
19360                versao: "^0.1".into(),
19361            },
19362            Membro {
19363                caixa: "pricing".into(),
19364                versao: "^0.2".into(),
19365            },
19366        ]);
19367        let view = c
19368            .aplicacao_view()
19369            .expect("Aplicacao kind must produce an aplicacao_view");
19370        assert_eq!(
19371            view.membros(),
19372            c.membros(),
19373            "aplicacao_view must fold Caixa::membros verbatim into \
19374             AplicacaoSpec::membros — the accessor and the view \
19375             composer must route through the same substrate-primitive \
19376             typed dispatch on the outer :membros slice (got view \
19377             membros={:?}, expected {:?})",
19378            view.membros(),
19379            c.membros(),
19380        );
19381    }
19382
19383    #[test]
19384    fn membros_projects_slice_by_borrow() {
19385        // The by-borrow pin: [`Caixa::membros`] returns `&[Membro]` by
19386        // borrow — the returned slice borrows the underlying
19387        // `Vec<Membro>` storage of the `:membros` slot and the
19388        // accessor must not clone the backing `Vec` on every call.
19389        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19390        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19391        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19392        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19393        // `exe_projects_slice_by_borrow` 65d9527,
19394        // `servicos_projects_slice_by_borrow` 611f78b,
19395        // `deps_projects_slice_by_borrow` ad34b4e,
19396        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19397        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19398        // `children_projects_slice_by_borrow` c17b51e) on the sibling
19399        // outer top-level [`Caixa`] scalar-element and composite-
19400        // element `&[T]` axes — folds on the outer-`Caixa` M3 mesh-
19401        // slot composite-element `&[Composite]` axis: the accessor's
19402        // returned slice must borrow from `&self` (the returned
19403        // reference's lifetime is tied to `&self`), and calling the
19404        // accessor twice on the same [`Caixa`] must yield slices that
19405        // are pointer-equal (the underlying byte-buffer is the storage
19406        // `Vec`'s allocation, not a fresh copy) as well as value-equal
19407        // (idempotent, no side effects on `&self`).
19408        //
19409        // Pins against a future silent detour that returned an owned
19410        // `Vec<Membro>` (which would type-check but silently clone on
19411        // every call), a `&Vec<Membro>` return (which would leak the
19412        // backing `Vec`'s grow/push/reserve surface no downstream
19413        // consumer reaches for), or a one-arm-only accessor that
19414        // returned a saturating value on some sentinel input.
19415        use crate::aplicacao::Membro;
19416        for membros in [
19417            vec![],
19418            vec![Membro {
19419                caixa: "cart".into(),
19420                versao: "^0.1".into(),
19421            }],
19422            vec![
19423                Membro {
19424                    caixa: "cart".into(),
19425                    versao: "^0.1".into(),
19426                },
19427                Membro {
19428                    caixa: "pricing".into(),
19429                    versao: "^0.2".into(),
19430                },
19431            ],
19432        ] {
19433            let c = caixa_aplicacao_with_membros(membros.clone());
19434            let first = c.membros();
19435            let second = c.membros();
19436            assert_eq!(
19437                first, second,
19438                "Caixa::membros must be idempotent — two successive \
19439                 calls on the same &self must return the same &[Membro]",
19440            );
19441            assert_eq!(
19442                first.as_ptr(),
19443                second.as_ptr(),
19444                "Caixa::membros must borrow the underlying Vec<Membro> \
19445                 storage — two successive calls must return slices with \
19446                 the same backing pointer (a fresh Vec<Membro> clone \
19447                 would change the pointer on every call)",
19448            );
19449            assert_eq!(
19450                first,
19451                membros.as_slice(),
19452                "Caixa::membros must return :membros verbatim by borrow \
19453                 — got {first:?}, expected {membros:?}",
19454            );
19455        }
19456    }
19457
19458    // ── Caixa::contratos — outer top-level &[WitContract] composite-slice accessor ──
19459
19460    fn caixa_aplicacao_with_contratos(contratos: Vec<crate::aplicacao::WitContract>) -> Caixa {
19461        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19462        c.kind = CaixaKind::Aplicacao;
19463        c.contratos = contratos;
19464        c
19465    }
19466
19467    fn contrato_http_for_test(
19468        de: &str,
19469        para: &str,
19470        endpoint: &str,
19471    ) -> crate::aplicacao::WitContract {
19472        crate::aplicacao::WitContract {
19473            de: de.into(),
19474            para: para.into(),
19475            wit: "wasi:http/proxy".into(),
19476            endpoint: Some(endpoint.into()),
19477            subject: None,
19478            slot: None,
19479        }
19480    }
19481
19482    #[test]
19483    fn contratos_returns_contratos_slice_verbatim_across_permutations() {
19484        // The canonical per-`Caixa` `:contratos` M3 mesh-slot outer-
19485        // composite `&[WitContract]`-return slice-shape pin:
19486        // [`Caixa::contratos`] must return the `:contratos` typed
19487        // `Vec<WitContract>` verbatim as a `&[WitContract]` slice-view
19488        // over the same backing buffer the raw
19489        // `self.contratos.as_slice()` field access borrows from,
19490        // element-equal across every representative fixture in the
19491        // accept-set — `[]` (the "no contracts declared" arm every
19492        // non-`Aplicacao`-kind `defcaixa` carries by
19493        // `#[serde(default)]` and every leaf-Aplicacao with a single
19494        // member carries), a canonical single-edge fixture (the
19495        // minimal directed-graph shape: one HTTP-shape `(cart → catalog)`
19496        // edge), and a canonical multi-edge fixture with three distinct
19497        // edges (the checkout-shape Aplicacao's HTTP-fan pattern:
19498        // `(cart → catalog)`, `(cart → pricing)`, `(cart → auth)`).
19499        //
19500        // Pins against a future silent detour that returned an owned
19501        // `Vec<WitContract>` (which would type-check but silently clone
19502        // on every accessor call, breaking the zero-cost projection
19503        // every peer sibling slice accessor carries), an axis-shuffled
19504        // projection (a future detour that reordered edges through the
19505        // accessor would silently split the paired
19506        // [`crate::StandardLayout::verify`] per-Aplicacao gate's
19507        // traversal input from the peer [`Self::aplicacao_view`] fold-
19508        // in path's clone-order input, since every canonical
19509        // `caixa-mesh` renderer's per-`(:de, :para)` adjacency-list
19510        // seed dispatch reads the edge set through the same slice),
19511        // or a reference to an operator-resolved overlay (the future
19512        // per-cluster `:contratos-overrides` slot — its resolution
19513        // must land at exactly this accessor body, not silently divert
19514        // the raw slot away from a second consumer).
19515        //
19516        // Fourth outer top-level [`Caixa`] `&[Composite]`-return slice
19517        // accessor pin on the substrate primitive for M2 / M3 typed-
19518        // slot vec-carry axes — closes the outer-`Caixa`
19519        // `&[Composite]` composite-slice sub-family the sibling M2
19520        // `upgrade_from_returns_upgrade_from_slice_verbatim_across_permutations`
19521        // (2a1f907) and
19522        // `children_returns_children_slice_verbatim_across_permutations`
19523        // (c17b51e) pins opened and the M3
19524        // `membros_returns_membros_slice_verbatim_across_permutations`
19525        // (0f26987) pin folded on, closing the outer-`Caixa` M3 mesh-
19526        // slot arm of the composite-slice sub-family. Peer at the outer
19527        // altitude of the closed inner-
19528        // [`crate::AplicacaoSpec::contratos`] (0dcc926) accessor on the
19529        // same MESH-COMPOSITION per-Aplicacao contract-list axis.
19530        let fixtures: Vec<Vec<crate::aplicacao::WitContract>> = vec![
19531            vec![],
19532            vec![contrato_http_for_test("cart", "catalog", "/items")],
19533            vec![
19534                contrato_http_for_test("cart", "catalog", "/items"),
19535                contrato_http_for_test("cart", "pricing", "/price"),
19536                contrato_http_for_test("cart", "auth", "/whoami"),
19537            ],
19538        ];
19539        for contratos in fixtures {
19540            let c = caixa_aplicacao_with_contratos(contratos.clone());
19541            assert_eq!(
19542                c.contratos(),
19543                contratos.as_slice(),
19544                "Caixa::contratos must return :contratos verbatim \
19545                 (got {:?}, expected {contratos:?})",
19546                c.contratos(),
19547            );
19548            assert_eq!(
19549                c.contratos(),
19550                c.contratos.as_slice(),
19551                "Caixa::contratos must element-equal the raw \
19552                 `self.contratos.as_slice()` field access across every \
19553                 value in the Vec<WitContract> accept-set",
19554            );
19555            assert_eq!(
19556                c.contratos().is_empty(),
19557                c.contratos.is_empty(),
19558                "Caixa::contratos().is_empty() must byte-equal \
19559                 self.contratos.is_empty() — a presence-bit drift would \
19560                 silently split the paired Caixa::declared_mesh_slots \
19561                 mesh declared-slot enumerator's presence probe from \
19562                 the peer Caixa::aplicacao_view typed-view composer's \
19563                 fold-in path",
19564            );
19565        }
19566    }
19567
19568    #[test]
19569    fn declared_mesh_slots_contratos_arm_routes_through_accessor() {
19570        // Composition pin: [`Caixa::declared_mesh_slots`]'s `:contratos`
19571        // presence-probe arm must key off [`Caixa::contratos`], not the
19572        // raw `!self.contratos.is_empty()` field-probe. Structurally: a
19573        // `Caixa { contratos: vec![WitContract { .. }], .. }` must push
19574        // `M3_AUTHOR_KEY_CONTRATOS` onto the declared-slot list (the
19575        // presence bit is non-empty, so the mesh kind-coherence gate
19576        // must surface the slot as "declared"), and a `Caixa {
19577        // contratos: vec![], .. }` must NOT push the label (the "author
19578        // omitted the slot entirely" arm — the empty-slice partition
19579        // the serde-default folds onto). The pair jointly pins the
19580        // accessor + declared-slot enumerator composition: any future
19581        // silent detour that had the accessor collapse
19582        // `[WitContract { .. }]` to `[]` (a `.filter(|c| c.de() !=
19583        // "__reserved__")` projection) would silently absorb the
19584        // "declared but degenerate" arm at the accessor boundary and
19585        // the [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
19586        // coherence gate would silently accept a struct-literal
19587        // `Caixa` carrying the drift.
19588        //
19589        // Peer of the sibling
19590        // `declared_servico_slots_upgrade_from_arm_routes_through_accessor`
19591        // (2a1f907),
19592        // `declared_supervisor_slots_children_arm_routes_through_accessor`
19593        // (c17b51e), and
19594        // `declared_mesh_slots_membros_arm_routes_through_accessor`
19595        // (0f26987) composition pins on the M2 `:upgrade-from` /
19596        // `:children` / M3 `:membros` composite-slice arms — same "the
19597        // enumerator gate must route through the substrate-primitive
19598        // typed dispatch" discipline extended onto the M3 `:contratos`
19599        // composite-slice arm, closing the M3 mesh-slot arm of the
19600        // declared-slot enumerator's routing invariant on the
19601        // composite-slice inputs.
19602        let c = caixa_aplicacao_with_contratos(vec![contrato_http_for_test(
19603            "cart", "catalog", "/items",
19604        )]);
19605        let slots = c.declared_mesh_slots();
19606        assert!(
19607            slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19608            "declared_mesh_slots must push M3_AUTHOR_KEY_CONTRATOS when \
19609             `:contratos` is non-empty — the accessor and the enumerator \
19610             gate must route through the same substrate-primitive \
19611             typed dispatch on the outer :contratos presence bit (got \
19612             slots={slots:?})",
19613        );
19614        let c = caixa_aplicacao_with_contratos(vec![]);
19615        let slots = c.declared_mesh_slots();
19616        assert!(
19617            !slots.contains(&crate::render::M3_AUTHOR_KEY_CONTRATOS),
19618            "declared_mesh_slots must NOT push M3_AUTHOR_KEY_CONTRATOS \
19619             when `:contratos` is empty — the author-omitted arm must \
19620             route through the accessor's empty-slice return unchanged \
19621             (got slots={slots:?})",
19622        );
19623    }
19624
19625    #[test]
19626    fn aplicacao_view_contratos_arm_routes_through_accessor() {
19627        // Composition pin: [`Caixa::aplicacao_view`]'s per-`:contratos`
19628        // fold-in arm must key off [`Caixa::contratos`], not the raw
19629        // `self.contratos.clone()` field-clone. Structurally: a `Caixa
19630        // { kind: Aplicacao, contratos: vec![WitContract { de: "cart",
19631        // .. }, WitContract { de: "pricing", .. }], .. }` must fold the
19632        // per-edge list through the accessor into the typed
19633        // [`crate::AplicacaoSpec`] view's `contratos` slot verbatim —
19634        // every entry the accessor surfaces must land in the view's
19635        // `contratos` slot in the same order. The pair jointly pins
19636        // the accessor + view-composer composition: a future silent
19637        // detour that had the accessor shuffle or drop an edge would
19638        // silently split the paired declared-slot enumerator's
19639        // presence bit from the typed-view composer's edge-list, a
19640        // two-consumer split at the enumerator and the view composer
19641        // far from the source `caixa.lisp`.
19642        //
19643        // Peer of the sibling
19644        // `aplicacao_view_membros_arm_routes_through_accessor`
19645        // (0f26987) composition pin on the M3 `:membros` outer-
19646        // `&[Composite]` composite-slice arm, closing the aplicacao-
19647        // view composer's routing invariant on the composite-slice
19648        // inputs at the outer altitude.
19649        let c = caixa_aplicacao_with_contratos(vec![
19650            contrato_http_for_test("cart", "catalog", "/items"),
19651            contrato_http_for_test("cart", "pricing", "/price"),
19652        ]);
19653        let view = c
19654            .aplicacao_view()
19655            .expect("Aplicacao kind must produce an aplicacao_view");
19656        assert_eq!(
19657            view.contratos(),
19658            c.contratos(),
19659            "aplicacao_view must fold Caixa::contratos verbatim into \
19660             AplicacaoSpec::contratos — the accessor and the view \
19661             composer must route through the same substrate-primitive \
19662             typed dispatch on the outer :contratos slice (got view \
19663             contratos={:?}, expected {:?})",
19664            view.contratos(),
19665            c.contratos(),
19666        );
19667    }
19668
19669    #[test]
19670    fn contratos_projects_slice_by_borrow() {
19671        // The by-borrow pin: [`Caixa::contratos`] returns `&[WitContract]`
19672        // by borrow — the returned slice borrows the underlying
19673        // `Vec<WitContract>` storage of the `:contratos` slot and the
19674        // accessor must not clone the backing `Vec` on every call.
19675        // Peer of the sibling outer top-level [`Caixa`] `&[T]`-return
19676        // by-borrow pins (`autores_projects_slice_by_borrow` b5d813f,
19677        // `etiquetas_projects_slice_by_borrow` 78c7d3c,
19678        // `bibliotecas_projects_slice_by_borrow` 8a36c23,
19679        // `exe_projects_slice_by_borrow` 65d9527,
19680        // `servicos_projects_slice_by_borrow` 611f78b,
19681        // `deps_projects_slice_by_borrow` ad34b4e,
19682        // `deps_dev_projects_slice_by_borrow` f7fd81e,
19683        // `upgrade_from_projects_slice_by_borrow` 2a1f907,
19684        // `children_projects_slice_by_borrow` c17b51e,
19685        // `membros_projects_slice_by_borrow` 0f26987) on the sibling
19686        // outer top-level [`Caixa`] scalar-element and composite-
19687        // element `&[T]` axes — closes the outer-`Caixa` M3 mesh-slot
19688        // composite-element `&[Composite]` axis on the by-borrow pin:
19689        // the accessor's returned slice must borrow from `&self` (the
19690        // returned reference's lifetime is tied to `&self`), and
19691        // calling the accessor twice on the same [`Caixa`] must yield
19692        // slices that are pointer-equal (the underlying byte-buffer is
19693        // the storage `Vec`'s allocation, not a fresh copy) as well as
19694        // value-equal (idempotent, no side effects on `&self`).
19695        //
19696        // Pins against a future silent detour that returned an owned
19697        // `Vec<WitContract>` (which would type-check but silently clone
19698        // on every call), a `&Vec<WitContract>` return (which would
19699        // leak the backing `Vec`'s grow/push/reserve surface no
19700        // downstream consumer reaches for), or a one-arm-only accessor
19701        // that returned a saturating value on some sentinel input.
19702        for contratos in [
19703            vec![],
19704            vec![contrato_http_for_test("cart", "catalog", "/items")],
19705            vec![
19706                contrato_http_for_test("cart", "catalog", "/items"),
19707                contrato_http_for_test("cart", "pricing", "/price"),
19708            ],
19709        ] {
19710            let c = caixa_aplicacao_with_contratos(contratos.clone());
19711            let first = c.contratos();
19712            let second = c.contratos();
19713            assert_eq!(
19714                first, second,
19715                "Caixa::contratos must be idempotent — two successive \
19716                 calls on the same &self must return the same \
19717                 &[WitContract]",
19718            );
19719            assert_eq!(
19720                first.as_ptr(),
19721                second.as_ptr(),
19722                "Caixa::contratos must borrow the underlying \
19723                 Vec<WitContract> storage — two successive calls must \
19724                 return slices with the same backing pointer (a fresh \
19725                 Vec<WitContract> clone would change the pointer on \
19726                 every call)",
19727            );
19728            assert_eq!(
19729                first,
19730                contratos.as_slice(),
19731                "Caixa::contratos must return :contratos verbatim by \
19732                 borrow — got {first:?}, expected {contratos:?}",
19733            );
19734        }
19735    }
19736
19737    // ── drift-detection: Caixa top-level multi-word serde-derive-to-const identity ──
19738
19739    #[test]
19740    fn caixa_multi_word_serde_keys_match_lifted_top_level_key_consts() {
19741        // Load-bearing invariant: every multi-word top-level [`Caixa`]
19742        // serde-derived JSON key routes through a lifted `&'static str`
19743        // const. The Rust field names are `snake_case`
19744        // (`deps_dev` / `upgrade_from` / `max_restarts` /
19745        // `restart_window`); [`Caixa`]'s `#[serde(rename_all =
19746        // "camelCase")]` derive attribute maps each to the camelCase
19747        // byte-string the [`Caixa::to_lisp`] round-trip's
19748        // `serde_json::to_value(self)` step lands under before
19749        // `tatara_lisp::domain::json_to_sexp` re-projects the JSON keys
19750        // to the kebab-case `:deps-dev` / `:upgrade-from` /
19751        // `:max-restarts` / `:restart-window` author surface. Serialize
19752        // a fully-populated [`Caixa`] and pin that each canonical
19753        // byte-sequence appears verbatim in the JSON — a future
19754        // accidental `rename_all = "snake_case"` / `"kebab-case"` /
19755        // verbatim-field-name flip at the derive attribute (any of
19756        // which would silently break every [`Caixa::to_lisp`]
19757        // round-trip and the future M4 operator-side manifest ingest's
19758        // `Value::get(<key>)` navigation) surfaces here as a build-time
19759        // test failure at `manifest.rs`, not as an apply-time
19760        // `.get(<stale-canonical-const>)` returning `None` far from the
19761        // derive-attr drift's commit. Same discipline the sibling
19762        // `supervisor_spec_serde_keys_match_lifted_supervisor_key_consts`
19763        // (40cc4e5), `membro_serde_keys_match_lifted_membro_key_consts`
19764        // (ce80ca0), and `upgrade_from_entry_serde_keys_match_lifted_
19765        // m2_upgrade_from_key_consts` (36ffe65) pins established on the
19766        // sibling M2 supervision-tree, M3 [`Membro`] per-entry, and M2
19767        // [`UpgradeFromEntry`] per-entry axes — extended here to the
19768        // enclosing M0 [`Caixa`] top-level axis so the last of the four
19769        // multi-word top-level [`Caixa`] serde-derived JSON keys
19770        // (`depsDev`) joins the substrate's "one canonical byte-string
19771        // per typed serialized-key axis" discipline.
19772        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
19773        use crate::upgrade::{UpgradeFromEntry, UpgradeInstruction};
19774        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19775        c.deps_dev = vec![Dep::simple("tatara-check", "^0.1")];
19776        c.upgrade_from = vec![UpgradeFromEntry {
19777            from: "0.0.1".into(),
19778            instructions: vec![UpgradeInstruction::Restart],
19779        }];
19780        c.estrategia = Some(RestartStrategy::OneForOne);
19781        c.max_restarts = Some(3);
19782        c.restart_window = Some("60s".into());
19783        c.children = vec![ChildSpec {
19784            caixa: "child".into(),
19785            versao: "^0.1".into(),
19786            restart: RestartPolicy::Permanent,
19787        }];
19788        let json = serde_json::to_string(&c).unwrap();
19789        for key in [
19790            crate::render::CAIXA_KEY_DEPS_DEV,
19791            crate::render::M2_KEY_UPGRADE_FROM,
19792            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19793            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19794        ] {
19795            let quoted = format!("\"{key}\"");
19796            assert!(
19797                json.contains(&quoted),
19798                "serialized Caixa must carry the lifted top-level \
19799                 multi-word byte-sequence {quoted} verbatim in the JSON \
19800                 emission (got: {json})",
19801            );
19802        }
19803    }
19804
19805    #[test]
19806    fn caixa_top_level_multi_word_key_consts_are_pairwise_distinct() {
19807        // Cross-axis drift-detection pin: a future collapse of the four
19808        // canonical [`Caixa`] top-level multi-word byte-strings onto the
19809        // same value (e.g. an accidental copy-paste flip of
19810        // [`crate::render::CAIXA_KEY_DEPS_DEV`] to also read
19811        // `"upgradeFrom"`) would silently reroute every downstream
19812        // `Value::get(<key>)` probe on one axis onto the sibling axis's
19813        // top-level entry and pass every propagation-probe test that
19814        // expected only the stale axis's value. Peer of the sibling
19815        // four-way distinct pin on the `SUPERVISOR_KEY_*` tetrad
19816        // (40cc4e5) and the two-way pin on `MEMBRO_KEY_*` (ce80ca0).
19817        let all = [
19818            crate::render::CAIXA_KEY_DEPS_DEV,
19819            crate::render::M2_KEY_UPGRADE_FROM,
19820            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19821            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19822        ];
19823        for (i, a) in all.iter().enumerate() {
19824            for b in all.iter().skip(i + 1) {
19825                assert_ne!(
19826                    a, b,
19827                    "Caixa top-level multi-word key consts must be \
19828                     pairwise-distinct canonical byte-sequences — got \
19829                     `{a}` == `{b}`",
19830                );
19831            }
19832        }
19833    }
19834
19835    #[test]
19836    fn caixa_top_level_multi_word_key_consts_are_lower_camel_case_shape() {
19837        // Shape-pin: every [`Caixa`] top-level multi-word key const must
19838        // be a lowerCamelCase byte-sequence (no `snake_case`
19839        // underscores, no `kebab-case` hyphens, no leading colon, no
19840        // `PascalCase` leading capital, no whitespace / dots) — the
19841        // canonical shape the `#[serde(rename_all = "camelCase")]`
19842        // derive produces on [`Caixa`]. A future flip to a
19843        // non-camelCase attribute at the derive surfaces both here
19844        // (this test fails on the stale-constant shape) and at
19845        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19846        // (that test fails on the mismatch between const and derive).
19847        // Peer with `membro_key_consts_are_lower_camel_case_shape`
19848        // (ce80ca0) and `supervisor_key_consts_are_lower_camel_case_shape`
19849        // (40cc4e5) on the sibling per-entry / supervisor-tree axes.
19850        for key in [
19851            crate::render::CAIXA_KEY_DEPS_DEV,
19852            crate::render::M2_KEY_UPGRADE_FROM,
19853            crate::render::SUPERVISOR_KEY_MAX_RESTARTS,
19854            crate::render::SUPERVISOR_KEY_RESTART_WINDOW,
19855        ] {
19856            assert!(
19857                !key.is_empty(),
19858                "Caixa top-level multi-word key const must be non-empty \
19859                 (got {key:?})"
19860            );
19861            let first = key.chars().next().unwrap();
19862            assert!(
19863                first.is_ascii_lowercase(),
19864                "Caixa top-level multi-word key const must lead with an \
19865                 ASCII-lowercase byte (got {key:?}, leads with {first:?})",
19866            );
19867            assert!(
19868                key.chars().all(|c| c.is_ascii_alphanumeric()),
19869                "Caixa top-level multi-word key const must be \
19870                 ASCII-alphanumeric only — no `_` / `-` / `:` / `.` / \
19871                 whitespace (got {key:?})",
19872            );
19873        }
19874    }
19875
19876    #[test]
19877    fn caixa_key_deps_dev_pins_canonical_camel_case_byte_string() {
19878        // Scalar-value pin: the byte-string the
19879        // [`crate::render::CAIXA_KEY_DEPS_DEV`] const resolves to,
19880        // asserted verbatim. A future rebrand (`depsDev` → `devDeps`
19881        // matching Cargo's verbatim `dev-dependencies` axis, `depsDev`
19882        // → `depsTest` matching a hypothetical per-test-target
19883        // vocabulary flip) lands as an edit to exactly one const AND
19884        // one derive attribute — the sibling
19885        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19886        // pin already ties the const to the derive attribute, so a
19887        // rebrand that touches only one side of the pair fails at
19888        // caixa-core build time. Same "scalar-value pin per const"
19889        // discipline the sibling
19890        // `m2_top_level_author_key_consts_pin_canonical_kebab_case_labels`
19891        // (f49c8b0) and `contrato_key_consts_pin_canonical_camel_case_labels`
19892        // (ca463a4) pins carry on the peer M2 / M3 top-level slot axes.
19893        assert_eq!(crate::render::CAIXA_KEY_DEPS_DEV, "depsDev");
19894    }
19895
19896    #[test]
19897    fn caixa_key_deps_pins_canonical_byte_string() {
19898        // Scalar-value pin: the byte-string the
19899        // [`crate::render::CAIXA_KEY_DEPS`] const resolves to, asserted
19900        // verbatim. Peer of `caixa_key_deps_dev_pins_canonical_camel_case_byte_string`
19901        // on the two-list dep-graph serialized-key axis — the sibling
19902        // pin covers the multi-word `deps_dev → depsDev` camelCase
19903        // arm, this pin covers the single-word `deps → deps` no-op arm
19904        // (the [`crate::Caixa::deps`] field name carries no `_`, so the
19905        // `#[serde(rename_all = "camelCase")]` derive is a no-op on this
19906        // axis and the emitted JSON key equals the source-side field
19907        // name byte-for-byte). A future [`crate::Caixa::deps`] field
19908        // rename (`deps` → `dependencies` matching Cargo's verbatim
19909        // `[dependencies]` axis, `deps` → `runtime_deps` matching a
19910        // hypothetical per-runtime-target vocabulary flip) OR an added
19911        // `#[serde(rename = "…")]` explicit override lands as an edit
19912        // to exactly one const AND one derive-attr / field name — the
19913        // sibling `caixa_deps_serde_key_matches_lifted_caixa_key_deps`
19914        // pin ties the const to the emitted JSON key, so a rebrand
19915        // that touches only one side of the pair fails at caixa-core
19916        // build time.
19917        assert_eq!(crate::render::CAIXA_KEY_DEPS, "deps");
19918    }
19919
19920    #[test]
19921    fn caixa_deps_serde_key_matches_lifted_caixa_key_deps() {
19922        // Load-bearing invariant on the single-word `deps` top-level
19923        // axis: the byte-string [`crate::render::CAIXA_KEY_DEPS`] pins
19924        // must appear verbatim in the JSON [`Caixa::to_lisp`]'s
19925        // `serde_json::to_value(self)` step emits. Serialize a
19926        // populated [`Caixa`] whose `:deps` slot carries at least one
19927        // entry (the `#[serde(default)]` attribute on the field emits
19928        // an empty `[]` even without members, but a non-empty vec
19929        // additionally covers the codec's per-`Dep`-entry emission
19930        // path) and pin that `"deps"` appears verbatim in the JSON
19931        // emission — a future accidental `rename_all = "snake_case"` /
19932        // `"kebab-case"` flip at the derive attribute (or an added
19933        // `#[serde(rename = "…")]` explicit override on the field, or
19934        // a Rust field rename) would break every [`Caixa::to_lisp`]
19935        // round-trip and the future M4 operator-side manifest ingest's
19936        // `Value::get(CAIXA_KEY_DEPS)` navigation — surfaces here as a
19937        // build-time test failure at `manifest.rs`, not as an
19938        // apply-time `.get(<stale-canonical-const>)` returning `None`
19939        // far from the drift's commit. Peer of the sibling
19940        // `caixa_multi_word_serde_keys_match_lifted_top_level_key_consts`
19941        // multi-word pin on the same M0 [`Caixa`] top-level
19942        // serialized-key axis, extended here to the single-word arm
19943        // the multi-word test's `rename_all = "camelCase"` sweep can't
19944        // reach (single-word `deps → deps` is a no-op the multi-word
19945        // pin's `\"depsDev\"` / `\"upgradeFrom\"` / `\"maxRestarts\"` /
19946        // `\"restartWindow\"` byte-scan can never observe).
19947        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
19948        c.deps = vec![Dep::simple("caixa-core", "^0.1")];
19949        let json = serde_json::to_string(&c).unwrap();
19950        let quoted = format!("\"{}\"", crate::render::CAIXA_KEY_DEPS);
19951        assert!(
19952            json.contains(&quoted),
19953            "serialized Caixa must carry the lifted top-level `deps` \
19954             byte-sequence {quoted} verbatim in the JSON emission (got: \
19955             {json})",
19956        );
19957    }
19958
19959    #[test]
19960    fn caixa_dep_graph_two_list_key_consts_are_pairwise_distinct() {
19961        // Cross-axis drift-detection pin on the two-list dep-graph
19962        // renderer-side wire-key axis: a future collapse of the
19963        // canonical [`crate::render::CAIXA_KEY_DEPS`] /
19964        // [`crate::render::CAIXA_KEY_DEPS_DEV`] byte-strings onto the
19965        // same value (e.g. an accidental copy-paste flip of
19966        // `CAIXA_KEY_DEPS_DEV` to also read `"deps"`) would silently
19967        // reroute every downstream `Value::get(<key>)` probe on one
19968        // axis onto the sibling axis's dep-list and pass every
19969        // propagation-probe test that expected only the stale axis's
19970        // value — a dev-only dep would land in the runtime closure at
19971        // publish time, or a runtime dep would be excluded from the
19972        // published lacre. Peer of the sibling four-way distinct pin
19973        // on the top-level multi-word tetrad
19974        // (`caixa_top_level_multi_word_key_consts_are_pairwise_distinct`)
19975        // and the two-way pin on the sibling
19976        // [`DEP_AUTHOR_KEY_DEPS`] / [`DEP_AUTHOR_KEY_DEPS_DEV`]
19977        // author-facing arm (4da6fba's test), extended here to the
19978        // renderer-side wire-key arm of the same two-list dep-graph
19979        // axis so both halves of the "one canonical byte-string per
19980        // typed axis per (author, wire)" grid carry the same
19981        // distinct-ness discipline.
19982        assert_ne!(
19983            crate::render::CAIXA_KEY_DEPS,
19984            crate::render::CAIXA_KEY_DEPS_DEV,
19985            "CAIXA_KEY_DEPS and CAIXA_KEY_DEPS_DEV must be distinct \
19986             canonical byte-sequences on the two-list dep-graph \
19987             renderer-side wire-key axis"
19988        );
19989    }
19990
19991    // ── DepList / Caixa::push_dep pin ────────────────────────────────
19992    //
19993    // The compounding pin: the two-arm closed-set typed enum
19994    // [`crate::dep::DepList`] carries the runtime-closure `:deps`
19995    // (`Prod`) vs dev-only-closure `:deps-dev` (`Dev`) dispatch every
19996    // consumer of the top-level manifest's dep-mutation surface reads
19997    // through, and the typed dispatch [`Caixa::push_dep`] on the
19998    // substrate primitive folds the "select list → check within-list
19999    // dup → push" cascade onto one method call. Prior to this landing
20000    // the two axes lived across two `&'static str` constants
20001    // (`DEP_AUTHOR_KEY_DEPS`, `DEP_AUTHOR_KEY_DEPS_DEV`) with no closed-
20002    // set type carrying the pair; the `feira add` mutation site's
20003    // inline `if self.dev { &mut caixa.deps_dev } else { &mut
20004    // caixa.deps }` dispatch expressed no compile-time link back to
20005    // the substrate primitive, and a future third dep-list axis would
20006    // have silently split at every open-coded mutation site.
20007
20008    #[test]
20009    fn dep_list_as_str_routes_through_lifted_author_key_constants() {
20010        // Every arm returns the same `&'static str` the substrate's
20011        // canonical `DEP_AUTHOR_KEY_DEPS` / `DEP_AUTHOR_KEY_DEPS_DEV`
20012        // constants carry. A future rebrand on either constant reaches
20013        // the enum through one edit; a regression to inline literals
20014        // (e.g. `Prod => ":deps"`) would silently split the diagnostic
20015        // quotes from the wire-format constants every consumer routes
20016        // through and this pin flags it at build time.
20017        assert_eq!(
20018            crate::dep::DepList::Prod.as_str(),
20019            crate::render::DEP_AUTHOR_KEY_DEPS
20020        );
20021        assert_eq!(
20022            crate::dep::DepList::Dev.as_str(),
20023            crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20024        );
20025    }
20026
20027    #[test]
20028    fn dep_list_display_routes_through_as_str() {
20029        // Same as-str-through-Display convergence discipline the
20030        // sibling closed-set typed enums carry — a `format!("{list}")`
20031        // call must land byte-for-byte on the accessor's return so a
20032        // future consumer that formats the enum for a diagnostic line
20033        // reaches the same wire-format constant the wire-format
20034        // producers do.
20035        assert_eq!(
20036            format!("{}", crate::dep::DepList::Prod),
20037            crate::dep::DepList::Prod.as_str()
20038        );
20039        assert_eq!(
20040            format!("{}", crate::dep::DepList::Dev),
20041            crate::dep::DepList::Dev.as_str()
20042        );
20043    }
20044
20045    #[test]
20046    fn dep_list_all_enumerates_every_variant_once() {
20047        // Exhaustive-iteration pin — every arm appears exactly once in
20048        // `ALL`, matching the closed set the compiler enforces on the
20049        // sibling `match self` arms. A future variant addition that
20050        // extends only one method's match without extending `ALL`
20051        // would silently drop the new arm from every consumer that
20052        // iterates the slice.
20053        let variants: &[crate::dep::DepList] = crate::dep::DepList::ALL;
20054        assert!(variants.contains(&crate::dep::DepList::Prod));
20055        assert!(variants.contains(&crate::dep::DepList::Dev));
20056        assert_eq!(variants.len(), 2);
20057    }
20058
20059    #[test]
20060    fn dep_list_from_wire_returns_prod_on_deps_wire_scalar() {
20061        // Reverse projection on the two-list dep-graph axis: the
20062        // author-surface wire tag the sibling `as_str` emitter walks
20063        // for `Prod` (`:deps` via `DEP_AUTHOR_KEY_DEPS`) parses back to
20064        // `Some(DepList::Prod)`. A regression that hand-rolled the
20065        // per-arm match without routing through the lifted
20066        // `DEP_AUTHOR_KEY_DEPS` const would silently disagree on any
20067        // future wire-tag rebrand and this pin flags it at build time.
20068        assert_eq!(
20069            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS),
20070            Some(crate::dep::DepList::Prod)
20071        );
20072    }
20073
20074    #[test]
20075    fn dep_list_from_wire_returns_dev_on_deps_dev_wire_scalar() {
20076        // Peer of the `Prod`-arm pin on the dev-only axis: the
20077        // author-surface wire tag the sibling `as_str` emitter walks
20078        // for `Dev` (`:deps-dev` via `DEP_AUTHOR_KEY_DEPS_DEV`) parses
20079        // back to `Some(DepList::Dev)`. Same drift-detection posture
20080        // as the peer arm — the sibling method `match` arms are
20081        // compiler-checked exhaustive so a future variant addition
20082        // trips at build time.
20083        assert_eq!(
20084            crate::dep::DepList::from_wire(crate::render::DEP_AUTHOR_KEY_DEPS_DEV),
20085            Some(crate::dep::DepList::Dev)
20086        );
20087    }
20088
20089    #[test]
20090    fn dep_list_from_wire_returns_none_on_unknown_wire_scalar() {
20091        // Every input outside the closed-set arm-string set the
20092        // sibling `as_str` emitter walks lands on the terminal `None`
20093        // fallback — no silent-accept surface. Sweeps a set of
20094        // plausibly-adjacent scalars (unprefixed wire form, PascalCase
20095        // rebrand candidates, foreign wire tags, empty string) so a
20096        // future variant addition that widened one wire form without
20097        // extending the emitter's arm-set would trip the sibling
20098        // round-trip pin below rather than silently accepting the new
20099        // form here.
20100        for candidate in [
20101            "",
20102            "deps",
20103            "deps-dev",
20104            ":deps ",
20105            ":Deps",
20106            ":DEPS",
20107            ":build-dep",
20108            ":tool-dep",
20109            "prod",
20110            "dev",
20111        ] {
20112            assert_eq!(
20113                crate::dep::DepList::from_wire(candidate),
20114                None,
20115                "from_wire({candidate:?}) must return None; every input outside \
20116                 the {{DEP_AUTHOR_KEY_DEPS, DEP_AUTHOR_KEY_DEPS_DEV}} accept-set \
20117                 the sibling as_str emitter walks lands on the terminal fallback",
20118            );
20119        }
20120    }
20121
20122    #[test]
20123    fn dep_list_round_trips_through_as_str_and_from_wire() {
20124        // Load-bearing round-trip pin: every arm the `ALL` iteration
20125        // exposes survives the `as_str` → `from_wire` composition
20126        // byte-for-byte. Same discipline the sibling closed-set enums
20127        // carry — `CaixaKind` /
20128        // `RestartStrategy` / `RestartPolicy` /
20129        // `PlacementStrategy` — extended onto the two-list dep-graph
20130        // axis. A future variant addition that extends `ALL` +
20131        // `as_str` without extending `from_wire` (or vice versa)
20132        // trips at build time on this iteration because the compiler
20133        // enforces exhaustiveness on the sibling `match self` arms.
20134        for &list in crate::dep::DepList::ALL {
20135            assert_eq!(
20136                crate::dep::DepList::from_wire(list.as_str()),
20137                Some(list),
20138                "DepList::from_wire(as_str({list:?})) must round-trip to Some({list:?}) — \
20139                 a silent split between the forward emitter and the reverse parser \
20140                 would drift the two halves of the two-list dep-graph axis's typed dispatch",
20141            );
20142        }
20143    }
20144
20145    #[test]
20146    fn push_dep_routes_to_deps_slot_on_prod_arm() {
20147        // The `Prod` arm dispatches to the runtime-closure `:deps`
20148        // slot every downstream lacre-pipeline consumer resolves at
20149        // build time. A future arm that regressed to inline `&mut
20150        // self.deps_dev` on the `Prod` path would silently reroute
20151        // every runtime dep into the dev-only closure at publish time
20152        // — this pin refuses that regression.
20153        let src = Caixa::template("host");
20154        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20155        let before_deps = caixa.deps().len();
20156        let before_deps_dev = caixa.deps_dev().len();
20157        let dep = Dep {
20158            nome: "caixa-teia".to_string(),
20159            versao: "^0.1".to_string(),
20160            fonte: None,
20161            opcional: false,
20162            caracteristicas: Vec::new(),
20163        };
20164        caixa
20165            .push_dep(crate::dep::DepList::Prod, dep)
20166            .expect("first push into :deps succeeds");
20167        assert_eq!(caixa.deps().len(), before_deps + 1);
20168        assert_eq!(caixa.deps_dev().len(), before_deps_dev);
20169        assert_eq!(caixa.deps().last().unwrap().nome(), "caixa-teia");
20170    }
20171
20172    #[test]
20173    fn push_dep_routes_to_deps_dev_slot_on_dev_arm() {
20174        // Peer of the sibling `Prod`-arm dispatch pin — the `Dev` arm
20175        // must dispatch to the dev-only-closure `:deps-dev` slot every
20176        // downstream test-facing artifact resolver reads. A future
20177        // regression that inverted the two arms would silently route
20178        // every dev-only dep into the runtime closure at publish time
20179        // and this pin catches it before the drift ships.
20180        let src = Caixa::template("host");
20181        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20182        let dep = Dep {
20183            nome: "tatara-check".to_string(),
20184            versao: "*".to_string(),
20185            fonte: None,
20186            opcional: false,
20187            caracteristicas: Vec::new(),
20188        };
20189        caixa
20190            .push_dep(crate::dep::DepList::Dev, dep)
20191            .expect("first push into :deps-dev succeeds");
20192        assert!(caixa.deps().is_empty());
20193        assert_eq!(caixa.deps_dev().len(), 1);
20194        assert_eq!(caixa.deps_dev().last().unwrap().nome(), "tatara-check");
20195    }
20196
20197    #[test]
20198    fn push_dep_refuses_within_list_duplicate_nome_with_typed_error() {
20199        // Within-list dup check routes through the canonical
20200        // [`DepError::DuplicateNome`] carrier — the substrate's typed
20201        // diagnostic for the same axis [`Caixa::validate_deps`]'s
20202        // parse-time [`crate::render::insert_first_seen`] walk raises
20203        // on. Prior to the lift the mutation site's inline
20204        // `bail!("dep '{}' already declared", …)` string-diagnostic
20205        // path expressed no through-line back to the typed error;
20206        // routing every dep-list refusal through one carrier means an
20207        // author reading a `feira add` refusal and a `feira build`
20208        // refusal reaches for the same corrective surface without
20209        // switching diagnostic idioms.
20210        let src = Caixa::template("host");
20211        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20212        let dep = Dep {
20213            nome: "caixa-teia".to_string(),
20214            versao: "^0.1".to_string(),
20215            fonte: None,
20216            opcional: false,
20217            caracteristicas: Vec::new(),
20218        };
20219        caixa
20220            .push_dep(crate::dep::DepList::Prod, dep.clone())
20221            .expect("first push succeeds");
20222        let dup = Dep {
20223            nome: "caixa-teia".to_string(),
20224            versao: "^0.2".to_string(),
20225            fonte: None,
20226            opcional: false,
20227            caracteristicas: Vec::new(),
20228        };
20229        let err = caixa
20230            .push_dep(crate::dep::DepList::Prod, dup)
20231            .expect_err("second push with same :nome refuses");
20232        assert_eq!(
20233            err,
20234            DepError::DuplicateNome {
20235                nome: "caixa-teia".to_string(),
20236                list: crate::render::DEP_AUTHOR_KEY_DEPS,
20237            }
20238        );
20239        // The refused mutation must not corrupt the target list —
20240        // exactly one entry lives past the refusal, matching the
20241        // canonical single-source-of-truth invariant `Caixa::deps()`
20242        // carries.
20243        assert_eq!(caixa.deps().len(), 1);
20244    }
20245
20246    #[test]
20247    fn push_dep_refuses_dup_on_dev_list_arm_names_deps_dev_key() {
20248        // Peer of the sibling `Prod`-arm dup-refusal pin — the `Dev`
20249        // arm's refusal must carry `DEP_AUTHOR_KEY_DEPS_DEV` in the
20250        // `list` payload so a future author reading the refusal grep's
20251        // for the correct `:deps-dev` block in their `caixa.lisp`,
20252        // not the sibling `:deps` block the runtime closure resolves.
20253        let src = Caixa::template("host");
20254        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20255        let dep = Dep {
20256            nome: "tatara-check".to_string(),
20257            versao: "*".to_string(),
20258            fonte: None,
20259            opcional: false,
20260            caracteristicas: Vec::new(),
20261        };
20262        caixa
20263            .push_dep(crate::dep::DepList::Dev, dep.clone())
20264            .expect("first push succeeds");
20265        let err = caixa
20266            .push_dep(crate::dep::DepList::Dev, dep)
20267            .expect_err("second push with same :nome refuses");
20268        assert!(matches!(
20269            err,
20270            DepError::DuplicateNome {
20271                ref nome,
20272                list,
20273            } if nome == "tatara-check"
20274                && list == crate::render::DEP_AUTHOR_KEY_DEPS_DEV
20275        ));
20276    }
20277
20278    #[test]
20279    fn push_dep_allows_same_nome_across_prod_and_dev_lists() {
20280        // The within-list dup check is scoped to the target arm — a
20281        // caixa may legitimately carry the same `:nome` under both
20282        // `:deps` and `:deps-dev` (though the substrate's peer
20283        // [`crate::Caixa::validate_deps`] walk still refuses the
20284        // shape at parse time; the mutation-site refusal is scoped to
20285        // the mutation-site's list to match the peer parse-time
20286        // per-list [`crate::render::insert_first_seen`] discipline).
20287        // The two arms hold independent seen-sets.
20288        let src = Caixa::template("host");
20289        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20290        let dep_prod = Dep {
20291            nome: "shared".to_string(),
20292            versao: "^0.1".to_string(),
20293            fonte: None,
20294            opcional: false,
20295            caracteristicas: Vec::new(),
20296        };
20297        let dep_dev = Dep {
20298            nome: "shared".to_string(),
20299            versao: "*".to_string(),
20300            fonte: None,
20301            opcional: false,
20302            caracteristicas: Vec::new(),
20303        };
20304        caixa
20305            .push_dep(crate::dep::DepList::Prod, dep_prod)
20306            .expect("push into :deps succeeds");
20307        caixa
20308            .push_dep(crate::dep::DepList::Dev, dep_dev)
20309            .expect("push same :nome into :deps-dev succeeds");
20310        assert_eq!(caixa.deps().len(), 1);
20311        assert_eq!(caixa.deps_dev().len(), 1);
20312    }
20313
20314    #[test]
20315    fn deps_of_prod_returns_the_deps_slot_verbatim() {
20316        // The `Prod` arm of the typed-dispatch [`Caixa::deps_of`] read
20317        // accessor must project onto the runtime-closure `:deps` slot —
20318        // element-equal and length-equal to the sibling per-slot
20319        // [`Caixa::deps`] accessor's return over every per-caixa fixture.
20320        // A future arm that regressed to `self.deps_dev()` on the `Prod`
20321        // path would silently reroute every downstream typed-dispatch
20322        // walker (the [`Caixa::validate_deps`] per-list
20323        // [`crate::render::insert_first_seen`] dedup walk, any future
20324        // per-axis-parametrised consumer) into the sibling dev-only
20325        // closure and this pin refuses that regression.
20326        let src = Caixa::template("host");
20327        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20328        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20329        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 0);
20330        let dep = Dep {
20331            nome: "caixa-teia".to_string(),
20332            versao: "^0.1".to_string(),
20333            fonte: None,
20334            opcional: false,
20335            caracteristicas: Vec::new(),
20336        };
20337        caixa
20338            .push_dep(crate::dep::DepList::Prod, dep.clone())
20339            .expect("push into :deps succeeds");
20340        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod), caixa.deps());
20341        assert_eq!(caixa.deps_of(crate::dep::DepList::Prod).len(), 1);
20342        assert_eq!(
20343            caixa.deps_of(crate::dep::DepList::Prod)[0].nome(),
20344            "caixa-teia"
20345        );
20346    }
20347
20348    #[test]
20349    fn deps_of_dev_returns_the_deps_dev_slot_verbatim() {
20350        // Peer of the sibling `Prod`-arm pin — the `Dev` arm of
20351        // [`Caixa::deps_of`] must project onto the dev-only-closure
20352        // `:deps-dev` slot, element-equal and length-equal to the
20353        // sibling per-slot [`Caixa::deps_dev`] accessor's return. A
20354        // future regression that inverted the two arms would silently
20355        // route every dev-list walker onto the runtime closure and this
20356        // pin catches it before the drift ships.
20357        let src = Caixa::template("host");
20358        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20359        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20360        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 0);
20361        let dep = Dep {
20362            nome: "tatara-check".to_string(),
20363            versao: "*".to_string(),
20364            fonte: None,
20365            opcional: false,
20366            caracteristicas: Vec::new(),
20367        };
20368        caixa
20369            .push_dep(crate::dep::DepList::Dev, dep)
20370            .expect("push into :deps-dev succeeds");
20371        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev), caixa.deps_dev());
20372        assert_eq!(caixa.deps_of(crate::dep::DepList::Dev).len(), 1);
20373        assert_eq!(
20374            caixa.deps_of(crate::dep::DepList::Dev)[0].nome(),
20375            "tatara-check"
20376        );
20377    }
20378
20379    #[test]
20380    fn deps_of_exhaustive_over_dep_list_all_covers_the_two_slots() {
20381        // Composition pin: iterating [`crate::dep::DepList::ALL`] through
20382        // [`Caixa::deps_of`] must land on the same two-slot partition the
20383        // per-slot [`Caixa::deps`] / [`Caixa::deps_dev`] accessors
20384        // expose — the canonical dispatch a future per-axis-parametrised
20385        // walker (a future `feira app graph` per-list dep summary, a
20386        // future M4 per-cluster dev-closure-audit overlay the CR
20387        // materializer resolves per-CR) reads through. Prior to the
20388        // lift the two-block iteration lived open-coded at every walker,
20389        // so a future third dep-list axis (`:deps-build`, per CAIXA-SDLC
20390        // §I) would have had to grow a third block at every consumer.
20391        // A regression that dropped the `Dev` arm from `ALL` would flip
20392        // the collected pairs to `[(":deps", &[])]` alone and this pin
20393        // refuses that shape.
20394        let src = Caixa::template("host");
20395        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20396        let prod_dep = Dep {
20397            nome: "caixa-teia".to_string(),
20398            versao: "^0.1".to_string(),
20399            fonte: None,
20400            opcional: false,
20401            caracteristicas: Vec::new(),
20402        };
20403        let dev_dep = Dep {
20404            nome: "tatara-check".to_string(),
20405            versao: "*".to_string(),
20406            fonte: None,
20407            opcional: false,
20408            caracteristicas: Vec::new(),
20409        };
20410        caixa
20411            .push_dep(crate::dep::DepList::Prod, prod_dep)
20412            .expect("push into :deps succeeds");
20413        caixa
20414            .push_dep(crate::dep::DepList::Dev, dev_dep)
20415            .expect("push into :deps-dev succeeds");
20416        let collected: Vec<(&'static str, usize, &str)> = crate::dep::DepList::ALL
20417            .iter()
20418            .map(|&list| {
20419                let slice = caixa.deps_of(list);
20420                (list.as_str(), slice.len(), slice[0].nome())
20421            })
20422            .collect();
20423        assert_eq!(
20424            collected,
20425            vec![
20426                (crate::render::DEP_AUTHOR_KEY_DEPS, 1, "caixa-teia"),
20427                (crate::render::DEP_AUTHOR_KEY_DEPS_DEV, 1, "tatara-check"),
20428            ]
20429        );
20430    }
20431
20432    #[test]
20433    fn caixa_deps_of_is_const_fn() {
20434        // Fail-before-pass-after pin on [`Caixa::deps_of`]'s
20435        // `const`-eval-surface posture. The typed-dispatch read
20436        // accessor forwards through the sibling `pub const fn`
20437        // [`Caixa::deps`] / [`Caixa::deps_dev`] per-slot slice
20438        // accessors on the two [`crate::dep::DepList`] enum arms —
20439        // every operator in the body is already `const`-callable
20440        // (`DepList` is a plain `#[derive(Copy)]` closed-set
20441        // discriminator so the `match` arms are const-evaluable, and
20442        // each arm dispatches through the sibling `pub const fn`
20443        // slice accessor). Any future accidental downgrade to
20444        // non-`const` fails the `deps_of_via_const_fn` wrapper below
20445        // at caixa-core build time with E0015 (`cannot call non-const
20446        // method`), strictly stronger than a runtime `assert!` and
20447        // side-stepping the destructor-in-const restriction the
20448        // `Caixa` fixture's owning `String` / `Vec<Dep>` carriers
20449        // rule out on the direct-`const _: () = assert!(...)`
20450        // residence.
20451        //
20452        // Peer of the sibling outer-`Caixa` accessor family pins
20453        // ([`caixa_outer_string_slice_return_accessor_family_is_const_fn`]
20454        // on the `&[String]` universal-axis surface,
20455        // [`caixa_outer_composite_slice_return_accessor_family_is_const_fn`]
20456        // on the outer `&[T]` composite-slice surface,
20457        // [`caixa_outer_option_composite_reference_return_accessor_family_is_const_fn`]
20458        // on the outer `Option<&Composite>` surface) — this pin
20459        // extends the `const`-eval-surface discipline onto the outer-
20460        // `Caixa` typed-dispatch read surface on the [`DepList`]-keyed
20461        // dep-list axis, closing the outer-`Caixa` accessor family's
20462        // last unlifted `pub fn` on the read side.
20463        const fn deps_of_via_const_fn(c: &Caixa, list: crate::dep::DepList) -> &[Dep] {
20464            c.deps_of(list)
20465        }
20466        let src = Caixa::template("host");
20467        let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20468        // Empty-list arm: both `Prod` and `Dev` degenerate to the
20469        // empty slice with no silent `None` collapse — the
20470        // `#[serde(default)]` `Vec::new()` fold every `defcaixa` form
20471        // that omits the slot lands on.
20472        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod).is_empty());
20473        assert!(deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev).is_empty());
20474        assert_eq!(
20475            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20476            caixa.deps()
20477        );
20478        assert_eq!(
20479            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20480            caixa.deps_dev()
20481        );
20482        // Populated arms: each list carries its own entry, and the
20483        // wrapper / direct dispatches agree byte-for-byte on the
20484        // slice-view under both non-empty arms.
20485        let prod_dep = Dep {
20486            nome: "caixa-teia".to_string(),
20487            versao: "^0.1".to_string(),
20488            fonte: None,
20489            opcional: false,
20490            caracteristicas: Vec::new(),
20491        };
20492        let dev_dep = Dep {
20493            nome: "tatara-check".to_string(),
20494            versao: "*".to_string(),
20495            fonte: None,
20496            opcional: false,
20497            caracteristicas: Vec::new(),
20498        };
20499        caixa
20500            .push_dep(crate::dep::DepList::Prod, prod_dep)
20501            .expect("push into :deps succeeds");
20502        caixa
20503            .push_dep(crate::dep::DepList::Dev, dev_dep)
20504            .expect("push into :deps-dev succeeds");
20505        assert_eq!(
20506            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod),
20507            caixa.deps()
20508        );
20509        assert_eq!(
20510            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev),
20511            caixa.deps_dev()
20512        );
20513        assert_eq!(
20514            deps_of_via_const_fn(&caixa, crate::dep::DepList::Prod)[0].nome(),
20515            "caixa-teia"
20516        );
20517        assert_eq!(
20518            deps_of_via_const_fn(&caixa, crate::dep::DepList::Dev)[0].nome(),
20519            "tatara-check"
20520        );
20521    }
20522
20523    #[test]
20524    fn validate_deps_iterates_through_dep_list_all_via_deps_of() {
20525        // Composition pin: the [`Caixa::validate_deps`] parse-time gate
20526        // must route its per-list [`crate::render::insert_first_seen`]
20527        // dedup walk through [`Caixa::deps_of`] + [`crate::dep::DepList::ALL`]
20528        // rather than the pre-lift open-coded two-block iteration over
20529        // `self.deps()` + `self.deps_dev()`. A regression that dropped
20530        // one arm (e.g. hand-inlining `self.deps()` alone) would silently
20531        // stop refusing within-list dups on the sibling arm; a
20532        // regression that flipped the arm-to-list-key mapping
20533        // (`Dev => DEP_AUTHOR_KEY_DEPS`) would silently mislabel the
20534        // diagnostic surface. Both drifts surface here through a paired
20535        // duplicate-name refusal per arm plus an offending-list-key
20536        // check on the emitted [`DepError::DuplicateNome`] carrier.
20537        for &list in crate::dep::DepList::ALL {
20538            let src = Caixa::template("host");
20539            let mut caixa = Caixa::from_lisp(&src).expect("template parses");
20540            let dup = Dep {
20541                nome: "twin".to_string(),
20542                versao: "^0.1".to_string(),
20543                fonte: None,
20544                opcional: false,
20545                caracteristicas: Vec::new(),
20546            };
20547            match list {
20548                crate::dep::DepList::Prod => {
20549                    caixa.deps.push(dup.clone());
20550                    caixa.deps.push(dup);
20551                }
20552                crate::dep::DepList::Dev => {
20553                    caixa.deps_dev.push(dup.clone());
20554                    caixa.deps_dev.push(dup);
20555                }
20556            }
20557            let err = caixa
20558                .validate_deps()
20559                .expect_err("within-list duplicate :nome must refuse");
20560            assert_eq!(
20561                err,
20562                DepError::DuplicateNome {
20563                    nome: "twin".to_string(),
20564                    list: list.as_str(),
20565                },
20566                "validate_deps on {list} arm must emit \
20567                 DepError::DuplicateNome carrying the arm's own \
20568                 as_str() diagnostic — the arm-to-list-key mapping \
20569                 flowed through DepList::ALL + Caixa::deps_of"
20570            );
20571        }
20572    }
20573
20574    #[test]
20575    fn caixa_licenca_default_pins_canonical_mit_byte() {
20576        // Bridge-arm pin: [`CAIXA_LICENCA_DEFAULT`] resolves to the
20577        // canonical SPDX-`"MIT"` byte today, the same license expression
20578        // every peer substrate-side consumer of the author-omitted
20579        // `:licenca` slot ([`caixa-helm`]'s `build_readme` fallback arm at
20580        // `caixa-helm/src/lib.rs`, the future M4
20581        // `mesh.pleme.io/v1alpha1/Aplicacao` CR materializer's per-CR
20582        // `Chart.yaml annotations["artifacthub.io/license"]` emitter this
20583        // crate's [`Caixa::validate_licenca`] docstring roadmap already
20584        // names as the second consumer) fills into its per-consumer
20585        // README/annotation emit site. Pin the literal here (peer with the
20586        // [`crate::version::DEFAULT_PUBLISH_TAG_PREFIX`] /
20587        // [`crate::version::DEFAULT_GIT_REMOTE`] /
20588        // [`crate::version::DEFAULT_PLEME_GIT_ORG`] canonical-literal pins
20589        // on the sibling lifted-constant surfaces) so a future
20590        // substrate-side license-fallback rebrand surfaces here as a
20591        // coordinated edit-point: the sibling caixa-helm
20592        // `build_readme_license_line_routes_through_lifted_caixa_licenca_default`
20593        // pinning test already pins the equality at the renderer-emit
20594        // axis; this pin closes the second coordinate of the pair by
20595        // anchoring the lifted constant's current byte to the canonical
20596        // CAIXA-SDLC §I license scaffold's documented shape.
20597        assert_eq!(CAIXA_LICENCA_DEFAULT, "MIT");
20598    }
20599
20600    // ── Caixa::validate_upgrade_from — compound per-Caixa entry gate on ──
20601    // ── the M2 `:upgrade-from` slot: folds the three top-level        ──
20602    // ── `crate::upgrade` validators (per-entry + cross-entry           ──
20603    // ── duplicate-`:from`, cross-slot `:from < :versao` precedence,   ──
20604    // ── cross-slot `:state-change` ↔ `:on-state-change` composition)  ──
20605    // ── onto one substrate primitive. Byte-for-byte equivalent to the ──
20606    // ── pre-fold three-block cascade at                               ──
20607    // ── `crate::layout::StandardLayout::verify` under the same        ──
20608    // ── canonical dispatch order.                                     ──
20609
20610    #[test]
20611    fn validate_upgrade_from_folds_per_entry_arm_matches_gate() {
20612        // Fail-before-pass-after per-arm equivalence pin on the
20613        // per-entry + cross-entry axis: a fixture whose `:upgrade-from`
20614        // carries a per-entry-invalid `:from` (git-tag shape `"v0.1.0"`,
20615        // which `semver::Version::parse` rejects) surfaces the same
20616        // [`crate::UpgradeError`] through the compound gate
20617        // [`Caixa::validate_upgrade_from`] and the standalone per-entry
20618        // gate [`crate::upgrade::validate_upgrade_from`] on the same
20619        // [`Caixa::upgrade_from`] slice. Pins the fold — a silent
20620        // regression that de-folded the per-entry arm would surface here
20621        // as a mismatch between the two dispatches. Sibling in shape to
20622        // the peer per-slot-≡-standalone equivalence pins the
20623        // [`crate::AplicacaoSpec::validate_contratos`] /
20624        // [`crate::MeshPolicy::validate`] /
20625        // [`crate::SupervisorSpec::validate_children`] compound gates
20626        // each carry on their axes.
20627        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20628        c.upgrade_from = vec![crate::UpgradeFromEntry {
20629            from: "v0.1.0".into(),
20630            instructions: vec![crate::UpgradeInstruction::Restart],
20631        }];
20632        let via_method = c.validate_upgrade_from().unwrap_err();
20633        let via_standalone = crate::upgrade::validate_upgrade_from(c.upgrade_from()).unwrap_err();
20634        assert_eq!(
20635            via_method, via_standalone,
20636            "Caixa::validate_upgrade_from must surface the per-entry \
20637             axis's diagnostic byte-equal to the standalone \
20638             `crate::upgrade::validate_upgrade_from` on the same \
20639             upgrade_from() slice"
20640        );
20641        assert!(
20642            matches!(
20643                via_method,
20644                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.1.0"
20645            ),
20646            "expected FromInvalid on the git-tag-shape `:from`, got {via_method:?}"
20647        );
20648    }
20649
20650    #[test]
20651    fn validate_upgrade_from_folds_versao_arm_matches_gate() {
20652        // Per-arm equivalence pin on the cross-slot `:from ↔ :versao`
20653        // precedence axis: a fixture with a well-formed `:from` (so the
20654        // per-entry arm passes) whose parsed semver is >= the caixa's
20655        // `:versao` under SemVer-2 precedence surfaces the same
20656        // [`crate::UpgradeError::FromNotBeforeVersao`] through both the
20657        // compound gate and the standalone
20658        // [`crate::upgrade::validate_upgrade_from_against_versao`] gate
20659        // keyed off the same `(upgrade_from, versao)` pair. Pins the
20660        // fold's second arm — reaching this arm through the compound
20661        // gate requires the per-entry arm to pass first, which itself
20662        // pins the per-arm cross-arm ordering.
20663        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20664        c.versao = "0.1.0".into();
20665        c.upgrade_from = vec![crate::UpgradeFromEntry {
20666            from: "0.2.0".into(),
20667            instructions: vec![crate::UpgradeInstruction::Restart],
20668        }];
20669        let via_method = c.validate_upgrade_from().unwrap_err();
20670        let via_standalone =
20671            crate::upgrade::validate_upgrade_from_against_versao(c.upgrade_from(), c.versao())
20672                .unwrap_err();
20673        assert_eq!(
20674            via_method, via_standalone,
20675            "Caixa::validate_upgrade_from must surface the \
20676             `:from >= :versao` diagnostic byte-equal to the standalone \
20677             `crate::upgrade::validate_upgrade_from_against_versao` on \
20678             the same (upgrade_from, versao) pair"
20679        );
20680        assert!(
20681            matches!(
20682                via_method,
20683                crate::UpgradeError::FromNotBeforeVersao { ref from, ref versao }
20684                    if from == "0.2.0" && versao == "0.1.0"
20685            ),
20686            "expected FromNotBeforeVersao carrying the offending pair, got {via_method:?}"
20687        );
20688    }
20689
20690    #[test]
20691    fn validate_upgrade_from_folds_behavior_arm_matches_gate() {
20692        // Per-arm equivalence pin on the cross-slot `:state-change ↔
20693        // :on-state-change` composition axis: a fixture with a
20694        // well-formed `:from` strictly less than `:versao` (so the
20695        // per-entry and versao arms both pass) whose `:instructions`
20696        // list carries a `(:state-change …)` instruction with no
20697        // `:behavior :on-state-change` callback declared surfaces the
20698        // same [`crate::UpgradeError::StateChangeWithoutOnStateChangeCallback`]
20699        // through both the compound gate and the standalone
20700        // [`crate::upgrade::validate_upgrade_from_against_behavior`]
20701        // gate keyed off the same `(upgrade_from, behavior)` pair.
20702        // Reaching this arm through the compound gate requires both
20703        // prior arms to pass first — the ordering pin below pins the
20704        // per-arm dispatch order explicitly.
20705        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20706        c.versao = "0.2.0".into();
20707        c.behavior = None;
20708        c.upgrade_from = vec![crate::UpgradeFromEntry {
20709            from: "0.1.0".into(),
20710            instructions: vec![
20711                crate::UpgradeInstruction::LoadModule {
20712                    module: "demo".into(),
20713                },
20714                crate::UpgradeInstruction::StateChange {
20715                    script: std::path::PathBuf::from("lib/m.lisp"),
20716                },
20717                crate::UpgradeInstruction::SoftPurge {
20718                    module: "demo-old".into(),
20719                },
20720            ],
20721        }];
20722        let via_method = c.validate_upgrade_from().unwrap_err();
20723        let via_standalone =
20724            crate::upgrade::validate_upgrade_from_against_behavior(c.upgrade_from(), c.behavior())
20725                .unwrap_err();
20726        assert_eq!(
20727            via_method, via_standalone,
20728            "Caixa::validate_upgrade_from must surface the \
20729             `:state-change` ↔ `:on-state-change` composition \
20730             diagnostic byte-equal to the standalone \
20731             `crate::upgrade::validate_upgrade_from_against_behavior` \
20732             on the same (upgrade_from, behavior) pair"
20733        );
20734        assert!(
20735            matches!(
20736                via_method,
20737                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback {
20738                    ref from,
20739                    ref script,
20740                } if from == "0.1.0" && script == &std::path::PathBuf::from("lib/m.lisp")
20741            ),
20742            "expected StateChangeWithoutOnStateChangeCallback carrying \
20743             the offending (from, script) pair, got {via_method:?}"
20744        );
20745    }
20746
20747    #[test]
20748    fn validate_upgrade_from_per_entry_arm_fires_before_versao_arm() {
20749        // Cross-arm ordering pin between the first two arms of the
20750        // fold: a fixture carrying BOTH a per-entry-invalid `:from`
20751        // (`"v0.0.5"` — git-tag shape rejected by
20752        // [`crate::upgrade::validate_upgrade_from`]) AND a would-be
20753        // versao-precedence violation on a second entry (`"0.2.0" >=
20754        // :versao "0.1.0"`) surfaces the per-entry diagnostic first
20755        // through the compound gate. Sanity assertion: the second
20756        // entry alone under the same `:versao` trips the versao arm
20757        // on its own via the standalone
20758        // [`crate::upgrade::validate_upgrade_from_against_versao`], so
20759        // the per-entry-first surfacing is a real ordering property,
20760        // not a case where the versao arm silently accepts the
20761        // fixture. Pins the pre-fold layout wire-up's canonical
20762        // dispatch order (per-entry → versao → behavior) as a
20763        // property of the substrate primitive rather than a
20764        // convention of the layout call site.
20765        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20766        c.versao = "0.1.0".into();
20767        c.upgrade_from = vec![
20768            crate::UpgradeFromEntry {
20769                from: "v0.0.5".into(),
20770                instructions: vec![crate::UpgradeInstruction::Restart],
20771            },
20772            crate::UpgradeFromEntry {
20773                from: "0.2.0".into(),
20774                instructions: vec![crate::UpgradeInstruction::Restart],
20775            },
20776        ];
20777        let err = c.validate_upgrade_from().unwrap_err();
20778        assert!(
20779            matches!(
20780                err,
20781                crate::UpgradeError::FromInvalid { ref from, .. } if from == "v0.0.5"
20782            ),
20783            "per-entry arm must fire before versao arm — expected \
20784             FromInvalid on `v0.0.5`, got {err:?}"
20785        );
20786        // Sanity: the versao-violating second entry alone under the
20787        // same `:versao` trips the versao arm on its own — proves the
20788        // per-entry-first surfacing above is a real ordering property.
20789        let sanity = crate::upgrade::validate_upgrade_from_against_versao(
20790            &[crate::UpgradeFromEntry {
20791                from: "0.2.0".into(),
20792                instructions: vec![crate::UpgradeInstruction::Restart],
20793            }],
20794            "0.1.0",
20795        )
20796        .unwrap_err();
20797        assert!(
20798            matches!(sanity, crate::UpgradeError::FromNotBeforeVersao { .. }),
20799            "sanity: the versao-violating fixture alone must trip the \
20800             versao arm — got {sanity:?}"
20801        );
20802    }
20803
20804    #[test]
20805    fn validate_upgrade_from_versao_arm_fires_before_behavior_arm() {
20806        // Cross-arm ordering pin between the second and third arms of
20807        // the fold: a fixture carrying BOTH a versao-precedence
20808        // violation (`:from "0.2.0" >= :versao "0.1.0"`) AND a
20809        // would-be missing-callback violation (a `(:state-change …)`
20810        // instruction with no `:behavior :on-state-change`) surfaces
20811        // the versao diagnostic first through the compound gate.
20812        // Sanity assertion: the missing-callback fixture alone (with
20813        // the versao-precedence violation removed by bumping
20814        // `:versao` past `:from`) trips the behavior arm on its own
20815        // via the standalone
20816        // [`crate::upgrade::validate_upgrade_from_against_behavior`],
20817        // so the versao-first surfacing is a real ordering property,
20818        // not a case where the behavior arm silently accepts the
20819        // fixture.
20820        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20821        c.versao = "0.1.0".into();
20822        c.behavior = None;
20823        c.upgrade_from = vec![crate::UpgradeFromEntry {
20824            from: "0.2.0".into(),
20825            instructions: vec![
20826                crate::UpgradeInstruction::LoadModule {
20827                    module: "demo".into(),
20828                },
20829                crate::UpgradeInstruction::StateChange {
20830                    script: std::path::PathBuf::from("lib/m.lisp"),
20831                },
20832            ],
20833        }];
20834        let err = c.validate_upgrade_from().unwrap_err();
20835        assert!(
20836            matches!(
20837                err,
20838                crate::UpgradeError::FromNotBeforeVersao { ref from, .. } if from == "0.2.0"
20839            ),
20840            "versao arm must fire before behavior arm — expected \
20841             FromNotBeforeVersao on `0.2.0`, got {err:?}"
20842        );
20843        // Sanity: the same instructions under a `:versao` that
20844        // accepts the `:from` (so the versao arm passes) trips the
20845        // behavior arm — proves the versao-first surfacing above is a
20846        // real ordering property.
20847        let sanity = crate::upgrade::validate_upgrade_from_against_behavior(
20848            &[crate::UpgradeFromEntry {
20849                from: "0.2.0".into(),
20850                instructions: vec![
20851                    crate::UpgradeInstruction::LoadModule {
20852                        module: "demo".into(),
20853                    },
20854                    crate::UpgradeInstruction::StateChange {
20855                        script: std::path::PathBuf::from("lib/m.lisp"),
20856                    },
20857                ],
20858            }],
20859            None,
20860        )
20861        .unwrap_err();
20862        assert!(
20863            matches!(
20864                sanity,
20865                crate::UpgradeError::StateChangeWithoutOnStateChangeCallback { .. }
20866            ),
20867            "sanity: the missing-callback fixture alone must trip the \
20868             behavior arm — got {sanity:?}"
20869        );
20870    }
20871
20872    #[test]
20873    fn validate_upgrade_from_accepts_clean_fixture() {
20874        // Positive control: a well-formed `:upgrade-from` (single entry
20875        // with `:from` strictly less than `:versao`, no
20876        // `:state-change` instruction so the behavior arm is vacuous)
20877        // passes the compound gate cleanly. A future tightening of any
20878        // one arm's accepted set surfaces here as a test failure
20879        // first. Mirrors the peer `validate_versao_accepts_canonical_forms`
20880        // positive-control posture on the sibling per-Caixa gate.
20881        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20882        c.versao = "0.2.0".into();
20883        c.upgrade_from = vec![crate::UpgradeFromEntry {
20884            from: "0.1.0".into(),
20885            instructions: vec![crate::UpgradeInstruction::Restart],
20886        }];
20887        c.validate_upgrade_from()
20888            .expect("clean fixture must pass the compound `:upgrade-from` gate");
20889    }
20890
20891    #[test]
20892    fn validate_upgrade_from_accepts_empty_upgrade_from() {
20893        // Positive control on the empty-list arm: a caixa without any
20894        // `:upgrade-from` block (the default `Vec::new()`
20895        // `#[serde(default)]` folds an omitted slot onto) passes the
20896        // compound gate cleanly regardless of `:versao` or `:behavior`
20897        // — each of the three standalone validators is vacuous on the
20898        // empty entry list. Pins the identity element of the fold on
20899        // the empty-slot side.
20900        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20901        assert!(
20902            c.upgrade_from().is_empty(),
20903            "template caixa must carry an empty :upgrade-from — got {:?}",
20904            c.upgrade_from()
20905        );
20906        c.validate_upgrade_from()
20907            .expect("empty :upgrade-from must pass the compound gate cleanly");
20908    }
20909
20910    // ── Caixa::validate_limits — compound per-Caixa entry gate on   ──
20911    // ── the M2 `:limits` slot: folds the                            ──
20912    // ── [`crate::LimitsSpec::validate`] four-axis cascade on the    ──
20913    // ── present-slot arm and the `Option::None` identity element on ──
20914    // ── the absent-slot arm onto one substrate primitive.           ──
20915    // ── Byte-for-byte equivalent to the pre-fold                    ──
20916    // ── `if let Some(l) = caixa.limits() { l.validate() }`          ──
20917    // ── unwrap-and-dispatch pattern at                              ──
20918    // ── `crate::layout::StandardLayout::verify` (`layout.rs`).      ──
20919
20920    #[test]
20921    fn validate_limits_folds_arm_matches_gate() {
20922        // Fail-before-pass-after per-arm equivalence pin on the
20923        // present-slot arm: a fixture whose `:limits` carries a
20924        // zero-floor-violating `:fuel` (`Some(0)`, which
20925        // [`crate::LimitsSpec::validate`] rejects through
20926        // [`crate::LimitsError::FuelZero`]) surfaces the same
20927        // [`crate::LimitsError`] byte-equal through both the compound
20928        // gate [`Caixa::validate_limits`] and the standalone
20929        // [`crate::LimitsSpec::validate`] gate on the same `LimitsSpec`
20930        // value. Pins the fold — a silent regression that de-folded
20931        // the present-slot arm would surface here as a mismatch
20932        // between the two dispatches. Sibling in shape to the peer
20933        // per-arm equivalence pins the
20934        // [`crate::AplicacaoSpec::validate_contratos`] /
20935        // [`crate::MeshPolicy::validate`] /
20936        // [`crate::SupervisorSpec::validate_children`] /
20937        // [`Caixa::validate_upgrade_from`] compound gates each carry
20938        // on their axes.
20939        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20940        let l = crate::LimitsSpec {
20941            memory: None,
20942            fuel: Some(0),
20943            wall_clock: None,
20944            cpu: None,
20945        };
20946        c.limits = Some(l);
20947        let via_method = c.validate_limits().unwrap_err();
20948        let via_standalone = l.validate().unwrap_err();
20949        assert_eq!(
20950            via_method, via_standalone,
20951            "Caixa::validate_limits must surface the present-slot \
20952             arm's diagnostic byte-equal to the standalone \
20953             `LimitsSpec::validate` on the same `LimitsSpec` value"
20954        );
20955        assert!(
20956            matches!(via_method, crate::LimitsError::FuelZero),
20957            "expected FuelZero on the zero-floor-violating `:fuel`, \
20958             got {via_method:?}"
20959        );
20960    }
20961
20962    #[test]
20963    fn validate_limits_accepts_none() {
20964        // Positive control on the absent-slot arm (the fold's identity
20965        // element): a caixa without any `:limits` block (the
20966        // canonical "no bound declared — engine-default applies"
20967        // author shape [`crate::LimitsSpec::is_empty`]'s per-axis
20968        // `None` cascade reads, and the shape the [`Caixa::template`]
20969        // scaffold emits by construction) passes the compound gate
20970        // cleanly, regardless of any per-axis defect a subsequent
20971        // `Some(_)` binding would surface. Pins the identity element
20972        // of the fold on the absent-slot side, matching the peer
20973        // `validate_upgrade_from_accepts_empty_upgrade_from` positive-
20974        // control posture on the sibling M2 slot.
20975        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
20976        assert!(
20977            c.limits().is_none(),
20978            "template caixa must carry an absent :limits — got {:?}",
20979            c.limits()
20980        );
20981        c.validate_limits()
20982            .expect("absent :limits must pass the compound gate cleanly");
20983    }
20984
20985    #[test]
20986    fn validate_limits_accepts_clean_fixture() {
20987        // Positive control on the present-slot arm: a caixa whose
20988        // `:limits` is `Some(LimitsSpec::default())` (all four axes
20989        // `None` — every axis absent under the outer `Some(_)`
20990        // binding, so every present-slot arm on
20991        // [`crate::LimitsSpec::validate`] is vacuous) passes the
20992        // compound gate cleanly. A future tightening of any one axis
20993        // that surfaces a diagnostic on the all-`None` `LimitsSpec`
20994        // would land here as a test failure first. Pins the
20995        // present-slot arm's accept-shape on the canonical
20996        // "declared-but-empty" author fixture the
20997        // `limits_round_trip_via_json` peer already round-trips
20998        // (`caixa-core/src/manifest.rs:6971`).
20999        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21000        c.limits = Some(crate::LimitsSpec::default());
21001        c.validate_limits()
21002            .expect("Some(LimitsSpec::default()) must pass the compound gate cleanly");
21003    }
21004
21005    // ── Caixa::validate_behavior — compound per-Caixa entry gate on ──
21006    // ── the M2 `:behavior` slot's pure value-shape surface: folds   ──
21007    // ── the [`crate::BehaviorSpec::validate`] six-slot cascade on   ──
21008    // ── the present-slot arm and the `Option::None` identity        ──
21009    // ── element on the absent-slot arm onto one substrate primitive.──
21010    // ── Byte-for-byte equivalent to the pre-fold                    ──
21011    // ── `if let Some(b) = caixa.behavior() { b.validate() }`        ──
21012    // ── unwrap-and-dispatch pattern at                              ──
21013    // ── `crate::layout::StandardLayout::verify` (`layout.rs`). The  ──
21014    // ── on-disk callback-path existence walk stays open-coded at    ──
21015    // ── the layout altitude because it needs the                    ──
21016    // ── [`crate::layout::LayoutInvariants::exists`] filesystem       ──
21017    // ── oracle the pure typed-shape surface has no reference to —   ──
21018    // ── mirror of the peer M2 `:upgrade-from` per-instruction       ──
21019    // ── script-path existence probe that stayed at the layout       ──
21020    // ── altitude after the [`Caixa::validate_upgrade_from`] lift    ──
21021    // ── (d6801df) for the same reason.                              ──
21022
21023    #[test]
21024    fn validate_behavior_folds_arm_matches_gate() {
21025        // Fail-before-pass-after per-arm equivalence pin on the
21026        // present-slot arm: a fixture whose `:behavior` carries an
21027        // absolute-path `:on-init` (`"/etc/passwd"`, which
21028        // [`crate::BehaviorSpec::validate`] rejects through
21029        // [`crate::BehaviorError::AbsolutePath`]) surfaces the same
21030        // [`crate::BehaviorError`] byte-equal through both the
21031        // compound gate [`Caixa::validate_behavior`] and the standalone
21032        // [`crate::BehaviorSpec::validate`] gate on the same
21033        // `BehaviorSpec` value. Pins the fold — a silent regression
21034        // that de-folded the present-slot arm would surface here as a
21035        // mismatch between the two dispatches. Sibling in shape to the
21036        // peer per-arm equivalence pins the
21037        // [`Caixa::validate_limits`] (baa4688),
21038        // [`Caixa::validate_upgrade_from`] (d6801df),
21039        // [`crate::MeshPolicy::validate`],
21040        // [`crate::AplicacaoSpec::validate_contratos`], and
21041        // [`crate::SupervisorSpec::validate_children`] compound gates
21042        // each carry on their axes.
21043        use crate::BehaviorSpec;
21044        use std::path::PathBuf;
21045        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21046        let b = BehaviorSpec {
21047            on_init: Some(PathBuf::from("/etc/passwd")),
21048            ..Default::default()
21049        };
21050        c.behavior = Some(b.clone());
21051        let via_method = c.validate_behavior().unwrap_err();
21052        let via_standalone = b.validate().unwrap_err();
21053        assert_eq!(
21054            via_method, via_standalone,
21055            "Caixa::validate_behavior must surface the present-slot \
21056             arm's diagnostic byte-equal to the standalone \
21057             `BehaviorSpec::validate` on the same `BehaviorSpec` value"
21058        );
21059        assert!(
21060            matches!(via_method, crate::BehaviorError::AbsolutePath { .. }),
21061            "expected AbsolutePath on the absolute `:on-init` path, \
21062             got {via_method:?}"
21063        );
21064    }
21065
21066    #[test]
21067    fn validate_behavior_accepts_none() {
21068        // Positive control on the absent-slot arm (the fold's identity
21069        // element): a caixa without any `:behavior` block (the
21070        // canonical "no callback declared — the runtime falls back to
21071        // the wasm-engine's default per arm" author shape
21072        // [`crate::BehaviorSpec::is_empty`]'s per-slot `None` cascade
21073        // reads, and the shape the [`Caixa::template`] scaffold emits
21074        // by construction) passes the compound gate cleanly,
21075        // regardless of any per-slot defect a subsequent `Some(_)`
21076        // binding would surface. Pins the identity element of the fold
21077        // on the absent-slot side, matching the peer
21078        // `validate_limits_accepts_none` (baa4688) and
21079        // `validate_upgrade_from_accepts_empty_upgrade_from` (d6801df)
21080        // positive-control postures on the sibling M2 slots.
21081        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21082        assert!(
21083            c.behavior().is_none(),
21084            "template caixa must carry an absent :behavior — got {:?}",
21085            c.behavior()
21086        );
21087        c.validate_behavior()
21088            .expect("absent :behavior must pass the compound gate cleanly");
21089    }
21090
21091    #[test]
21092    fn validate_behavior_accepts_clean_fixture() {
21093        // Positive control on the present-slot arm: a caixa whose
21094        // `:behavior` is `Some(BehaviorSpec::default())` (all six
21095        // slots `None` — every slot absent under the outer `Some(_)`
21096        // binding, so every present-slot arm on
21097        // [`crate::BehaviorSpec::validate`] is vacuous) passes the
21098        // compound gate cleanly. A future tightening of any one arm
21099        // that surfaces a diagnostic on the all-`None` `BehaviorSpec`
21100        // would land here as a test failure first. Pins the
21101        // present-slot arm's accept-shape on the canonical
21102        // "declared-but-empty" author fixture the sibling
21103        // `empty_behavior_round_trip` peer already round-trips
21104        // (`caixa-core/src/behavior.rs` tests). Mirror of the peer
21105        // `validate_limits_accepts_clean_fixture` (baa4688)
21106        // positive-control posture on the sibling M2 `:limits` slot.
21107        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21108        c.behavior = Some(crate::BehaviorSpec::default());
21109        c.validate_behavior()
21110            .expect("Some(BehaviorSpec::default()) must pass the compound gate cleanly");
21111    }
21112
21113    // ── Caixa::validate_deps — compound per-Caixa entry gate on the ──
21114    // ── dep-graph axis: folds the two standalone validators         ──
21115    // ── (per-entry + within-list duplicate walk that this method    ──
21116    // ── opened on, cross-slot self-edge via                         ──
21117    // ── `crate::dep::validate_no_self_dep`) onto one substrate      ──
21118    // ── primitive. Byte-for-byte equivalent to the pre-fold         ──
21119    // ── two-block cascade at                                        ──
21120    // ── `crate::layout::StandardLayout::verify` under the same      ──
21121    // ── canonical dispatch order (per-entry → self-edge).           ──
21122
21123    #[test]
21124    fn validate_deps_folds_per_entry_arm_matches_gate() {
21125        // Fail-before-pass-after per-arm equivalence pin on the
21126        // per-entry + within-list duplicate axis: a fixture whose
21127        // `:deps` carries a per-entry-invalid `:versao` (`"^bad"`,
21128        // which [`crate::parse_requirement`] rejects) surfaces the
21129        // same [`crate::DepError`] through the compound gate
21130        // [`Caixa::validate_deps`] and the standalone per-entry walk
21131        // ([`Dep::validate`]) on the offending entry. Pins the
21132        // fold — a silent regression that de-folded the per-entry arm
21133        // would surface here as a mismatch between the two
21134        // dispatches. Sibling in shape to the peer
21135        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21136        // per-arm equivalence pin (d6801df) on the M2
21137        // `:upgrade-from` compound gate's per-entry arm, extended
21138        // here onto the universal-axis `:deps` compound gate's
21139        // per-entry arm.
21140        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21141        c.deps = vec![Dep::simple("d", "^bad")];
21142        let via_method = c.validate_deps().unwrap_err();
21143        let via_standalone = c.deps()[0].validate().unwrap_err();
21144        assert_eq!(
21145            via_method, via_standalone,
21146            "Caixa::validate_deps must surface the per-entry arm's \
21147             diagnostic byte-equal to the standalone \
21148             `Dep::validate` on the same offending entry",
21149        );
21150        assert!(
21151            matches!(
21152                via_method,
21153                DepError::VersaoInvalid { ref nome, .. } if nome == "d"
21154            ),
21155            "expected VersaoInvalid on the malformed :versao, got {via_method:?}",
21156        );
21157    }
21158
21159    #[test]
21160    fn validate_deps_folds_self_edge_arm_matches_gate() {
21161        // Per-arm equivalence pin on the cross-slot self-edge axis:
21162        // a fixture whose `:deps` lists the caixa's own `:nome`
21163        // (a self-dep, which
21164        // [`crate::dep::validate_no_self_dep`] rejects as a
21165        // structurally-invalid one-node cycle in the lacre closure's
21166        // dep-graph) surfaces the same [`crate::DepError::DepIsSelf`]
21167        // through both the compound gate and the standalone
21168        // [`crate::dep::validate_no_self_dep`] gate keyed off the
21169        // same `(deps, deps_dev, nome)` triple. Pins the fold's
21170        // second arm — reaching this arm through the compound gate
21171        // requires the per-entry + within-list duplicate walk to
21172        // pass first, which itself pins one cross-arm ordering step.
21173        // Sibling in shape to the peer
21174        // `validate_upgrade_from_folds_versao_arm_matches_gate` /
21175        // `_folds_behavior_arm_matches_gate` cross-slot equivalence
21176        // pins (d6801df) on the M2 `:upgrade-from` compound gate's
21177        // cross-slot arms.
21178        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21179        c.deps = vec![Dep::simple("demo", "^0.1")];
21180        let via_method = c.validate_deps().unwrap_err();
21181        let via_standalone =
21182            crate::dep::validate_no_self_dep(c.deps(), c.deps_dev(), c.nome()).unwrap_err();
21183        assert_eq!(
21184            via_method, via_standalone,
21185            "Caixa::validate_deps must surface the cross-slot \
21186             self-edge diagnostic byte-equal to the standalone \
21187             `crate::dep::validate_no_self_dep` on the same \
21188             (deps, deps_dev, nome) triple",
21189        );
21190        assert!(
21191            matches!(
21192                via_method,
21193                DepError::DepIsSelf { ref nome, list }
21194                    if nome == "demo" && list == crate::render::DEP_AUTHOR_KEY_DEPS
21195            ),
21196            "expected DepIsSelf carrying (nome=\"demo\", list=\":deps\"), got {via_method:?}",
21197        );
21198    }
21199
21200    #[test]
21201    fn validate_deps_per_entry_arm_fires_before_self_edge_arm() {
21202        // Cross-arm ordering pin between the two arms of the fold:
21203        // a fixture carrying BOTH a per-entry-invalid `:versao`
21204        // (`"^bad"` — [`crate::parse_requirement`] rejects the
21205        // requirement grammar) on a non-self-dep entry AND a
21206        // would-be self-edge violation on a second entry (the
21207        // caixa's own `:nome` "demo") surfaces the per-entry
21208        // diagnostic first through the compound gate. Sanity
21209        // assertion: the second entry alone under the same parent
21210        // `:nome` trips the self-edge arm on its own via the
21211        // standalone [`crate::dep::validate_no_self_dep`], so the
21212        // per-entry-first surfacing is a real ordering property,
21213        // not a case where the self-edge arm silently accepts the
21214        // fixture. Pins the pre-fold layout wire-up's canonical
21215        // dispatch order (per-entry + within-list duplicate →
21216        // self-edge) as a property of the substrate primitive
21217        // rather than a convention of the layout call site. Sibling
21218        // in shape to
21219        // `validate_upgrade_from_per_entry_arm_fires_before_versao_arm`
21220        // (d6801df) on the M2 `:upgrade-from` compound gate's
21221        // per-arm ordering property.
21222        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21223        c.deps = vec![
21224            Dep::simple("orquestra", "^bad"),
21225            Dep::simple("demo", "^0.1"),
21226        ];
21227        let err = c.validate_deps().unwrap_err();
21228        assert!(
21229            matches!(
21230                err,
21231                DepError::VersaoInvalid { ref nome, .. } if nome == "orquestra"
21232            ),
21233            "per-entry arm must fire before self-edge arm — expected \
21234             VersaoInvalid on \"orquestra\", got {err:?}",
21235        );
21236        // Sanity: the self-referential entry alone under the same
21237        // parent `:nome` trips the self-edge arm on its own — proves
21238        // the per-entry-first surfacing above is a real ordering
21239        // property, not a case where the self-edge arm silently
21240        // accepts the fixture.
21241        let sanity = crate::dep::validate_no_self_dep(&[Dep::simple("demo", "^0.1")], &[], "demo")
21242            .unwrap_err();
21243        assert!(
21244            matches!(sanity, DepError::DepIsSelf { ref nome, .. } if nome == "demo"),
21245            "sanity: the self-referential entry alone must trip the \
21246             self-edge arm — got {sanity:?}",
21247        );
21248    }
21249
21250    #[test]
21251    fn validate_deps_accepts_clean_fixture() {
21252        // Positive control: a well-formed dep-graph (one `:deps`
21253        // entry naming a non-self DNS-1123 nome + Cargo-shaped
21254        // requirement, one `:deps-dev` entry on a distinct non-self
21255        // nome) passes the compound gate cleanly. A future
21256        // tightening of either arm's accepted set surfaces here as
21257        // a test failure first. Mirrors the peer
21258        // `validate_upgrade_from_accepts_clean_fixture` positive-
21259        // control posture on the sibling per-Caixa compound gate.
21260        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21261        c.deps = vec![Dep::simple("caixa-teia", "^0.1")];
21262        c.deps_dev = vec![Dep::simple("caixa-lint", "^0.2")];
21263        c.validate_deps()
21264            .expect("clean fixture must pass the compound `:deps` gate");
21265    }
21266
21267    #[test]
21268    fn validate_deps_accepts_empty_deps_lists() {
21269        // Positive control on the empty-list arm: a caixa without
21270        // any `:deps` or `:deps-dev` entries (the default
21271        // `Vec::new()` `#[serde(default)]` folds an omitted slot
21272        // onto) passes the compound gate cleanly regardless of
21273        // `:nome` — both the per-entry walk and the self-edge walk
21274        // are vacuous on the empty entry list. Pins the identity
21275        // element of the fold on the empty-slot side, peer with the
21276        // `validate_upgrade_from_accepts_empty_upgrade_from` empty-
21277        // arm positive control (d6801df) on the sibling
21278        // `:upgrade-from` compound gate.
21279        let c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21280        assert!(
21281            c.deps().is_empty(),
21282            "template caixa must carry an empty :deps — got {:?}",
21283            c.deps(),
21284        );
21285        assert!(
21286            c.deps_dev().is_empty(),
21287            "template caixa must carry an empty :deps-dev — got {:?}",
21288            c.deps_dev(),
21289        );
21290        c.validate_deps()
21291            .expect("empty :deps / :deps-dev must pass the compound gate cleanly");
21292    }
21293
21294    // ── Caixa::validate_aplicacao_shape — compound per-Caixa gate ────────
21295
21296    /// Build a minimal well-formed Aplicacao fixture on top of the
21297    /// canonical template. Every arm of the compound gate then patches
21298    /// exactly one axis away from clean so its per-arm diagnostic
21299    /// surfaces without collateral noise from a peer slot.
21300    fn aplicacao_fixture(nome: &str) -> Caixa {
21301        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
21302        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21303        c.kind = CaixaKind::Aplicacao;
21304        c.bibliotecas = vec![];
21305        c.membros = vec![
21306            Membro {
21307                caixa: "checkout".into(),
21308                versao: "^0.1".into(),
21309            },
21310            Membro {
21311                caixa: "cart".into(),
21312                versao: "^0.1".into(),
21313            },
21314        ];
21315        // `:placement` defaults to `Replicated` with an empty
21316        // `:clusters` list which
21317        // [`crate::AplicacaoSpec::validate_placement`] refuses; every
21318        // per-strategy variant needs at least one named cluster (per
21319        // MESH-COMPOSITION §II.1). Pin a single-cluster `SingleNode`
21320        // placement so the typed-shape cascade passes cleanly and the
21321        // per-arm fixtures below can each patch exactly one axis.
21322        c.placement = Some(Placement {
21323            estrategia: PlacementStrategy::SingleNode,
21324            clusters: vec!["rio".into()],
21325            shard_key: None,
21326            affinity: None,
21327        });
21328        c
21329    }
21330
21331    #[test]
21332    fn validate_aplicacao_shape_folds_view_arm_matches_gate() {
21333        // Fail-before-pass-after per-arm equivalence pin on the
21334        // typed-shape cascade arm: a fixture whose typed
21335        // [`crate::AplicacaoSpec`] view fails
21336        // [`crate::AplicacaoSpec::validate`] (here — empty `:membros`,
21337        // which [`crate::AplicacaoSpec::validate_membros`] rejects as
21338        // [`crate::AplicacaoError::NoMembros`] at the first per-slot
21339        // gate) surfaces the same [`crate::AplicacaoError`] diagnostic
21340        // through both the compound gate
21341        // [`Caixa::validate_aplicacao_shape`] and the standalone
21342        // [`crate::AplicacaoSpec::validate`] on the same folded view.
21343        // Pins the fold — a silent regression that de-folded the
21344        // typed-shape arm would surface here as a mismatch between the
21345        // two dispatches. Sibling in shape to the peer
21346        // `validate_deps_folds_per_entry_arm_matches_gate` (b5dd55e) /
21347        // `validate_upgrade_from_folds_per_entry_arm_matches_gate`
21348        // (d6801df) per-arm equivalence pins on the sibling per-slot
21349        // compound gates.
21350        let mut c = aplicacao_fixture("demo");
21351        c.membros = vec![];
21352        let via_method = c.validate_aplicacao_shape().unwrap_err();
21353        let via_standalone = c.aplicacao_view().unwrap().validate().unwrap_err();
21354        assert_eq!(
21355            via_method, via_standalone,
21356            "Caixa::validate_aplicacao_shape must surface the typed-\
21357             shape arm's diagnostic byte-equal to the standalone \
21358             `AplicacaoSpec::validate` on the same folded view",
21359        );
21360        assert!(
21361            matches!(via_method, crate::AplicacaoError::NoMembros),
21362            "expected NoMembros on the empty :membros, got {via_method:?}",
21363        );
21364    }
21365
21366    #[test]
21367    fn validate_aplicacao_shape_folds_self_membership_arm_matches_gate() {
21368        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21369        // fixture whose `:membros` names the Aplicacao's own `:nome`
21370        // (which [`crate::aplicacao::validate_no_self_membership`]
21371        // rejects as [`crate::AplicacaoError::MembroIsSelfAplicacao`],
21372        // a one-node lacre-closure recursion in the Aplicacao's
21373        // mesh-graph) surfaces the same
21374        // [`crate::AplicacaoError::MembroIsSelfAplicacao`] through both
21375        // the compound gate and the standalone
21376        // [`crate::aplicacao::validate_no_self_membership`] keyed off
21377        // the same `(membros, nome)` pair. Pins the fold's second arm
21378        // — reaching this arm through the compound gate requires the
21379        // typed-shape cascade to pass first, which itself pins one
21380        // cross-arm ordering step. Sibling in shape to the peer
21381        // `validate_deps_folds_self_edge_arm_matches_gate` (b5dd55e)
21382        // cross-slot equivalence pin on the sibling per-slot compound
21383        // gate.
21384        use crate::aplicacao::Membro;
21385        let mut c = aplicacao_fixture("demo");
21386        c.membros = vec![Membro {
21387            caixa: "demo".into(),
21388            versao: "^0.1".into(),
21389        }];
21390        let via_method = c.validate_aplicacao_shape().unwrap_err();
21391        let via_standalone =
21392            crate::aplicacao::validate_no_self_membership(c.membros(), c.nome()).unwrap_err();
21393        assert_eq!(
21394            via_method, via_standalone,
21395            "Caixa::validate_aplicacao_shape must surface the cross-\
21396             slot self-edge diagnostic byte-equal to the standalone \
21397             `aplicacao::validate_no_self_membership` on the same \
21398             (membros, nome) pair",
21399        );
21400        assert!(
21401            matches!(
21402                via_method,
21403                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa } if caixa == "demo"
21404            ),
21405            "expected MembroIsSelfAplicacao carrying (caixa=\"demo\"), \
21406             got {via_method:?}",
21407        );
21408    }
21409
21410    #[test]
21411    fn validate_aplicacao_shape_view_arm_fires_before_self_membership_arm() {
21412        // Cross-arm ordering pin between the two arms of the fold: a
21413        // fixture carrying BOTH a typed-shape violation (a `:contratos`
21414        // edge whose `:para` is not a declared member — rejected by
21415        // [`crate::AplicacaoSpec::validate_contratos`] as
21416        // [`crate::AplicacaoError::ContratoMemberMissing`]) AND a
21417        // would-be self-edge violation (a `:membros` entry naming the
21418        // caixa's own `:nome`) surfaces the typed-shape diagnostic
21419        // first through the compound gate. Sanity assertion: the
21420        // self-referential `:membros` entry alone under the same
21421        // parent `:nome` trips the self-edge arm on its own via the
21422        // standalone [`crate::aplicacao::validate_no_self_membership`],
21423        // so the typed-shape-first surfacing is a real ordering
21424        // property, not a case where the self-edge arm silently
21425        // accepts the fixture. Pins the pre-fold layout wire-up's
21426        // canonical dispatch order (typed-shape cascade → cross-slot
21427        // self-edge) as a property of the substrate primitive rather
21428        // than a convention of the layout call site. Sibling in shape
21429        // to `validate_deps_per_entry_arm_fires_before_self_edge_arm`
21430        // (b5dd55e) on the sibling per-slot compound gate's per-arm
21431        // ordering property.
21432        use crate::aplicacao::{Membro, WitContract};
21433        let mut c = aplicacao_fixture("demo");
21434        c.membros = vec![Membro {
21435            caixa: "demo".into(),
21436            versao: "^0.1".into(),
21437        }];
21438        c.contratos = vec![WitContract {
21439            de: "demo".into(),
21440            para: "orphan".into(),
21441            wit: "wasi:http/proxy".into(),
21442            endpoint: Some("/x".into()),
21443            subject: None,
21444            slot: None,
21445        }];
21446        let err = c.validate_aplicacao_shape().unwrap_err();
21447        assert!(
21448            matches!(
21449                err,
21450                crate::AplicacaoError::ContratoMemberMissing { ref caixa }
21451                    if caixa == "orphan"
21452            ),
21453            "typed-shape arm must fire before self-edge arm — expected \
21454             ContratoMemberMissing on \"orphan\", got {err:?}",
21455        );
21456        // Sanity: the self-referential `:membros` entry alone under
21457        // the same parent `:nome` trips the self-edge arm on its own
21458        // — proves the typed-shape-first surfacing above is a real
21459        // ordering property, not a case where the self-edge arm
21460        // silently accepts the fixture.
21461        let sanity = crate::aplicacao::validate_no_self_membership(
21462            &[Membro {
21463                caixa: "demo".into(),
21464                versao: "^0.1".into(),
21465            }],
21466            "demo",
21467        )
21468        .unwrap_err();
21469        assert!(
21470            matches!(
21471                sanity,
21472                crate::AplicacaoError::MembroIsSelfAplicacao { ref caixa }
21473                    if caixa == "demo"
21474            ),
21475            "sanity: the self-referential :membros entry alone must \
21476             trip the self-edge arm — got {sanity:?}",
21477        );
21478    }
21479
21480    #[test]
21481    fn validate_aplicacao_shape_accepts_non_aplicacao_kind() {
21482        // Positive control on the identity-element arm: every non-
21483        // Aplicacao kind passes the compound gate trivially — the
21484        // paired [`Caixa::aplicacao_view`] accessor returns `None`
21485        // off the Aplicacao arm (by construction, keyed on
21486        // `caixa.kind().is_aplicacao()`), so the fold short-circuits
21487        // to `Ok(())` without touching the mesh slots. Pins the
21488        // identity element on every non-Aplicacao kind — a future
21489        // refactor that made the mesh-slot cascade fire on the wrong
21490        // kind (say, on a `Servico` whose mesh slots happen to be
21491        // populated in a mis-authored manifest, which the peer
21492        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] kind-
21493        // coherence gate would refuse upstream anyway) surfaces here
21494        // as a test failure first. Peer with the
21495        // `validate_limits_accepts_none` / `validate_behavior_accepts_none`
21496        // identity-element pins on the sibling M2 `Option`-shaped
21497        // per-Caixa compound gates.
21498        for kind in [
21499            CaixaKind::Biblioteca,
21500            CaixaKind::Binario,
21501            CaixaKind::Servico,
21502            CaixaKind::Supervisor,
21503            CaixaKind::Acao,
21504        ] {
21505            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21506            c.kind = kind;
21507            assert!(
21508                c.aplicacao_view().is_none(),
21509                "aplicacao_view must return None off the Aplicacao arm \
21510                 for kind {kind:?}",
21511            );
21512            c.validate_aplicacao_shape().expect(
21513                "non-Aplicacao kinds must pass the compound gate as the fold's identity element",
21514            );
21515        }
21516    }
21517
21518    #[test]
21519    fn validate_aplicacao_shape_accepts_clean_fixture() {
21520        // Positive control: a well-formed Aplicacao (two DNS-1123
21521        // members with valid semver constraints, no `:contratos` /
21522        // `:entrada` / `:placement` / `:politicas` set — every
21523        // per-slot gate accepts the vacuous / omitted arm) passes the
21524        // compound gate cleanly. A future tightening of either arm's
21525        // accepted set surfaces here as a test failure first. Mirrors
21526        // the peer `validate_deps_accepts_clean_fixture` (b5dd55e) /
21527        // `validate_upgrade_from_accepts_clean_fixture` (d6801df)
21528        // positive-control postures on the sibling per-Caixa
21529        // compound gates.
21530        let c = aplicacao_fixture("demo");
21531        c.validate_aplicacao_shape()
21532            .expect("clean Aplicacao fixture must pass the compound gate");
21533    }
21534
21535    // ── Caixa::validate_supervisor_shape — compound per-Caixa gate ───────
21536
21537    /// Build a minimal well-formed Supervisor fixture on top of the
21538    /// canonical template. Every arm of the compound gate then patches
21539    /// exactly one axis away from clean so its per-arm diagnostic
21540    /// surfaces without collateral noise from a peer slot. Peer of
21541    /// [`aplicacao_fixture`] on the sibling per-Aplicacao compound
21542    /// gate's pin family.
21543    fn supervisor_fixture(nome: &str) -> Caixa {
21544        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
21545        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21546        c.kind = CaixaKind::Supervisor;
21547        // Supervisors don't run code — clear the biblioteca slot the
21548        // template seeds so the fold's per-arm diagnostics surface
21549        // without the peer `SupervisorOwnsCode` kind-coherence gate
21550        // firing upstream at the layout altitude.
21551        c.bibliotecas = vec![];
21552        // `:estrategia` defaults to `OneForOne` at the typed view level,
21553        // and `OneForOne` requires at least one `:children` entry — pin
21554        // a single-child `Permanent` worker so the typed-shape cascade
21555        // passes cleanly and the per-arm fixtures below can each patch
21556        // exactly one axis.
21557        c.estrategia = Some(RestartStrategy::OneForOne);
21558        c.children = vec![ChildSpec {
21559            caixa: "worker".into(),
21560            versao: "^0.1".into(),
21561            restart: RestartPolicy::Permanent,
21562        }];
21563        c
21564    }
21565
21566    #[test]
21567    fn validate_supervisor_shape_folds_view_arm_matches_gate() {
21568        // Fail-before-pass-after per-arm equivalence pin on the
21569        // typed-shape cascade arm: a fixture whose typed
21570        // [`crate::SupervisorSpec`] view fails
21571        // [`crate::SupervisorSpec::validate`] (here — a duplicate
21572        // `:children` `:caixa` entry, which
21573        // [`crate::SupervisorSpec::validate`]'s set-not-multiset gate
21574        // rejects as [`crate::SupervisorError::DuplicateChildCaixa`])
21575        // surfaces the same [`crate::SupervisorError`] diagnostic
21576        // through both the compound gate
21577        // [`Caixa::validate_supervisor_shape`] and the standalone
21578        // [`crate::SupervisorSpec::validate`] on the same folded view.
21579        // Pins the fold — a silent regression that de-folded the
21580        // typed-shape arm would surface here as a mismatch between the
21581        // two dispatches. Sibling in shape to the peer
21582        // `validate_aplicacao_shape_folds_view_arm_matches_gate`
21583        // (949a7a0) on the sibling per-Aplicacao compound gate.
21584        use crate::supervisor::{ChildSpec, RestartPolicy};
21585        let mut c = supervisor_fixture("demo");
21586        c.children = vec![
21587            ChildSpec {
21588                caixa: "worker".into(),
21589                versao: "^0.1".into(),
21590                restart: RestartPolicy::Permanent,
21591            },
21592            ChildSpec {
21593                caixa: "worker".into(),
21594                versao: "^0.1".into(),
21595                restart: RestartPolicy::Permanent,
21596            },
21597        ];
21598        let via_method = c.validate_supervisor_shape().unwrap_err();
21599        let via_standalone = c.supervisor_view().unwrap().validate().unwrap_err();
21600        assert_eq!(
21601            via_method, via_standalone,
21602            "Caixa::validate_supervisor_shape must surface the typed-\
21603             shape arm's diagnostic byte-equal to the standalone \
21604             `SupervisorSpec::validate` on the same folded view",
21605        );
21606        assert!(
21607            matches!(
21608                via_method,
21609                crate::SupervisorError::DuplicateChildCaixa { ref caixa }
21610                    if caixa == "worker"
21611            ),
21612            "expected DuplicateChildCaixa on the duplicate 'worker' \
21613             child, got {via_method:?}",
21614        );
21615    }
21616
21617    #[test]
21618    fn validate_supervisor_shape_folds_self_supervision_arm_matches_gate() {
21619        // Per-arm equivalence pin on the cross-slot self-edge axis: a
21620        // fixture whose `:children :caixa` names the Supervisor's own
21621        // `:nome` (which
21622        // [`crate::supervisor::validate_no_self_supervision`] rejects
21623        // as [`crate::SupervisorError::ChildSupervisesSelf`], a
21624        // one-node reconciliation cycle in the supervisor's
21625        // supervision-tree) surfaces the same
21626        // [`crate::SupervisorError::ChildSupervisesSelf`] through both
21627        // the compound gate and the standalone
21628        // [`crate::supervisor::validate_no_self_supervision`] keyed
21629        // off the same `(children, nome)` pair. Pins the fold's
21630        // second arm — reaching this arm through the compound gate
21631        // requires the typed-shape cascade to pass first, which itself
21632        // pins one cross-arm ordering step. Sibling in shape to the
21633        // peer
21634        // `validate_aplicacao_shape_folds_self_membership_arm_matches_gate`
21635        // (949a7a0) cross-slot equivalence pin on the sibling
21636        // per-Aplicacao compound gate.
21637        use crate::supervisor::{ChildSpec, RestartPolicy};
21638        let mut c = supervisor_fixture("demo");
21639        c.children = vec![ChildSpec {
21640            caixa: "demo".into(),
21641            versao: "^0.1".into(),
21642            restart: RestartPolicy::Permanent,
21643        }];
21644        let via_method = c.validate_supervisor_shape().unwrap_err();
21645        let via_standalone =
21646            crate::supervisor::validate_no_self_supervision(c.children(), c.nome()).unwrap_err();
21647        assert_eq!(
21648            via_method, via_standalone,
21649            "Caixa::validate_supervisor_shape must surface the cross-\
21650             slot self-edge diagnostic byte-equal to the standalone \
21651             `supervisor::validate_no_self_supervision` on the same \
21652             (children, nome) pair",
21653        );
21654        assert!(
21655            matches!(
21656                via_method,
21657                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21658            ),
21659            "expected ChildSupervisesSelf carrying (caixa=\"demo\"), \
21660             got {via_method:?}",
21661        );
21662    }
21663
21664    #[test]
21665    fn validate_supervisor_shape_view_arm_fires_before_self_supervision_arm() {
21666        // Cross-arm ordering pin between the two arms of the fold: a
21667        // fixture carrying BOTH a typed-shape violation (a per-child
21668        // empty `:caixa` name — rejected by
21669        // [`crate::SupervisorSpec::validate`] as
21670        // [`crate::SupervisorError::EmptyChildName`]) AND a would-be
21671        // self-edge violation (a `:children` entry naming the
21672        // supervisor's own `:nome`) surfaces the typed-shape
21673        // diagnostic first through the compound gate. Sanity
21674        // assertion: the self-referential `:children` entry alone
21675        // under the same parent `:nome` trips the self-edge arm on
21676        // its own via the standalone
21677        // [`crate::supervisor::validate_no_self_supervision`], so the
21678        // typed-shape-first surfacing is a real ordering property, not
21679        // a case where the self-edge arm silently accepts the fixture.
21680        // Pins the pre-fold layout wire-up's canonical dispatch order
21681        // (typed-shape cascade → cross-slot self-edge) as a property
21682        // of the substrate primitive rather than a convention of the
21683        // layout call site. Sibling in shape to
21684        // `validate_aplicacao_shape_view_arm_fires_before_self_membership_arm`
21685        // (949a7a0) on the sibling per-Aplicacao compound gate.
21686        use crate::supervisor::{ChildSpec, RestartPolicy};
21687        let mut c = supervisor_fixture("demo");
21688        c.children = vec![
21689            ChildSpec {
21690                caixa: String::new(),
21691                versao: "^0.1".into(),
21692                restart: RestartPolicy::Permanent,
21693            },
21694            ChildSpec {
21695                caixa: "demo".into(),
21696                versao: "^0.1".into(),
21697                restart: RestartPolicy::Permanent,
21698            },
21699        ];
21700        let err = c.validate_supervisor_shape().unwrap_err();
21701        assert!(
21702            matches!(err, crate::SupervisorError::EmptyChildName),
21703            "typed-shape arm must fire before self-edge arm — expected \
21704             EmptyChildName on the empty :caixa child, got {err:?}",
21705        );
21706        // Sanity: the self-referential `:children` entry alone under
21707        // the same parent `:nome` trips the self-edge arm on its own
21708        // — proves the typed-shape-first surfacing above is a real
21709        // ordering property, not a case where the self-edge arm
21710        // silently accepts the fixture.
21711        let sanity = crate::supervisor::validate_no_self_supervision(
21712            &[ChildSpec {
21713                caixa: "demo".into(),
21714                versao: "^0.1".into(),
21715                restart: RestartPolicy::Permanent,
21716            }],
21717            "demo",
21718        )
21719        .unwrap_err();
21720        assert!(
21721            matches!(
21722                sanity,
21723                crate::SupervisorError::ChildSupervisesSelf { ref caixa } if caixa == "demo"
21724            ),
21725            "sanity: the self-referential :children entry alone must \
21726             trip the self-edge arm — got {sanity:?}",
21727        );
21728    }
21729
21730    #[test]
21731    fn validate_supervisor_shape_accepts_non_supervisor_kind() {
21732        // Positive control on the identity-element arm: every non-
21733        // Supervisor kind passes the compound gate trivially — the
21734        // paired [`Caixa::supervisor_view`] accessor returns `None`
21735        // off the Supervisor arm (by construction, keyed on
21736        // `caixa.kind().is_supervisor()`), so the fold short-circuits
21737        // to `Ok(())` without touching the supervision-tree slots.
21738        // Pins the identity element on every non-Supervisor kind — a
21739        // future refactor that made the supervision-tree cascade fire
21740        // on the wrong kind (say, on a `Servico` whose supervision
21741        // slots happen to be populated in a mis-authored manifest,
21742        // which the peer
21743        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
21744        // kind-coherence gate would refuse upstream anyway) surfaces
21745        // here as a test failure first. Peer with the
21746        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
21747        // (949a7a0) / `validate_limits_accepts_none` /
21748        // `validate_behavior_accepts_none` identity-element pins on
21749        // the sibling per-Caixa compound gates.
21750        for kind in [
21751            CaixaKind::Biblioteca,
21752            CaixaKind::Binario,
21753            CaixaKind::Servico,
21754            CaixaKind::Aplicacao,
21755            CaixaKind::Acao,
21756        ] {
21757            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
21758            c.kind = kind;
21759            assert!(
21760                c.supervisor_view().is_none(),
21761                "supervisor_view must return None off the Supervisor \
21762                 arm for kind {kind:?}",
21763            );
21764            c.validate_supervisor_shape().expect(
21765                "non-Supervisor kinds must pass the compound gate as the fold's identity element",
21766            );
21767        }
21768    }
21769
21770    #[test]
21771    fn validate_supervisor_shape_accepts_clean_fixture() {
21772        // Positive control: a well-formed Supervisor (single
21773        // DNS-1123-valid `Permanent` worker child under the
21774        // `OneForOne` strategy — the OTP MaxIntensity/Period defaults
21775        // accept the vacuous `:max-restarts` / `:restart-window`
21776        // arms) passes the compound gate cleanly. A future tightening
21777        // of either arm's accepted set surfaces here as a test
21778        // failure first. Mirrors the peer
21779        // `validate_aplicacao_shape_accepts_clean_fixture` (949a7a0)
21780        // positive-control posture on the sibling per-Caixa compound
21781        // gate.
21782        let c = supervisor_fixture("demo");
21783        c.validate_supervisor_shape()
21784            .expect("clean Supervisor fixture must pass the compound gate");
21785    }
21786
21787    // ── Caixa::validate_acao_shape — compound per-Caixa gate ─────────────
21788
21789    /// Build a minimal well-formed `:kind Acao` fixture with a valid
21790    /// two-node acyclic `:ci` slot. Every arm of the compound gate
21791    /// then patches exactly one axis away from clean so its per-arm
21792    /// diagnostic surfaces without collateral noise from a peer slot.
21793    /// Peer of [`supervisor_fixture`] / [`aplicacao_fixture`] on the
21794    /// sibling per-kind compound gates' pin families.
21795    fn acao_fixture(nome: &str) -> Caixa {
21796        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
21797        c.kind = CaixaKind::Acao;
21798        // Acaos don't run code — clear the biblioteca slot the template
21799        // seeds so the compound gate's per-arm diagnostics surface
21800        // without the peer `AcaoOwnsCode` kind-coherence gate firing
21801        // upstream at the layout altitude.
21802        c.bibliotecas = vec![];
21803        c.ci = Some(canteiro_types::CiRun {
21804            workspace: "pleme-io".into(),
21805            repo: "caixa".into(),
21806            nodes: vec![
21807                canteiro_types::CiNode::new(
21808                    "build",
21809                    canteiro_types::EnvClass::None,
21810                    canteiro_types::ActionRef {
21811                        name: "build".into(),
21812                        command: "true".into(),
21813                        args: vec![],
21814                    },
21815                    vec![],
21816                ),
21817                canteiro_types::CiNode::new(
21818                    "test",
21819                    canteiro_types::EnvClass::None,
21820                    canteiro_types::ActionRef {
21821                        name: "test".into(),
21822                        command: "true".into(),
21823                        args: vec![],
21824                    },
21825                    vec!["build".into()],
21826                ),
21827            ],
21828        });
21829        c
21830    }
21831
21832    #[test]
21833    fn validate_acao_shape_folds_decompose_arm_matches_gate() {
21834        // Fail-before-pass-after per-arm equivalence pin on the
21835        // decompose axis: a fixture whose `:ci` slot fails
21836        // [`canteiro_types::decompose`] (here — a minimal two-node
21837        // cycle `a → b → a`, which the sibling
21838        // [`crate::render::decompose_ci`] wraps as
21839        // [`crate::CiDecomposeFailure`] carrying
21840        // [`canteiro_types::DecomposeError::Cycle`]) surfaces the same
21841        // [`crate::CiDecomposeFailure`] diagnostic through both the
21842        // compound gate [`Caixa::validate_acao_shape`] and the
21843        // standalone [`crate::render::decompose_ci`] on the same
21844        // `(caixa, ci)` fixture. Pins the fold — a silent regression
21845        // that de-folded the decompose arm would surface here as a
21846        // mismatch between the two dispatches. Sibling in shape to the
21847        // peer `validate_supervisor_shape_folds_view_arm_matches_gate`
21848        // / `validate_aplicacao_shape_folds_view_arm_matches_gate` on
21849        // the sibling per-kind compound gates.
21850        //
21851        // [`crate::CiDecomposeFailure`] does not derive `PartialEq`
21852        // (its `#[source]` carrier [`canteiro_types::DecomposeError`]
21853        // does, but the wrapper deliberately does not), so the two
21854        // dispatches are compared through their field pair
21855        // (`nome` + `source`) rather than through `assert_eq!` on the
21856        // wrapper itself — every field on the wrapper is thereby
21857        // pinned byte-equal without depending on an implementation
21858        // detail of `CiDecomposeFailure`'s derive set.
21859        let mut c = acao_fixture("demo");
21860        c.ci = Some(canteiro_types::CiRun {
21861            workspace: "pleme-io".into(),
21862            repo: "caixa".into(),
21863            nodes: vec![
21864                canteiro_types::CiNode::new(
21865                    "a",
21866                    canteiro_types::EnvClass::None,
21867                    canteiro_types::ActionRef {
21868                        name: "a".into(),
21869                        command: "true".into(),
21870                        args: vec![],
21871                    },
21872                    vec!["b".into()],
21873                ),
21874                canteiro_types::CiNode::new(
21875                    "b",
21876                    canteiro_types::EnvClass::None,
21877                    canteiro_types::ActionRef {
21878                        name: "b".into(),
21879                        command: "true".into(),
21880                        args: vec![],
21881                    },
21882                    vec!["a".into()],
21883                ),
21884            ],
21885        });
21886        let via_method = c.validate_acao_shape().unwrap_err();
21887        let via_standalone =
21888            crate::render::decompose_ci(&c, c.ci().expect("fixture has a :ci")).unwrap_err();
21889        assert_eq!(
21890            via_method.nome, via_standalone.nome,
21891            "Caixa::validate_acao_shape must surface the decompose \
21892             failure's `nome` byte-equal to the standalone \
21893             `decompose_ci` on the same (caixa, ci) fixture",
21894        );
21895        assert_eq!(
21896            via_method.source, via_standalone.source,
21897            "Caixa::validate_acao_shape must surface the decompose \
21898             failure's `source` byte-equal to the standalone \
21899             `decompose_ci` on the same (caixa, ci) fixture",
21900        );
21901        assert_eq!(
21902            via_method.source,
21903            canteiro_types::DecomposeError::Cycle,
21904            "expected the two-node cycle `a → b → a` to surface as \
21905             DecomposeError::Cycle, got {source:?}",
21906            source = via_method.source,
21907        );
21908    }
21909
21910    #[test]
21911    fn validate_acao_shape_folds_duplicate_node_arm_matches_gate() {
21912        // Per-arm equivalence pin on the `DuplicateNode` decompose
21913        // arm — the sibling of `Cycle` on the substrate's
21914        // `canteiro_types::DecomposeError` enumeration. A fixture
21915        // whose `:ci` slot carries two nodes sharing one name
21916        // surfaces the same [`crate::CiDecomposeFailure`] through
21917        // both dispatches, pinned by field pair. The three
21918        // decompose arms (`DuplicateNode` / `UnknownDep` / `Cycle`)
21919        // together enumerate every failure mode
21920        // [`canteiro_types::decompose`] refuses, so the per-arm
21921        // pins collectively cover the whole decompose axis.
21922        let mut c = acao_fixture("demo");
21923        c.ci = Some(canteiro_types::CiRun {
21924            workspace: "pleme-io".into(),
21925            repo: "caixa".into(),
21926            nodes: vec![
21927                canteiro_types::CiNode::new(
21928                    "twin",
21929                    canteiro_types::EnvClass::None,
21930                    canteiro_types::ActionRef {
21931                        name: "twin".into(),
21932                        command: "true".into(),
21933                        args: vec![],
21934                    },
21935                    vec![],
21936                ),
21937                canteiro_types::CiNode::new(
21938                    "twin",
21939                    canteiro_types::EnvClass::None,
21940                    canteiro_types::ActionRef {
21941                        name: "twin".into(),
21942                        command: "true".into(),
21943                        args: vec![],
21944                    },
21945                    vec![],
21946                ),
21947            ],
21948        });
21949        let via_method = c.validate_acao_shape().unwrap_err();
21950        assert_eq!(
21951            via_method.source,
21952            canteiro_types::DecomposeError::DuplicateNode("twin".into()),
21953            "expected DuplicateNode on the two-\"twin\"-name fixture, \
21954             got {source:?}",
21955            source = via_method.source,
21956        );
21957    }
21958
21959    #[test]
21960    fn validate_acao_shape_folds_unknown_dep_arm_matches_gate() {
21961        // Per-arm equivalence pin on the `UnknownDep` decompose arm —
21962        // the third and last arm on `canteiro_types::DecomposeError`
21963        // after `Cycle` and `DuplicateNode`. A fixture whose `:ci`
21964        // slot names a `deps` entry no declared node satisfies
21965        // surfaces the same [`crate::CiDecomposeFailure`] through
21966        // both dispatches. Pins the third decompose arm at the
21967        // compound gate.
21968        let mut c = acao_fixture("demo");
21969        c.ci = Some(canteiro_types::CiRun {
21970            workspace: "pleme-io".into(),
21971            repo: "caixa".into(),
21972            nodes: vec![canteiro_types::CiNode::new(
21973                "orphan",
21974                canteiro_types::EnvClass::None,
21975                canteiro_types::ActionRef {
21976                    name: "orphan".into(),
21977                    command: "true".into(),
21978                    args: vec![],
21979                },
21980                vec!["ghost".into()],
21981            )],
21982        });
21983        let via_method = c.validate_acao_shape().unwrap_err();
21984        assert_eq!(
21985            via_method.source,
21986            canteiro_types::DecomposeError::UnknownDep {
21987                node: "orphan".into(),
21988                dep: "ghost".into(),
21989            },
21990            "expected UnknownDep on the orphan-node-depends-on-ghost \
21991             fixture, got {source:?}",
21992            source = via_method.source,
21993        );
21994    }
21995
21996    #[test]
21997    fn validate_acao_shape_accepts_non_acao_kind() {
21998        // Positive control on the identity-element arm: every non-
21999        // Acao kind passes the compound gate trivially — the paired
22000        // `caixa.kind().is_acao()` guard short-circuits before the
22001        // decompose gate ever fires, so the fold returns `Ok(())`
22002        // without touching the `:ci` slot even when a non-Acao
22003        // fixture happens to declare one (the sibling
22004        // [`crate::LayoutError::CiOnNonAcao`] kind-coherence gate
22005        // catches that at the layout altitude anyway). Pins the
22006        // identity element on every non-Acao kind. Peer with the
22007        // `validate_supervisor_shape_accepts_non_supervisor_kind` /
22008        // `validate_aplicacao_shape_accepts_non_aplicacao_kind`
22009        // identity-element pins on the sibling per-Caixa compound
22010        // gates.
22011        for kind in [
22012            CaixaKind::Biblioteca,
22013            CaixaKind::Binario,
22014            CaixaKind::Servico,
22015            CaixaKind::Supervisor,
22016            CaixaKind::Aplicacao,
22017        ] {
22018            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22019            c.kind = kind;
22020            c.validate_acao_shape().expect(
22021                "non-Acao kinds must pass the compound gate as the fold's identity element",
22022            );
22023        }
22024    }
22025
22026    #[test]
22027    fn validate_acao_shape_accepts_absent_ci_slot() {
22028        // Positive control on the second identity-element arm: a
22029        // `:kind Acao` caixa with `ci = None` passes the compound
22030        // gate trivially — the presence gate is the sibling axis
22031        // owned by [`crate::LayoutError::MissingCi`] /
22032        // [`crate::require_ci`] / [`crate::MissingCiSlot`], not by
22033        // the decompose gate. A caixa that carries no `:ci` slot
22034        // has no run to decompose, so the fold's `let Some(ci) = …
22035        // else { return Ok(()) }` arm short-circuits before the
22036        // decompose gate fires. Pins that the two axes stay
22037        // separately diagnosable at the layout altitude — a future
22038        // regression that collapsed the presence gate onto the
22039        // shape gate here would land a
22040        // [`crate::CiDecomposeFailure`] on the wrong axis and
22041        // surface an off-target diagnostic at `feira build` time.
22042        let mut c = acao_fixture("demo");
22043        c.ci = None;
22044        c.validate_acao_shape().expect(
22045            "an :kind Acao caixa with absent :ci must pass the compound gate — \
22046             the presence gate is layout's MissingCi axis, not the decompose gate",
22047        );
22048    }
22049
22050    #[test]
22051    fn validate_acao_shape_accepts_clean_fixture() {
22052        // Positive control: a well-formed Acao (a two-node acyclic
22053        // `:ci` run with `test` depending on `build`) passes the
22054        // compound gate cleanly. A future tightening of the
22055        // decompose gate's accepted set surfaces here as a test
22056        // failure first. Mirrors the peer
22057        // `validate_supervisor_shape_accepts_clean_fixture` /
22058        // `validate_aplicacao_shape_accepts_clean_fixture`
22059        // positive-control posture on the sibling per-Caixa
22060        // compound gates.
22061        let c = acao_fixture("demo");
22062        c.validate_acao_shape()
22063            .expect("clean Acao fixture must pass the compound gate");
22064    }
22065
22066    fn bare_servico_fixture(nome: &str) -> Caixa {
22067        // A minimal Servico caixa with no code and no typed slots —
22068        // the cross-family fold's identity element on every arm.
22069        // Clears the biblioteca slot the template seeds so the
22070        // per-arm patches below can each add exactly one typed slot
22071        // without a peer `ServicoOwnsCode` / layout-side kind-gate
22072        // firing upstream.
22073        let mut c = Caixa::from_lisp(&Caixa::template(nome)).unwrap();
22074        c.kind = CaixaKind::Servico;
22075        c.bibliotecas = vec![];
22076        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22077        c
22078    }
22079
22080    #[test]
22081    fn validate_kind_slot_coherence_folds_mesh_arm_matches_gate() {
22082        // Fail-before-pass-after per-arm equivalence pin on the M3
22083        // mesh-slot arm of the cross-family kind-coherence fold: a
22084        // non-Aplicacao caixa carrying a declared M3 mesh slot (here
22085        // a `:kind Servico` fixture with a single `:membros` entry —
22086        // the smallest possible M3 slot declaration on a foreign
22087        // kind) surfaces the same
22088        // [`crate::LayoutError::MeshSlotsOnNonAplicacao`] variant
22089        // through both the compound gate
22090        // [`Caixa::validate_kind_slot_coherence`] and the standalone
22091        // constructor [`crate::LayoutError::mesh_slots_on_non_aplicacao`]
22092        // dispatched on the same `declared_mesh_slots` list. Pins
22093        // the fold — a silent regression that de-folded the mesh
22094        // arm would surface here as a mismatch between the two
22095        // dispatches. Sibling in shape to the peer
22096        // `validate_aplicacao_shape_folds_view_arm_matches_gate` /
22097        // `validate_supervisor_shape_folds_view_arm_matches_gate` /
22098        // `validate_acao_shape_folds_decompose_arm_matches_gate`
22099        // per-arm equivalence pins on the sibling per-kind compound
22100        // gates.
22101        use crate::aplicacao::Membro;
22102        let mut c = bare_servico_fixture("demo");
22103        c.membros = vec![Membro {
22104            caixa: "cart".into(),
22105            versao: "^0.1".into(),
22106        }];
22107        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22108        let via_standalone =
22109            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22110        assert_eq!(
22111            via_method, via_standalone,
22112            "Caixa::validate_kind_slot_coherence must surface the M3 \
22113             mesh-slot arm's diagnostic byte-equal to the standalone \
22114             LayoutError::mesh_slots_on_non_aplicacao ctor on the same \
22115             declared_mesh_slots list",
22116        );
22117    }
22118
22119    #[test]
22120    fn validate_kind_slot_coherence_folds_supervisor_arm_matches_gate() {
22121        // Per-arm equivalence pin on the supervisor-tree arm — the
22122        // sibling of the mesh arm on the cross-family fold. A
22123        // non-Supervisor caixa carrying a declared supervisor slot
22124        // (a `:kind Servico` fixture with `:estrategia` set — the
22125        // smallest possible supervisor slot declaration on a
22126        // foreign kind) surfaces the same
22127        // [`crate::LayoutError::SupervisorSlotsOnNonSupervisor`]
22128        // variant through both dispatches, pinned by field pair
22129        // through `PartialEq`.
22130        use crate::supervisor::RestartStrategy;
22131        let mut c = bare_servico_fixture("demo");
22132        c.estrategia = Some(RestartStrategy::OneForOne);
22133        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22134        let via_standalone = crate::LayoutError::supervisor_slots_on_non_supervisor(
22135            &c,
22136            c.declared_supervisor_slots(),
22137        );
22138        assert_eq!(
22139            via_method, via_standalone,
22140            "Caixa::validate_kind_slot_coherence must surface the \
22141             supervisor-tree arm's diagnostic byte-equal to the \
22142             standalone LayoutError::supervisor_slots_on_non_supervisor \
22143             ctor on the same declared_supervisor_slots list",
22144        );
22145    }
22146
22147    #[test]
22148    fn validate_kind_slot_coherence_folds_servico_arm_matches_gate() {
22149        // Per-arm equivalence pin on the M2 Servico-runtime arm —
22150        // the third and last arm on the cross-family fold. A
22151        // non-Servico caixa carrying a declared M2 slot (a `:kind
22152        // Biblioteca` fixture with `:limits` set — the smallest
22153        // possible M2 slot declaration on a foreign kind) surfaces
22154        // the same [`crate::LayoutError::ServicoSlotsOnNonServico`]
22155        // variant through both dispatches. The three arms together
22156        // enumerate every typed-slot family the substrate carries
22157        // whose "declared but ignored" footgun is gated at the
22158        // layout altitude by a `{ caixa, kind, slots }` wrap variant,
22159        // so the per-arm pins collectively cover the whole
22160        // cross-family kind-coherence axis.
22161        use crate::limits::LimitsSpec;
22162        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22163        c.kind = CaixaKind::Biblioteca;
22164        c.limits = Some(LimitsSpec {
22165            memory: Some(64 * 1024 * 1024),
22166            fuel: None,
22167            wall_clock: None,
22168            cpu: None,
22169        });
22170        let via_method = c.validate_kind_slot_coherence().unwrap_err();
22171        let via_standalone =
22172            crate::LayoutError::servico_slots_on_non_servico(&c, c.declared_servico_slots());
22173        assert_eq!(
22174            via_method, via_standalone,
22175            "Caixa::validate_kind_slot_coherence must surface the M2 \
22176             Servico-runtime arm's diagnostic byte-equal to the \
22177             standalone LayoutError::servico_slots_on_non_servico ctor \
22178             on the same declared_servico_slots list",
22179        );
22180    }
22181
22182    #[test]
22183    fn validate_kind_slot_coherence_mesh_arm_fires_before_supervisor_arm() {
22184        // Cross-arm ordering pin between the first two arms of the
22185        // fold: a fixture carrying BOTH a declared M3 mesh slot
22186        // (`:membros`) AND a declared supervisor-tree slot
22187        // (`:estrategia`) on a foreign kind (a `:kind Servico` here —
22188        // foreign to both the Aplicacao arm and the Supervisor arm)
22189        // surfaces the M3 mesh diagnostic first through the compound
22190        // gate. Pins the pre-fold layout wire-up's canonical
22191        // diagnostic sequence (mesh → supervisor → servico) as a
22192        // property of the substrate primitive rather than a
22193        // convention of the layout call site. A silent reordering
22194        // regression at the primitive would surface here as a
22195        // wrong-variant match before landing at a downstream
22196        // consumer's diagnostic-ordering expectation.
22197        use crate::aplicacao::Membro;
22198        use crate::supervisor::RestartStrategy;
22199        let mut c = bare_servico_fixture("demo");
22200        c.membros = vec![Membro {
22201            caixa: "cart".into(),
22202            versao: "^0.1".into(),
22203        }];
22204        c.estrategia = Some(RestartStrategy::OneForOne);
22205        let err = c.validate_kind_slot_coherence().unwrap_err();
22206        assert!(
22207            matches!(err, crate::LayoutError::MeshSlotsOnNonAplicacao { .. }),
22208            "expected MeshSlotsOnNonAplicacao to fire before \
22209             SupervisorSlotsOnNonSupervisor under the canonical \
22210             mesh → supervisor → servico order, got {err:?}",
22211        );
22212    }
22213
22214    #[test]
22215    fn validate_kind_slot_coherence_supervisor_arm_fires_before_servico_arm() {
22216        // Cross-arm ordering pin between the second and third arms
22217        // of the fold: a fixture carrying BOTH a declared
22218        // supervisor-tree slot (`:estrategia`) AND a declared M2 slot
22219        // (`:limits`) on a kind foreign to both (a `:kind Biblioteca`
22220        // here — foreign to both the Supervisor and the Servico
22221        // arms) surfaces the supervisor-tree diagnostic first
22222        // through the compound gate. Together with the peer
22223        // `_mesh_arm_fires_before_supervisor_arm` pin above this
22224        // pins the whole three-arm canonical order (mesh →
22225        // supervisor → servico) at the substrate primitive.
22226        use crate::limits::LimitsSpec;
22227        use crate::supervisor::RestartStrategy;
22228        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22229        c.kind = CaixaKind::Biblioteca;
22230        c.estrategia = Some(RestartStrategy::OneForOne);
22231        c.limits = Some(LimitsSpec {
22232            memory: Some(64 * 1024 * 1024),
22233            fuel: None,
22234            wall_clock: None,
22235            cpu: None,
22236        });
22237        let err = c.validate_kind_slot_coherence().unwrap_err();
22238        assert!(
22239            matches!(
22240                err,
22241                crate::LayoutError::SupervisorSlotsOnNonSupervisor { .. }
22242            ),
22243            "expected SupervisorSlotsOnNonSupervisor to fire before \
22244             ServicoSlotsOnNonServico under the canonical mesh → \
22245             supervisor → servico order, got {err:?}",
22246        );
22247    }
22248
22249    #[test]
22250    fn validate_kind_slot_coherence_accepts_owner_kind_on_every_arm() {
22251        // Positive control on the identity-element arm: the owner
22252        // kind of each typed-slot family passes the compound gate
22253        // even when it declares the full slot set that family owns.
22254        // Aplicacao with `:membros` populated passes the mesh arm;
22255        // Supervisor with `:estrategia` populated passes the
22256        // supervisor arm; Servico with `:limits` populated passes
22257        // the servico arm. Pins the fold's identity element on
22258        // every owner kind — a silent regression that dropped the
22259        // paired `!kind().is_<owner>()` short-circuit guard would
22260        // surface here as a false-positive rejection of every
22261        // native-slot declaration. Peer with the
22262        // `validate_<kind>_shape_accepts_non_<kind>_kind` identity-
22263        // element pins on the sibling per-Caixa compound gates.
22264        use crate::aplicacao::{Membro, Placement, PlacementStrategy};
22265        use crate::limits::LimitsSpec;
22266        use crate::supervisor::{ChildSpec, RestartPolicy, RestartStrategy};
22267
22268        let mut apli = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22269        apli.kind = CaixaKind::Aplicacao;
22270        apli.bibliotecas = vec![];
22271        apli.membros = vec![Membro {
22272            caixa: "cart".into(),
22273            versao: "^0.1".into(),
22274        }];
22275        apli.placement = Some(Placement {
22276            estrategia: PlacementStrategy::SingleNode,
22277            clusters: vec!["rio".into()],
22278            shard_key: None,
22279            affinity: None,
22280        });
22281        apli.validate_kind_slot_coherence().expect(
22282            "an :kind Aplicacao caixa with declared M3 mesh slots must \
22283             pass the compound gate — Aplicacao is the mesh-slot family's \
22284             owner kind and the fold's identity element on that arm",
22285        );
22286
22287        let mut sup = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22288        sup.kind = CaixaKind::Supervisor;
22289        sup.bibliotecas = vec![];
22290        sup.estrategia = Some(RestartStrategy::OneForOne);
22291        sup.children = vec![ChildSpec {
22292            caixa: "worker".into(),
22293            versao: "^0.1".into(),
22294            restart: RestartPolicy::Permanent,
22295        }];
22296        sup.validate_kind_slot_coherence().expect(
22297            "an :kind Supervisor caixa with declared supervisor-tree slots \
22298             must pass the compound gate — Supervisor is the \
22299             supervisor-slot family's owner kind and the fold's identity \
22300             element on that arm",
22301        );
22302
22303        let mut svc = bare_servico_fixture("svc");
22304        svc.limits = Some(LimitsSpec {
22305            memory: Some(64 * 1024 * 1024),
22306            fuel: None,
22307            wall_clock: None,
22308            cpu: None,
22309        });
22310        svc.validate_kind_slot_coherence().expect(
22311            "an :kind Servico caixa with declared M2 slots must pass the \
22312             compound gate — Servico is the M2-slot family's owner kind \
22313             and the fold's identity element on that arm",
22314        );
22315    }
22316
22317    #[test]
22318    fn validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind() {
22319        // Positive control on the second identity-element arm: a
22320        // bare caixa (no declared typed slots) passes the compound
22321        // gate on every kind. Pins the fold's identity element on
22322        // the empty-slot axis — the paired `Vec::is_empty` short-
22323        // circuit guard fires before the wrap dispatch on all three
22324        // arms, so a bare caixa of any kind surfaces no diagnostic.
22325        // A silent regression that dropped the emptiness guard
22326        // would surface here as a false-positive rejection of every
22327        // no-slot caixa across the whole kind axis.
22328        for kind in CaixaKind::ALL {
22329            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22330            c.kind = *kind;
22331            c.bibliotecas = vec![];
22332            c.validate_kind_slot_coherence().unwrap_or_else(|err| {
22333                panic!(
22334                    "a bare :kind {kind:?} caixa (no declared typed slots) \
22335                     must pass the compound gate — the fold's identity \
22336                     element on the empty-slot axis is the paired \
22337                     Vec::is_empty short-circuit guard, got {err:?}",
22338                )
22339            });
22340        }
22341    }
22342
22343    #[test]
22344    fn run_kind_owned_slot_family_gate_owner_kind_short_circuits_before_accumulator() {
22345        // Fail-before-pass-after identity-element pin on the owner-kind
22346        // arm of the substrate primitive: on a caixa whose kind IS the
22347        // owner of the family named by `is_owner`, the primitive
22348        // short-circuits before dispatching `accumulator` — pinned here
22349        // by a poison-pill accumulator that panics on call. If a
22350        // regression drops the `is_owner` short-circuit and always
22351        // invokes the accumulator, the poison panic surfaces here
22352        // rather than a spurious pass. Byte-equal to the pre-lift
22353        // `if !self.kind().is_<owner>() { … }` outer guard's
22354        // short-circuit at the pre-fold layout call site.
22355        let c = bare_servico_fixture("demo");
22356        c.run_kind_owned_slot_family_gate(
22357            CaixaKind::is_servico,
22358            |_| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking accumulator on the owner kind"),
22359            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on the owner kind"),
22360        )
22361        .expect(
22362            "the owner kind of a slot family must pass the substrate \
22363             primitive as the fold's identity element on the outer \
22364             is_owner guard, without invoking accumulator or wrap",
22365        );
22366    }
22367
22368    #[test]
22369    fn run_kind_owned_slot_family_gate_empty_accumulator_short_circuits_before_wrap() {
22370        // Fail-before-pass-after identity-element pin on the empty-
22371        // accumulator arm: on a non-owner kind whose per-family
22372        // accumulator yields no declared slot, the primitive short-
22373        // circuits before dispatching `wrap` — pinned here by a
22374        // poison-pill wrap that panics on call. Byte-equal to the
22375        // pre-lift `if !<slots>.is_empty() { … }` inner emptiness
22376        // guard's short-circuit at the pre-fold layout call site.
22377        let c = bare_servico_fixture("demo");
22378        c.run_kind_owned_slot_family_gate(
22379            CaixaKind::is_aplicacao,
22380            Caixa::declared_mesh_slots,
22381            |_, _| panic!("run_kind_owned_slot_family_gate must short-circuit before invoking wrap on an empty accumulator"),
22382        )
22383        .expect(
22384            "a non-owner kind carrying no declared slot in the family \
22385             must pass the substrate primitive as the fold's identity \
22386             element on the inner emptiness guard, without invoking \
22387             wrap",
22388        );
22389    }
22390
22391    #[test]
22392    fn run_kind_owned_slot_family_gate_non_owner_non_empty_wraps_verbatim() {
22393        // Equivalence pin on the refusal arm: on a non-owner kind
22394        // whose accumulator yields a non-empty slot list, the primitive
22395        // returns the caller-supplied wrap byte-equal to the direct
22396        // ctor dispatch on the same `(caixa, slots)` pair. Pins the
22397        // three-argument route through — `is_owner` fires false, the
22398        // accumulator produces the slot list, and the wrap ctor
22399        // receives verbatim what a direct dispatch would receive.
22400        // Sibling of the peer per-arm equivalence pins on
22401        // [`Caixa::validate_kind_slot_coherence`].
22402        use crate::aplicacao::Membro;
22403        let mut c = bare_servico_fixture("demo");
22404        c.membros = vec![Membro {
22405            caixa: "cart".into(),
22406            versao: "^0.1".into(),
22407        }];
22408        let via_primitive = c
22409            .run_kind_owned_slot_family_gate(
22410                CaixaKind::is_aplicacao,
22411                Caixa::declared_mesh_slots,
22412                crate::LayoutError::mesh_slots_on_non_aplicacao,
22413            )
22414            .unwrap_err();
22415        let via_direct =
22416            crate::LayoutError::mesh_slots_on_non_aplicacao(&c, c.declared_mesh_slots());
22417        assert_eq!(
22418            via_primitive, via_direct,
22419            "Caixa::run_kind_owned_slot_family_gate must route the \
22420             non-owner-kind + non-empty-accumulator arm through the \
22421             caller-supplied wrap byte-equal to the direct ctor \
22422             dispatch on the same (caixa, slots) pair",
22423        );
22424    }
22425
22426    #[test]
22427    fn validate_kind_slot_coherence_routes_each_arm_through_run_kind_owned_slot_family_gate() {
22428        // Cross-primitive routing pin: every arm of the compound gate
22429        // [`Caixa::validate_kind_slot_coherence`] routes through the
22430        // substrate primitive [`Caixa::run_kind_owned_slot_family_gate`]
22431        // on its `(is_owner, accumulator, wrap)` triple. A silent
22432        // regression that de-folded one arm and re-inlined the four-
22433        // line block would surface here as a mismatch between the
22434        // compound-gate error and the direct-primitive-dispatch error
22435        // on the same fixture. Sibling of the peer
22436        // `probe_declared_entries_routes_miss_arm_through_probe_declared_entry`
22437        // cross-primitive routing pin on the layout-pipeline
22438        // existence-probe axis.
22439        use crate::aplicacao::Membro;
22440        use crate::limits::LimitsSpec;
22441        use crate::supervisor::RestartStrategy;
22442
22443        // Mesh arm — non-Aplicacao carrying a declared M3 slot.
22444        let mut mesh = bare_servico_fixture("demo");
22445        mesh.membros = vec![Membro {
22446            caixa: "cart".into(),
22447            versao: "^0.1".into(),
22448        }];
22449        let via_compound = mesh.validate_kind_slot_coherence().unwrap_err();
22450        let via_primitive = mesh
22451            .run_kind_owned_slot_family_gate(
22452                CaixaKind::is_aplicacao,
22453                Caixa::declared_mesh_slots,
22454                crate::LayoutError::mesh_slots_on_non_aplicacao,
22455            )
22456            .unwrap_err();
22457        assert_eq!(
22458            via_compound, via_primitive,
22459            "validate_kind_slot_coherence's mesh arm must route \
22460             byte-equal through the run_kind_owned_slot_family_gate \
22461             substrate primitive",
22462        );
22463
22464        // Supervisor arm — non-Supervisor carrying a declared
22465        // supervisor-tree slot on a kind foreign to both the Aplicacao
22466        // arm and this one.
22467        let mut sup = bare_servico_fixture("demo");
22468        sup.estrategia = Some(RestartStrategy::OneForOne);
22469        let via_compound = sup.validate_kind_slot_coherence().unwrap_err();
22470        let via_primitive = sup
22471            .run_kind_owned_slot_family_gate(
22472                CaixaKind::is_supervisor,
22473                Caixa::declared_supervisor_slots,
22474                crate::LayoutError::supervisor_slots_on_non_supervisor,
22475            )
22476            .unwrap_err();
22477        assert_eq!(
22478            via_compound, via_primitive,
22479            "validate_kind_slot_coherence's supervisor arm must route \
22480             byte-equal through the run_kind_owned_slot_family_gate \
22481             substrate primitive",
22482        );
22483
22484        // Servico arm — non-Servico carrying a declared M2 slot on a
22485        // kind foreign to every prior arm (Biblioteca — foreign to
22486        // both the Aplicacao mesh arm and the Supervisor supervisor
22487        // arm and the Servico M2 arm).
22488        let mut svc = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22489        svc.kind = CaixaKind::Biblioteca;
22490        svc.limits = Some(LimitsSpec {
22491            memory: Some(64 * 1024 * 1024),
22492            fuel: None,
22493            wall_clock: None,
22494            cpu: None,
22495        });
22496        let via_compound = svc.validate_kind_slot_coherence().unwrap_err();
22497        let via_primitive = svc
22498            .run_kind_owned_slot_family_gate(
22499                CaixaKind::is_servico,
22500                Caixa::declared_servico_slots,
22501                crate::LayoutError::servico_slots_on_non_servico,
22502            )
22503            .unwrap_err();
22504        assert_eq!(
22505            via_compound, via_primitive,
22506            "validate_kind_slot_coherence's servico arm must route \
22507             byte-equal through the run_kind_owned_slot_family_gate \
22508             substrate primitive",
22509        );
22510    }
22511
22512    #[test]
22513    fn validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate() {
22514        // Fail-before-pass-after per-arm equivalence pin on the
22515        // Supervisor no-code arm of the reciprocal code-surface
22516        // fold: a `:kind Supervisor` caixa carrying a declared
22517        // `:bibliotecas` entry (the smallest possible code-surface
22518        // declaration on a no-code kind) surfaces the same
22519        // [`crate::LayoutError::SupervisorOwnsCode`] variant
22520        // through both the compound gate
22521        // [`Caixa::validate_no_code_kind_coherence`] and the
22522        // standalone constructor
22523        // [`crate::LayoutError::supervisor_owns_code`]. Pins the
22524        // fold — a silent regression that de-folded the Supervisor
22525        // arm would surface here as a mismatch between the two
22526        // dispatches. Sibling in shape to the peer
22527        // `validate_kind_slot_coherence_folds_supervisor_arm_matches_gate`
22528        // per-arm equivalence pin on the cross-family
22529        // typed-slot-coherence fold.
22530        let mut c = Caixa::from_lisp(&Caixa::template("sup")).unwrap();
22531        c.kind = CaixaKind::Supervisor;
22532        c.bibliotecas = vec!["lib/sup.lisp".into()];
22533        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22534        let via_standalone = crate::LayoutError::supervisor_owns_code(&c);
22535        assert_eq!(
22536            via_method, via_standalone,
22537            "Caixa::validate_no_code_kind_coherence must surface the \
22538             Supervisor arm's diagnostic byte-equal to the standalone \
22539             LayoutError::supervisor_owns_code ctor",
22540        );
22541    }
22542
22543    #[test]
22544    fn validate_no_code_kind_coherence_folds_aplicacao_arm_matches_gate() {
22545        // Per-arm equivalence pin on the Aplicacao no-code arm —
22546        // the sibling of the Supervisor arm on the code-surface
22547        // fold. A `:kind Aplicacao` caixa carrying a declared
22548        // `:exe` entry surfaces the same
22549        // [`crate::LayoutError::AplicacaoOwnsCode`] variant through
22550        // both dispatches. Uses the `:exe` code-surface axis (a
22551        // second axis distinct from the Supervisor arm's
22552        // `:bibliotecas` fixture) so the three per-arm pins
22553        // collectively exercise every arm of the `has_code`
22554        // disjunction (`:bibliotecas || :exe || :servicos`).
22555        let mut c = Caixa::from_lisp(&Caixa::template("app")).unwrap();
22556        c.kind = CaixaKind::Aplicacao;
22557        c.bibliotecas = vec![];
22558        c.exe = vec!["exe/app".into()];
22559        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22560        let via_standalone = crate::LayoutError::aplicacao_owns_code(&c);
22561        assert_eq!(
22562            via_method, via_standalone,
22563            "Caixa::validate_no_code_kind_coherence must surface the \
22564             Aplicacao arm's diagnostic byte-equal to the standalone \
22565             LayoutError::aplicacao_owns_code ctor",
22566        );
22567    }
22568
22569    #[test]
22570    fn validate_no_code_kind_coherence_folds_acao_arm_matches_gate() {
22571        // Per-arm equivalence pin on the Acao no-code arm — the
22572        // third and last arm on the code-surface fold. A `:kind
22573        // Acao` caixa carrying a declared `:servicos` entry
22574        // surfaces the same [`crate::LayoutError::AcaoOwnsCode`]
22575        // variant through both dispatches. Uses the `:servicos`
22576        // code-surface axis (the third distinct axis of the
22577        // `has_code` disjunction) so the three per-arm pins
22578        // collectively cover every arm of the code-surface
22579        // disjunction plus every no-code kind of the arm
22580        // dispatch.
22581        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
22582        c.kind = CaixaKind::Acao;
22583        c.bibliotecas = vec![];
22584        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22585        let via_method = c.validate_no_code_kind_coherence().unwrap_err();
22586        let via_standalone = crate::LayoutError::acao_owns_code(&c);
22587        assert_eq!(
22588            via_method, via_standalone,
22589            "Caixa::validate_no_code_kind_coherence must surface the \
22590             Acao arm's diagnostic byte-equal to the standalone \
22591             LayoutError::acao_owns_code ctor",
22592        );
22593    }
22594
22595    #[test]
22596    fn validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis() {
22597        // Positive control on the code-owning-kind identity
22598        // element: each of the three code-owning kinds
22599        // (`Biblioteca` owning `:bibliotecas`, `Binario` owning
22600        // `:exe`, `Servico` owning `:servicos`) passes the
22601        // compound gate cleanly when it declares its native code
22602        // surface. Pins the fold's second identity element — the
22603        // paired per-arm `is_<no-code-kind>()` short-circuit
22604        // fires on every code-owning kind, so a caixa with any
22605        // native code declaration on its owner kind surfaces no
22606        // diagnostic. A silent regression that dropped the paired
22607        // `is_<no-code-kind>()` short-circuit guard on any arm
22608        // would surface here as a false-positive rejection of the
22609        // corresponding owner kind. Peer with the
22610        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22611        // identity-element pin on the sibling cross-family fold.
22612        let mut bib = Caixa::from_lisp(&Caixa::template("bib")).unwrap();
22613        bib.kind = CaixaKind::Biblioteca;
22614        bib.bibliotecas = vec!["lib/bib.lisp".into()];
22615        bib.validate_no_code_kind_coherence().expect(
22616            "a :kind Biblioteca caixa with declared :bibliotecas must pass \
22617             the compound gate — Biblioteca owns the :bibliotecas code surface",
22618        );
22619
22620        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22621        bin.kind = CaixaKind::Binario;
22622        bin.bibliotecas = vec![];
22623        bin.exe = vec!["exe/bin".into()];
22624        bin.validate_no_code_kind_coherence().expect(
22625            "a :kind Binario caixa with declared :exe must pass the compound \
22626             gate — Binario owns the :exe code surface",
22627        );
22628
22629        let svc = bare_servico_fixture("svc");
22630        svc.validate_no_code_kind_coherence().expect(
22631            "a :kind Servico caixa with declared :servicos must pass the \
22632             compound gate — Servico owns the :servicos code surface",
22633        );
22634    }
22635
22636    #[test]
22637    fn validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22638        // Positive control on the has-no-code identity element:
22639        // a bare caixa (no declared code) passes the compound
22640        // gate on every kind — including the three no-code kinds
22641        // that would otherwise fire an OwnsCode diagnostic. Pins
22642        // the fold's first identity element — the paired
22643        // `!has_code` short-circuit fires before every per-arm
22644        // wrap dispatch, so a bare caixa of any kind surfaces no
22645        // diagnostic. A silent regression that dropped the
22646        // has_code guard would surface here as a false-positive
22647        // rejection of every no-code kind that declares no code.
22648        // Peer with the
22649        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22650        // identity-element pin on the sibling cross-family fold.
22651        for kind in CaixaKind::ALL {
22652            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22653            c.kind = *kind;
22654            c.bibliotecas = vec![];
22655            c.exe = vec![];
22656            c.servicos = vec![];
22657            c.validate_no_code_kind_coherence().unwrap_or_else(|err| {
22658                panic!(
22659                    "a bare :kind {kind:?} caixa (no declared code) must pass \
22660                     the compound gate — the fold's first identity element is \
22661                     the paired !has_code short-circuit, got {err:?}",
22662                )
22663            });
22664        }
22665    }
22666
22667    #[test]
22668    fn validate_ci_kind_coherence_folds_arm_matches_gate() {
22669        // Fail-before-pass-after per-arm equivalence pin on the
22670        // `:ci`-on-non-`Acao` arm: a `:kind Biblioteca` caixa
22671        // (the smallest non-`Acao` kind) carrying a declared
22672        // `:ci` slot surfaces the same
22673        // [`crate::LayoutError::CiOnNonAcao`] variant through the
22674        // compound gate [`Caixa::validate_ci_kind_coherence`] and
22675        // an inlined struct-literal wrap carrying `caixa.nome()`
22676        // + `caixa.kind()` verbatim. Pins the fold — a silent
22677        // regression that de-folded the arm would surface here as
22678        // a mismatch between the two dispatches. Sibling in shape
22679        // to the peer
22680        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22681        // per-arm equivalence pin on the reciprocal
22682        // code-surface fold.
22683        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22684        c.kind = CaixaKind::Biblioteca;
22685        c.ci = Some(canteiro_types::CiRun {
22686            workspace: "pleme-io".into(),
22687            repo: "caixa".into(),
22688            nodes: vec![],
22689        });
22690        let via_method = c.validate_ci_kind_coherence().unwrap_err();
22691        let via_standalone = crate::LayoutError::CiOnNonAcao {
22692            caixa: c.nome().to_string(),
22693            kind: c.kind(),
22694        };
22695        assert_eq!(
22696            via_method, via_standalone,
22697            "Caixa::validate_ci_kind_coherence must surface the \
22698             :ci-on-non-Acao arm's diagnostic byte-equal to a \
22699             LayoutError::CiOnNonAcao struct literal carrying the \
22700             caixa's nome + kind",
22701        );
22702    }
22703
22704    #[test]
22705    fn validate_ci_kind_coherence_fold_names_offending_kind_on_every_non_acao_kind() {
22706        // Exhaustive per-kind sweep on the non-`Acao` arm: for each
22707        // of the five non-`Acao` kinds
22708        // (`Biblioteca` / `Binario` / `Servico` / `Supervisor` /
22709        // `Aplicacao`), a caixa carrying a declared `:ci` slot
22710        // surfaces the [`crate::LayoutError::CiOnNonAcao`]
22711        // variant naming the offending kind verbatim. A silent
22712        // regression that mistyped one arm's kind-projection
22713        // (e.g. always threading `CaixaKind::Biblioteca` regardless
22714        // of the caixa's actual kind) would surface here as a
22715        // mismatch on every kind past the first. Peer of the
22716        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22717        // exhaustive-sweep pin on the sibling code-surface fold.
22718        for kind in CaixaKind::ALL {
22719            if kind.is_acao() {
22720                continue;
22721            }
22722            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22723            c.kind = *kind;
22724            c.ci = Some(canteiro_types::CiRun {
22725                workspace: "pleme-io".into(),
22726                repo: "caixa".into(),
22727                nodes: vec![],
22728            });
22729            let err = c.validate_ci_kind_coherence().unwrap_err();
22730            match err {
22731                crate::LayoutError::CiOnNonAcao {
22732                    caixa: got_caixa,
22733                    kind: got_kind,
22734                } => {
22735                    assert_eq!(
22736                        got_caixa,
22737                        c.nome(),
22738                        "CiOnNonAcao must name the offending caixa's nome verbatim on kind {kind:?}",
22739                    );
22740                    assert_eq!(
22741                        got_kind, *kind,
22742                        "CiOnNonAcao must name the offending kind verbatim on kind {kind:?}",
22743                    );
22744                }
22745                other => panic!(
22746                    "expected CiOnNonAcao on :kind {kind:?} with declared :ci, got {other:?}",
22747                ),
22748            }
22749        }
22750    }
22751
22752    #[test]
22753    fn validate_ci_kind_coherence_accepts_acao_on_every_ci_shape() {
22754        // Positive control on the owner-kind identity element: an
22755        // `:kind Acao` caixa passes the coherence gate cleanly on
22756        // every `:ci` shape — the arm's paired
22757        // `!kind().is_acao()` short-circuit fires before the
22758        // dispatch, so the fold surfaces no diagnostic even on
22759        // fixtures whose `:ci` would fail the peer
22760        // [`Self::validate_acao_shape`] decompose gate (a
22761        // duplicate-node fixture, an unknown-dep fixture, a
22762        // cyclic fixture). Pins the fold's first identity element
22763        // — a silent regression that dropped the paired
22764        // `!kind().is_acao()` short-circuit guard would surface
22765        // here as a false-positive rejection of every `Acao`
22766        // caixa. Peer with the
22767        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
22768        // identity-element pin on the sibling code-surface fold.
22769        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22770        c.kind = CaixaKind::Acao;
22771        c.bibliotecas = vec![];
22772        c.ci = Some(canteiro_types::CiRun {
22773            workspace: "pleme-io".into(),
22774            repo: "caixa".into(),
22775            nodes: vec![],
22776        });
22777        c.validate_ci_kind_coherence().expect(
22778            "a :kind Acao caixa with declared :ci must pass the compound \
22779             coherence gate — Acao is the :ci-owning kind (a malformed \
22780             :ci on Acao surfaces via validate_acao_shape's decompose gate, \
22781             not via this kind-coherence gate)",
22782        );
22783    }
22784
22785    #[test]
22786    fn validate_ci_kind_coherence_accepts_absent_ci_on_every_kind() {
22787        // Positive control on the absent-`:ci` identity element:
22788        // a caixa with `ci = None` passes the coherence gate on
22789        // every kind — including `Acao`, whose absent `:ci`
22790        // fails a separate presence gate ([`crate::LayoutError::MissingCi`])
22791        // downstream at the layout altitude, not this coherence
22792        // gate. Pins the fold's second identity element — the
22793        // paired `ci().is_some()` short-circuit fires before every
22794        // per-arm dispatch, so a caixa with no declared `:ci`
22795        // surfaces no coherence diagnostic. A silent regression
22796        // that dropped the paired `ci().is_some()` short-circuit
22797        // would surface here as a false-positive rejection on
22798        // every non-`Acao` kind. Peer with the
22799        // `validate_no_code_kind_coherence_accepts_bare_caixa_on_every_kind`
22800        // identity-element pin on the sibling code-surface fold.
22801        for kind in CaixaKind::ALL {
22802            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22803            c.kind = *kind;
22804            c.ci = None;
22805            c.validate_ci_kind_coherence().unwrap_or_else(|err| {
22806                panic!(
22807                    "a :kind {kind:?} caixa with no declared :ci must pass \
22808                     the compound coherence gate — the fold's second identity \
22809                     element is the paired ci().is_some() short-circuit, got \
22810                     {err:?}",
22811                )
22812            });
22813        }
22814    }
22815
22816    #[test]
22817    fn validate_foreign_code_kind_coherence_folds_arm_matches_gate() {
22818        // Fail-before-pass-after equivalence pin on the compound
22819        // foreign-code-slot coherence fold: a `:kind Servico` caixa
22820        // carrying a declared `:exe` entry (the smallest possible
22821        // foreign-code-slot declaration on a code-running kind that
22822        // is not its owner — Servico owns `:servicos`, not `:exe`)
22823        // surfaces the same [`crate::LayoutError::ForeignCodeSlot`]
22824        // variant through both the compound gate
22825        // [`Caixa::validate_foreign_code_kind_coherence`] and the
22826        // standalone constructor
22827        // [`crate::LayoutError::foreign_code_slot`] dispatched on the
22828        // same `declared_foreign_code_slots` list. Pins the fold — a
22829        // silent regression that de-folded the arm would surface here
22830        // as a mismatch between the two dispatches. Sibling in shape
22831        // to the peer
22832        // `validate_kind_slot_coherence_folds_mesh_arm_matches_gate`
22833        // / `validate_ci_kind_coherence_folds_arm_matches_gate` /
22834        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22835        // per-arm equivalence pins on the sibling kind-coherence folds.
22836        let mut c = bare_servico_fixture("demo");
22837        c.exe = vec!["exe/foreign".into()];
22838        let via_method = c.validate_foreign_code_kind_coherence().unwrap_err();
22839        let via_standalone =
22840            crate::LayoutError::foreign_code_slot(&c, c.declared_foreign_code_slots());
22841        assert_eq!(
22842            via_method, via_standalone,
22843            "Caixa::validate_foreign_code_kind_coherence must surface the \
22844             foreign-code-slot diagnostic byte-equal to the standalone \
22845             LayoutError::foreign_code_slot ctor on the same \
22846             declared_foreign_code_slots list",
22847        );
22848    }
22849
22850    #[test]
22851    fn validate_foreign_code_kind_coherence_exe_arm_precedes_servicos_arm() {
22852        // Cross-arm ordering pin on the fold's accumulator: a fixture
22853        // carrying BOTH a declared `:exe` AND a declared `:servicos`
22854        // on a kind foreign to both (a `:kind Biblioteca` here —
22855        // foreign to both the Binario arm and the Servico arm)
22856        // surfaces `:exe` first in the `ForeignCodeSlot`'s slots
22857        // list. Pins the canonical `:exe` → `:servicos` diagnostic
22858        // order [`Caixa::declared_foreign_code_slots`] establishes,
22859        // as a property of the substrate primitive rather than an
22860        // implicit accumulator convention. A silent reordering
22861        // regression at the accumulator would surface here as a
22862        // wrong-first-slot list before landing at a downstream
22863        // consumer's diagnostic-ordering expectation.
22864        let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22865        c.kind = CaixaKind::Biblioteca;
22866        c.exe = vec!["exe/demo".into()];
22867        c.servicos = vec!["servicos/demo.computeunit.yaml".into()];
22868        let err = c.validate_foreign_code_kind_coherence().unwrap_err();
22869        let crate::LayoutError::ForeignCodeSlot { slots, .. } = &err else {
22870            panic!("expected ForeignCodeSlot variant, got {err:?}");
22871        };
22872        assert!(
22873            slots.starts_with(":exe"),
22874            "expected the :exe arm to precede the :servicos arm in the \
22875             ForeignCodeSlot slots list under the canonical :exe → :servicos \
22876             order, got slots = {slots:?}",
22877        );
22878        assert!(
22879            slots.contains(":servicos"),
22880            "expected the :servicos arm to also fire in the ForeignCodeSlot \
22881             slots list on a fixture carrying both foreign code surfaces, \
22882             got slots = {slots:?}",
22883        );
22884    }
22885
22886    #[test]
22887    fn validate_foreign_code_kind_coherence_accepts_native_slot_on_owner_kind() {
22888        // Positive control on the native-slot identity element: each
22889        // code-surface slot's owner kind passes the fold trivially
22890        // when it declares only its native code surface. `:kind
22891        // Binario` with a declared `:exe` and no `:servicos` passes
22892        // (the `!requires_exe()` guard short-circuits the arm inside
22893        // [`Caixa::declared_foreign_code_slots`], so the accumulator
22894        // returns empty); `:kind Servico` with a declared `:servicos`
22895        // and no `:exe` passes for the mirror reason. Pins the fold's
22896        // native-slot identity element on both arms — a silent
22897        // regression that dropped either per-arm `!requires_<slot>()`
22898        // predicate would surface here as a false-positive rejection
22899        // of every native-slot declaration on its owner kind. Peer
22900        // with the
22901        // `validate_kind_slot_coherence_accepts_owner_kind_on_every_arm`
22902        // identity-element pin on the sibling cross-family fold.
22903        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22904        bin.kind = CaixaKind::Binario;
22905        bin.bibliotecas = vec![];
22906        bin.exe = vec!["exe/bin".into()];
22907        bin.servicos = vec![];
22908        bin.validate_foreign_code_kind_coherence().expect(
22909            "a :kind Binario caixa with a declared native :exe and no \
22910             :servicos must pass the compound coherence gate — Binario is \
22911             the :exe slot's owner kind and the fold's native-slot identity \
22912             element on that arm",
22913        );
22914
22915        let mut svc = bare_servico_fixture("svc");
22916        svc.exe = vec![];
22917        svc.validate_foreign_code_kind_coherence().expect(
22918            "a :kind Servico caixa with a declared native :servicos and no \
22919             :exe must pass the compound coherence gate — Servico is the \
22920             :servicos slot's owner kind and the fold's native-slot identity \
22921             element on that arm",
22922        );
22923    }
22924
22925    #[test]
22926    fn validate_foreign_code_kind_coherence_accepts_bare_caixa_on_every_kind() {
22927        // Positive control on the empty-slot identity element: a
22928        // bare caixa (no declared `:exe` and no declared `:servicos`)
22929        // passes the compound gate on every kind. Pins the fold's
22930        // identity element on the empty-accumulator axis — the outer
22931        // `is_empty` short-circuit fires before the wrap dispatch on
22932        // every kind, so a bare caixa of any kind surfaces no
22933        // foreign-code-slot diagnostic. A silent regression that
22934        // dropped the emptiness guard would surface here as a
22935        // false-positive rejection of every no-code-slot caixa
22936        // across the whole kind axis. Peer with the
22937        // `validate_kind_slot_coherence_accepts_bare_caixa_on_every_kind`
22938        // identity-element pin on the sibling cross-family fold.
22939        for kind in CaixaKind::ALL {
22940            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
22941            c.kind = *kind;
22942            c.bibliotecas = vec![];
22943            c.exe = vec![];
22944            c.servicos = vec![];
22945            c.validate_foreign_code_kind_coherence()
22946                .unwrap_or_else(|err| {
22947                    panic!(
22948                        "a bare :kind {kind:?} caixa (no declared :exe / \
22949                         :servicos) must pass the compound coherence gate — \
22950                         the fold's identity element on the empty-accumulator \
22951                         axis is the outer Vec::is_empty short-circuit, got \
22952                         {err:?}",
22953                    )
22954                });
22955        }
22956    }
22957
22958    #[test]
22959    fn validate_required_kind_slot_folds_binario_arm_matches_gate() {
22960        // Fail-before-pass-after per-arm equivalence pin on the
22961        // `Binario` required-`:exe` arm of the required-slot fold:
22962        // a `:kind Binario` caixa carrying no declared `:exe` entry
22963        // surfaces the same
22964        // [`crate::LayoutError::BinarioWithoutExe`] variant through
22965        // both the compound gate
22966        // [`Caixa::validate_required_kind_slot`] and the standalone
22967        // constructor [`crate::LayoutError::binario_without_exe`].
22968        // Pins the fold — a silent regression that de-folded the
22969        // `Binario` arm would surface here as a mismatch between
22970        // the two dispatches. Sibling in shape to the peer
22971        // `validate_no_code_kind_coherence_folds_supervisor_arm_matches_gate`
22972        // per-arm equivalence pin on the reciprocal code-surface
22973        // fold.
22974        let mut c = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
22975        c.kind = CaixaKind::Binario;
22976        c.bibliotecas = vec![];
22977        c.exe = vec![];
22978        let via_method = c.validate_required_kind_slot().unwrap_err();
22979        let via_standalone = crate::LayoutError::binario_without_exe(&c);
22980        assert_eq!(
22981            via_method, via_standalone,
22982            "Caixa::validate_required_kind_slot must surface the \
22983             Binario arm's diagnostic byte-equal to the standalone \
22984             LayoutError::binario_without_exe ctor",
22985        );
22986    }
22987
22988    #[test]
22989    fn validate_required_kind_slot_folds_servico_arm_matches_gate() {
22990        // Per-arm equivalence pin on the `Servico` required-
22991        // `:servicos` arm — the sibling of the Binario arm on the
22992        // required-slot fold. A `:kind Servico` caixa carrying no
22993        // declared `:servicos` entry surfaces the same
22994        // [`crate::LayoutError::ServicoWithoutServicos`] variant
22995        // through both dispatches.
22996        let mut c = Caixa::from_lisp(&Caixa::template("svc")).unwrap();
22997        c.kind = CaixaKind::Servico;
22998        c.bibliotecas = vec![];
22999        c.servicos = vec![];
23000        let via_method = c.validate_required_kind_slot().unwrap_err();
23001        let via_standalone = crate::LayoutError::servico_without_servicos(&c);
23002        assert_eq!(
23003            via_method, via_standalone,
23004            "Caixa::validate_required_kind_slot must surface the \
23005             Servico arm's diagnostic byte-equal to the standalone \
23006             LayoutError::servico_without_servicos ctor",
23007        );
23008    }
23009
23010    #[test]
23011    fn validate_required_kind_slot_folds_acao_arm_matches_gate() {
23012        // Per-arm equivalence pin on the `Acao` required-`:ci` arm
23013        // — the third and last arm on the required-slot fold. A
23014        // `:kind Acao` caixa carrying no declared `:ci` slot
23015        // surfaces the same [`crate::LayoutError::MissingCi`]
23016        // variant through both dispatches. The three per-arm pins
23017        // collectively cover every required-slot axis and every
23018        // owner kind of the arm dispatch.
23019        let mut c = Caixa::from_lisp(&Caixa::template("acao")).unwrap();
23020        c.kind = CaixaKind::Acao;
23021        c.bibliotecas = vec![];
23022        c.ci = None;
23023        let via_method = c.validate_required_kind_slot().unwrap_err();
23024        let via_standalone = crate::LayoutError::missing_ci(&c);
23025        assert_eq!(
23026            via_method, via_standalone,
23027            "Caixa::validate_required_kind_slot must surface the \
23028             Acao arm's diagnostic byte-equal to the standalone \
23029             LayoutError::missing_ci ctor",
23030        );
23031    }
23032
23033    #[test]
23034    fn validate_required_kind_slot_accepts_owner_kind_with_required_slot_present() {
23035        // Positive control on the owner-kind-with-slot-present
23036        // identity element: each of the three owner kinds
23037        // (`Binario` with a non-empty `:exe`, `Servico` with a
23038        // non-empty `:servicos`, `Acao` with `ci = Some(_)`)
23039        // passes the compound gate cleanly when it declares its
23040        // required slot. Pins the fold's second identity element
23041        // — the paired `is_empty` / `is_none` short-circuit fires
23042        // on every owner kind whose required slot is present, so
23043        // a caixa with its native required slot surfaces no
23044        // diagnostic. A silent regression that dropped the paired
23045        // `is_empty` / `is_none` short-circuit guard on any arm
23046        // would surface here as a false-positive rejection of the
23047        // corresponding owner kind. Peer with the
23048        // `validate_no_code_kind_coherence_accepts_owner_kind_on_every_code_axis`
23049        // identity-element pin on the sibling code-surface fold.
23050        let mut bin = Caixa::from_lisp(&Caixa::template("bin")).unwrap();
23051        bin.kind = CaixaKind::Binario;
23052        bin.bibliotecas = vec![];
23053        bin.exe = vec!["exe/bin".into()];
23054        bin.validate_required_kind_slot().expect(
23055            "a :kind Binario caixa with declared :exe must pass the \
23056             required-slot gate — Binario's required slot is present",
23057        );
23058
23059        let svc = bare_servico_fixture("svc");
23060        svc.validate_required_kind_slot().expect(
23061            "a :kind Servico caixa with declared :servicos must pass \
23062             the required-slot gate — Servico's required slot is present",
23063        );
23064
23065        let acao = acao_fixture("acao");
23066        acao.validate_required_kind_slot().expect(
23067            "a :kind Acao caixa with declared :ci must pass the \
23068             required-slot gate — Acao's required slot is present",
23069        );
23070    }
23071
23072    #[test]
23073    fn validate_required_kind_slot_accepts_non_owner_kinds() {
23074        // Positive control on the non-owner-kind identity element:
23075        // every kind that is not one of the three owner kinds
23076        // (`Binario` / `Servico` / `Acao`) passes the compound gate
23077        // trivially — each per-arm predicate is
23078        // `self.kind().requires_<slot>()`, which returns `true`
23079        // only for the owner kind of that arm, so a non-owner kind
23080        // short-circuits every per-arm dispatch. Bibliotheca,
23081        // Supervisor, and Aplicacao are the three non-owner kinds
23082        // this pin exercises — none of them owns a required slot in
23083        // this fold (`Biblioteca`'s `:bibliotecas` default-file
23084        // fallback stays on the layout-side `MissingLib` fs-oracle
23085        // gate outside this fold; `Supervisor`'s `:children` and
23086        // `Aplicacao`'s `:membros` are carried by
23087        // [`CaixaKind::requires_children`] /
23088        // [`CaixaKind::requires_membros`] without a paired
23089        // layout-side wire-up). A silent regression that swapped a
23090        // per-arm predicate for a non-`requires_*` guard would
23091        // surface here as a false-positive rejection of the
23092        // corresponding non-owner kind. Peer with the
23093        // `validate_ci_kind_coherence_accepts_absent_ci_on_every_kind`
23094        // identity-element pin on the sibling `:ci` fold.
23095        for kind in CaixaKind::ALL {
23096            if kind.requires_exe() || kind.requires_servicos() || kind.requires_ci() {
23097                continue;
23098            }
23099            let mut c = Caixa::from_lisp(&Caixa::template("demo")).unwrap();
23100            c.kind = *kind;
23101            c.bibliotecas = vec![];
23102            c.exe = vec![];
23103            c.servicos = vec![];
23104            c.ci = None;
23105            c.validate_required_kind_slot().unwrap_or_else(|err| {
23106                panic!(
23107                    "a :kind {kind:?} caixa (a non-owner kind on every \
23108                     required-slot arm) must pass the compound gate — the \
23109                     fold's identity element is the paired \
23110                     `self.kind().requires_<slot>()` short-circuit, got \
23111                     {err:?}",
23112                )
23113            });
23114        }
23115    }
23116
23117    // ── `manifest_code_path_slot_path_ctors!` — the paired `{ slot:
23118    //    &'static str, path: PathBuf }` two-slot envelope on
23119    //    `ManifestError`, strict sibling of the peer
23120    //    [`crate::behavior::behavior_slot_path_ctors!`] (67c31ec) on the
23121    //    sibling `BehaviorError` envelope's identical
23122    //    `{ slot: &'static str, path: PathBuf }` two-slot shape.
23123    //    Five-variant lift closing the five open-coded ctor sites
23124    //    remaining on the `:bibliotecas` / `:exe` / `:servicos`
23125    //    code-path-list value-shape trajectory this envelope carries.
23126
23127    #[test]
23128    fn code_path_absolute_ctor_matches_struct_literal_wrap() {
23129        let path = Path::new("/abs/lib/x.lisp");
23130        assert_eq!(
23131            ManifestError::code_path_absolute(":bibliotecas", path),
23132            ManifestError::CodePathAbsolute {
23133                slot: ":bibliotecas",
23134                path: path.to_path_buf(),
23135            },
23136            "generated code_path_absolute ctor must produce byte-equal \
23137             `ManifestError::CodePathAbsolute` to the pre-lift \
23138             struct-literal wrap on the same `(&'static str, &Path)` \
23139             fixture",
23140        );
23141    }
23142
23143    #[test]
23144    fn code_path_parent_escape_ctor_matches_struct_literal_wrap() {
23145        let path = Path::new("lib/../../etc/x.lisp");
23146        assert_eq!(
23147            ManifestError::code_path_parent_escape(":bibliotecas", path),
23148            ManifestError::CodePathParentEscape {
23149                slot: ":bibliotecas",
23150                path: path.to_path_buf(),
23151            },
23152            "generated code_path_parent_escape ctor must produce \
23153             byte-equal `ManifestError::CodePathParentEscape` to the \
23154             pre-lift struct-literal wrap on the same `(&'static str, \
23155             &Path)` fixture",
23156        );
23157    }
23158
23159    #[test]
23160    fn code_path_non_lisp_extension_ctor_matches_struct_literal_wrap() {
23161        let path = Path::new("lib/x.txt");
23162        assert_eq!(
23163            ManifestError::code_path_non_lisp_extension(":bibliotecas", path),
23164            ManifestError::CodePathNonLispExtension {
23165                slot: ":bibliotecas",
23166                path: path.to_path_buf(),
23167            },
23168            "generated code_path_non_lisp_extension ctor must produce \
23169             byte-equal `ManifestError::CodePathNonLispExtension` to \
23170             the pre-lift struct-literal wrap on the same \
23171             `(&'static str, &Path)` fixture",
23172        );
23173    }
23174
23175    #[test]
23176    fn code_path_non_computeunit_yaml_extension_ctor_matches_struct_literal_wrap() {
23177        let path = Path::new("servicos/x.yaml");
23178        assert_eq!(
23179            ManifestError::code_path_non_computeunit_yaml_extension(":servicos", path),
23180            ManifestError::CodePathNonComputeUnitYamlExtension {
23181                slot: ":servicos",
23182                path: path.to_path_buf(),
23183            },
23184            "generated code_path_non_computeunit_yaml_extension ctor \
23185             must produce byte-equal \
23186             `ManifestError::CodePathNonComputeUnitYamlExtension` to \
23187             the pre-lift struct-literal wrap on the same \
23188             `(&'static str, &Path)` fixture",
23189        );
23190    }
23191
23192    #[test]
23193    fn code_path_duplicate_ctor_matches_struct_literal_wrap() {
23194        let path = Path::new("lib/x.lisp");
23195        assert_eq!(
23196            ManifestError::code_path_duplicate(":bibliotecas", path),
23197            ManifestError::CodePathDuplicate {
23198                slot: ":bibliotecas",
23199                path: path.to_path_buf(),
23200            },
23201            "generated code_path_duplicate ctor must produce byte-equal \
23202             `ManifestError::CodePathDuplicate` to the pre-lift \
23203             struct-literal wrap on the same `(&'static str, &Path)` \
23204             fixture",
23205        );
23206    }
23207
23208    #[test]
23209    fn manifest_code_path_slot_path_ctors_route_slot_and_path_through_uniformly() {
23210        // Cross-axis routing pin: sweep the two constructor input axes
23211        // (`slot: &'static str`, `path: &Path`) through non-default
23212        // fixtures against every generated arm in the
23213        // [`manifest_code_path_slot_path_ctors!`] macro, so any
23214        // wrapper-side lowercase / trim / truncate / canonicalization at
23215        // codegen time — or a silent field re-name away from the
23216        // canonical `slot` / `path` axes on any one variant, or a `slot`
23217        // axis silently rerouted through `.to_string()` instead of
23218        // passed as `&'static str` verbatim, or a `path` axis silently
23219        // rerouted through `.canonicalize()` / `PathBuf::from(<lossy
23220        // string>)` instead of `.to_path_buf()` — surfaces here rather
23221        // than at a downstream diagnostic-shape mismatch. Peer of the
23222        // sibling
23223        // [`crate::behavior::tests::behavior_slot_path_ctors_route_slot_and_path_through_uniformly`]
23224        // pin (67c31ec) on the sibling `BehaviorError` envelope's
23225        // identical two-slot family.
23226        //
23227        // The `path` fixture carries three distinguishing traits at
23228        // once: a non-`root/`-relative leading segment (`weird/`), a
23229        // `..` component (a canonicalization trap that would collapse
23230        // to `weird/x.lisp` under `.canonicalize()`), and a mixed-case
23231        // extension (a lowercase-normalization trap that would collapse
23232        // `.LISP` to `.lisp` under any `to_ascii_lowercase()` codegen)
23233        // so a routing regression on any one of the three trap axes
23234        // surfaces at assert time. Similarly the `slot` fixture
23235        // sweeps the three canonical code-path author-key literals
23236        // (`:bibliotecas` / `:exe` / `:servicos`) so a silent lookup
23237        // against a per-variant const roster would surface here.
23238        let path = Path::new("weird/../nested/x.LISP");
23239        let cases: [(ManifestError, ManifestError); 5] = [
23240            (
23241                ManifestError::code_path_absolute(":bibliotecas", path),
23242                ManifestError::CodePathAbsolute {
23243                    slot: ":bibliotecas",
23244                    path: path.to_path_buf(),
23245                },
23246            ),
23247            (
23248                ManifestError::code_path_parent_escape(":exe", path),
23249                ManifestError::CodePathParentEscape {
23250                    slot: ":exe",
23251                    path: path.to_path_buf(),
23252                },
23253            ),
23254            (
23255                ManifestError::code_path_non_lisp_extension(":servicos", path),
23256                ManifestError::CodePathNonLispExtension {
23257                    slot: ":servicos",
23258                    path: path.to_path_buf(),
23259                },
23260            ),
23261            (
23262                ManifestError::code_path_non_computeunit_yaml_extension(":bibliotecas", path),
23263                ManifestError::CodePathNonComputeUnitYamlExtension {
23264                    slot: ":bibliotecas",
23265                    path: path.to_path_buf(),
23266                },
23267            ),
23268            (
23269                ManifestError::code_path_duplicate(":exe", path),
23270                ManifestError::CodePathDuplicate {
23271                    slot: ":exe",
23272                    path: path.to_path_buf(),
23273                },
23274            ),
23275        ];
23276        for (via_ctor, via_struct_literal) in cases {
23277            assert_eq!(
23278                via_ctor, via_struct_literal,
23279                "manifest_code_path_slot_path_ctors!-generated ctor \
23280                 must pass `slot` verbatim onto the canonical \
23281                 `&'static str` `slot` field and route `path` through \
23282                 `.to_path_buf()` onto the canonical `PathBuf` `path` \
23283                 field — a field-rename, silent-conversion, or \
23284                 axis-swap regression surfaces here rather than at a \
23285                 downstream diagnostic-shape mismatch",
23286            );
23287        }
23288    }
23289
23290    // ── `manifest_field_reason_ctors!` — the paired `{ <field>: String,
23291    //    reason: String }` two-slot envelope on `ManifestError`, direct
23292    //    sibling of the peer
23293    //    [`crate::aplicacao::aplicacao_field_reason_ctors!`] (981060b)
23294    //    on the M3 mesh `AplicacaoError` envelope's identical two-slot
23295    //    shape and of the peer [`crate::dep::dep_nome_axis_reason_ctors!`]
23296    //    (5621f8a) on the sibling `:deps` envelope's mirror-symmetric
23297    //    three-slot shape (the `nome` axis added at the per-dep-owned
23298    //    altitude). Nine-variant lift closing the nine open-coded ctor
23299    //    sites at the per-axis [`Caixa::validate_*`] cascade.
23300
23301    #[test]
23302    fn nome_invalid_ctor_matches_struct_literal_wrap() {
23303        let nome = "cart-svc";
23304        let reason = "sample reason text";
23305        assert_eq!(
23306            ManifestError::nome_invalid(nome, reason),
23307            ManifestError::NomeInvalid {
23308                nome: nome.to_string(),
23309                reason: reason.to_string(),
23310            },
23311            "generated nome_invalid ctor must produce byte-equal \
23312             `ManifestError::NomeInvalid` to the pre-lift struct-literal \
23313             wrap on the same `(&str, &str)` fixture",
23314        );
23315    }
23316
23317    #[test]
23318    fn nome_chart_name_budget_exceeded_ctor_matches_struct_literal_wrap() {
23319        let nome = "a-very-long-cart-service-name";
23320        let reason = "sample reason text";
23321        assert_eq!(
23322            ManifestError::nome_chart_name_budget_exceeded(nome, reason),
23323            ManifestError::NomeChartNameBudgetExceeded {
23324                nome: nome.to_string(),
23325                reason: reason.to_string(),
23326            },
23327            "generated nome_chart_name_budget_exceeded ctor must produce \
23328             byte-equal `ManifestError::NomeChartNameBudgetExceeded` to \
23329             the pre-lift struct-literal wrap on the same `(&str, &str)` \
23330             fixture",
23331        );
23332    }
23333
23334    #[test]
23335    fn versao_invalid_ctor_matches_struct_literal_wrap() {
23336        let versao = "0.1";
23337        let reason = "sample reason text";
23338        assert_eq!(
23339            ManifestError::versao_invalid(versao, reason),
23340            ManifestError::VersaoInvalid {
23341                versao: versao.to_string(),
23342                reason: reason.to_string(),
23343            },
23344            "generated versao_invalid ctor must produce byte-equal \
23345             `ManifestError::VersaoInvalid` to the pre-lift \
23346             struct-literal wrap on the same `(&str, &str)` fixture",
23347        );
23348    }
23349
23350    #[test]
23351    fn etiqueta_invalid_ctor_matches_struct_literal_wrap() {
23352        let etiqueta = "MyKeyword";
23353        let reason = "sample reason text";
23354        assert_eq!(
23355            ManifestError::etiqueta_invalid(etiqueta, reason),
23356            ManifestError::EtiquetaInvalid {
23357                etiqueta: etiqueta.to_string(),
23358                reason: reason.to_string(),
23359            },
23360            "generated etiqueta_invalid ctor must produce byte-equal \
23361             `ManifestError::EtiquetaInvalid` to the pre-lift \
23362             struct-literal wrap on the same `(&str, &str)` fixture",
23363        );
23364    }
23365
23366    #[test]
23367    fn autor_invalid_ctor_matches_struct_literal_wrap() {
23368        let autor = "Ada Lovelace";
23369        let reason = "sample reason text";
23370        assert_eq!(
23371            ManifestError::autor_invalid(autor, reason),
23372            ManifestError::AutorInvalid {
23373                autor: autor.to_string(),
23374                reason: reason.to_string(),
23375            },
23376            "generated autor_invalid ctor must produce byte-equal \
23377             `ManifestError::AutorInvalid` to the pre-lift struct-literal \
23378             wrap on the same `(&str, &str)` fixture",
23379        );
23380    }
23381
23382    #[test]
23383    fn repositorio_invalid_ctor_matches_struct_literal_wrap() {
23384        let repositorio = "https://example.com/no-dot-git";
23385        let reason = "sample reason text";
23386        assert_eq!(
23387            ManifestError::repositorio_invalid(repositorio, reason),
23388            ManifestError::RepositorioInvalid {
23389                repositorio: repositorio.to_string(),
23390                reason: reason.to_string(),
23391            },
23392            "generated repositorio_invalid ctor must produce byte-equal \
23393             `ManifestError::RepositorioInvalid` to the pre-lift \
23394             struct-literal wrap on the same `(&str, &str)` fixture",
23395        );
23396    }
23397
23398    #[test]
23399    fn descricao_invalid_ctor_matches_struct_literal_wrap() {
23400        let descricao = "some description";
23401        let reason = "sample reason text";
23402        assert_eq!(
23403            ManifestError::descricao_invalid(descricao, reason),
23404            ManifestError::DescricaoInvalid {
23405                descricao: descricao.to_string(),
23406                reason: reason.to_string(),
23407            },
23408            "generated descricao_invalid ctor must produce byte-equal \
23409             `ManifestError::DescricaoInvalid` to the pre-lift \
23410             struct-literal wrap on the same `(&str, &str)` fixture",
23411        );
23412    }
23413
23414    #[test]
23415    fn licenca_invalid_ctor_matches_struct_literal_wrap() {
23416        let licenca = "not-an-spdx";
23417        let reason = "sample reason text";
23418        assert_eq!(
23419            ManifestError::licenca_invalid(licenca, reason),
23420            ManifestError::LicencaInvalid {
23421                licenca: licenca.to_string(),
23422                reason: reason.to_string(),
23423            },
23424            "generated licenca_invalid ctor must produce byte-equal \
23425             `ManifestError::LicencaInvalid` to the pre-lift \
23426             struct-literal wrap on the same `(&str, &str)` fixture",
23427        );
23428    }
23429
23430    #[test]
23431    fn edicao_invalid_ctor_matches_struct_literal_wrap() {
23432        let edicao = "26";
23433        let reason = "sample reason text";
23434        assert_eq!(
23435            ManifestError::edicao_invalid(edicao, reason),
23436            ManifestError::EdicaoInvalid {
23437                edicao: edicao.to_string(),
23438                reason: reason.to_string(),
23439            },
23440            "generated edicao_invalid ctor must produce byte-equal \
23441             `ManifestError::EdicaoInvalid` to the pre-lift \
23442             struct-literal wrap on the same `(&str, &str)` fixture",
23443        );
23444    }
23445
23446    // Cross-family invariance pin — the nine sibling ctors all route
23447    // `reason: impl Into<String>` + `<field>: &str` verbatim onto their
23448    // respective typed variants through the shared
23449    // [`manifest_field_reason_ctors!`] macro. Sweeps three fixture
23450    // shapes for `reason` (`&str` literal, owned `String`, `format!(…)`
23451    // output — the three shapes every in-crate wire-up threads through:
23452    // the parser-shaped `String` every `Result<(), String>` predicate
23453    // returns, the `e.to_string()` owned `String` the
23454    // `semver::Version::parse` arm passes, and the literal-shape reason
23455    // the `EdicaoInvalid` direct arm passes) against every generated arm
23456    // so any per-arm wrapper transformation drift surfaces here rather
23457    // than at a downstream diagnostic-shape mismatch. Peer of the
23458    // sibling
23459    // [`crate::aplicacao::tests::aplicacao_field_reason_ctors_route_reason_through_into_uniformly`]
23460    // pin (981060b) on the sibling `AplicacaoError` envelope's identical
23461    // two-slot family.
23462    #[test]
23463    fn manifest_field_reason_ctors_route_reason_through_into_uniformly() {
23464        let via_literal = "literal reason text";
23465        let via_owned: String = String::from("literal reason text");
23466        let via_format = format!("{} reason text", "literal");
23467        assert_eq!(
23468            ManifestError::nome_invalid("n", via_literal),
23469            ManifestError::nome_invalid("n", via_owned.clone()),
23470        );
23471        assert_eq!(
23472            ManifestError::nome_invalid("n", via_literal),
23473            ManifestError::nome_invalid("n", via_format.clone()),
23474        );
23475        assert_eq!(
23476            ManifestError::nome_chart_name_budget_exceeded("n", via_literal),
23477            ManifestError::nome_chart_name_budget_exceeded("n", via_owned.clone()),
23478        );
23479        assert_eq!(
23480            ManifestError::versao_invalid("0.1", via_literal),
23481            ManifestError::versao_invalid("0.1", via_owned.clone()),
23482        );
23483        assert_eq!(
23484            ManifestError::etiqueta_invalid("k", via_literal),
23485            ManifestError::etiqueta_invalid("k", via_owned.clone()),
23486        );
23487        assert_eq!(
23488            ManifestError::autor_invalid("a", via_literal),
23489            ManifestError::autor_invalid("a", via_owned.clone()),
23490        );
23491        assert_eq!(
23492            ManifestError::repositorio_invalid("r", via_literal),
23493            ManifestError::repositorio_invalid("r", via_owned.clone()),
23494        );
23495        assert_eq!(
23496            ManifestError::descricao_invalid("d", via_literal),
23497            ManifestError::descricao_invalid("d", via_owned.clone()),
23498        );
23499        assert_eq!(
23500            ManifestError::licenca_invalid("l", via_literal),
23501            ManifestError::licenca_invalid("l", via_owned.clone()),
23502        );
23503        assert_eq!(
23504            ManifestError::edicao_invalid("26", via_literal),
23505            ManifestError::edicao_invalid("26", via_owned),
23506        );
23507        assert_eq!(
23508            ManifestError::edicao_invalid("26", via_literal),
23509            ManifestError::edicao_invalid("26", via_format),
23510        );
23511    }
23512
23513    // Cross-arm routing pin — the nine sibling ctors accept both `&str`
23514    // (from the [`Caixa::nome`] / [`Caixa::versao`] / [`Caixa::repositorio`]
23515    // / [`Caixa::descricao`] / [`Caixa::licenca`] / [`Caixa::edicao`]
23516    // accessors that return `&str`) and `&String` (from the
23517    // [`Caixa::etiquetas`] / [`Caixa::autores`] slice iterators that yield
23518    // `&String`) at the `<field>: &str` parameter via Deref coercion. This
23519    // pin sweeps both call shapes against the two accessors' actual
23520    // wire-up postures so a future rebrand of the etiquetas / autores
23521    // slice-iterator type (a lift from `&[String]` to `&[Cow<'_, str>]`,
23522    // a `smol_str::SmolStr` per-entry swap) that silently broke the
23523    // Deref-coercion path surfaces at this pin rather than at a
23524    // recompile-time type-mismatch far from the ctor family.
23525    #[test]
23526    fn manifest_field_reason_ctors_accept_both_str_and_string_slice_iters() {
23527        let owned: String = String::from("MyKeyword");
23528        // `&str` literal — the canonical accessor-return shape
23529        // ([`Caixa::nome`] etc. yield `&str`).
23530        assert_eq!(
23531            ManifestError::etiqueta_invalid("MyKeyword", "r"),
23532            ManifestError::EtiquetaInvalid {
23533                etiqueta: "MyKeyword".to_string(),
23534                reason: "r".to_string(),
23535            },
23536        );
23537        // `&String` — the canonical slice-iterator-yield shape
23538        // ([`Caixa::etiquetas`] / [`Caixa::autores`] yield `&String`).
23539        assert_eq!(
23540            ManifestError::etiqueta_invalid(&owned, "r"),
23541            ManifestError::EtiquetaInvalid {
23542                etiqueta: owned.clone(),
23543                reason: "r".to_string(),
23544            },
23545        );
23546        // Both call shapes must produce byte-equal
23547        // [`ManifestError::EtiquetaInvalid`] values on the same
23548        // underlying `String`, so a wire-up threading `etiqueta: &String`
23549        // through the same ctor as a peer wire-up threading `nome: &str`
23550        // through it collapses onto one canonical shape.
23551        assert_eq!(
23552            ManifestError::etiqueta_invalid("MyKeyword", "r"),
23553            ManifestError::etiqueta_invalid(&owned, "r"),
23554        );
23555    }
23556}